Виявити кодування та зробити все UTF-8


304

Я читаю багато текстів з різних RSS-каналів і вставляю їх у свою базу даних.

Звичайно, існує кілька різних кодувань символів, які використовуються в каналах, наприклад, UTF-8 та ISO 8859-1.

На жаль, іноді виникають проблеми з кодуваннями текстів. Приклад:

  1. "Я" у "Fußball" має виглядати так у моїй базі даних: "Ÿ". Якщо це "Ÿ", воно відображається правильно.

  2. Іноді "ß" у "Fußball" виглядає приблизно так у моїй базі даних: "ß". Тоді воно відображається неправильно, звичайно.

  3. В інших випадках "ß" зберігається як "ß" - тому без будь-яких змін. Тоді воно також відображається неправильно.

Що я можу зробити, щоб уникнути випадків 2 та 3?

Як я можу зробити все те саме кодування, бажано UTF-8? Коли я повинен використовувати utf8_encode(), коли я повинен використовувати utf8_decode()(зрозуміло, що таке ефект, але коли я повинен використовувати функції?) І коли я не повинен нічого робити з введенням?

Як зробити все те саме кодування? Можливо, з функцією mb_detect_encoding()? Чи можу я написати функцію для цього? Тому мої проблеми такі:

  1. Як дізнатися, яке кодування використовує текст?
  2. Як перетворити його в UTF-8 - незалежно від старого кодування?

Була б така функція, як ця робота?

function correct_encoding($text) {
    $current_encoding = mb_detect_encoding($text, 'auto');
    $text = iconv($current_encoding, 'UTF-8', $text);
    return $text;
}

Я перевірив це, але він не працює. Що не так з ним?


36
"" Я "у" Фусболі "має виглядати так у моїй базі даних:" Ÿ "." Ні, це не повинно бути схожим на Я. Переконайтесь, що ви встановлені збір та з'єднання правильно. Інакше сортування та пошук будуть порушені для вас.
Rich Bradshaw

5
Ваша база даних налаштована погано. Якщо ви хочете зберігати вміст Unicode, просто налаштуйте його для цього. Тому замість того, щоб намагатися вирішити проблему у своєму PHP-коді, спочатку слід виправити базу даних.
долмен

2
ВИКОРИСТАННЯ: $ from = mb_detect_encoding ($ текст); $ text = mb_convert_encoding ($ text, 'UTF-8', $ from);
Informate.it

Відповіді:


363

Якщо ви застосуєте utf8_encode()до вже рядку UTF-8, він поверне зібраний вихід UTF-8.

Я зробив функцію, яка вирішує всі ці проблеми. Це називається Encoding::toUTF8().

Вам не потрібно знати, що таке кодування ваших рядків. Це може бути Latin1 ( ISO 8859-1) , Windows-1252 або UTF-8, або рядок може мати їх суміш. Encoding::toUTF8()перетворить все на UTF-8.

Я зробив це через те, що сервіс давав мені канал даних, які всі переплутали, змішуючи UTF-8 та Latin1 в одній і тій же строці.

Використання:

require_once('Encoding.php');
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::toUTF8($utf8_or_latin1_or_mixed_string);

$latin1_string = Encoding::toLatin1($utf8_or_latin1_or_mixed_string);

Завантажити:

https://github.com/neitanod/forceutf8

Я включив ще одну функцію, Encoding::fixUFT8() яка буде виправляти всі рядки UTF-8, які виглядають зграйно.

Використання:

require_once('Encoding.php');
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::fixUTF8($garbled_utf8_string);

Приклади:

echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");

виведе:

Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football

Я перетворив функцію ( forceUTF8) у сімейство статичних функцій класу під назвою Encoding. Нова функція є Encoding::toUTF8().


1
Добре, якщо ви подивитеся на код, fixUTF8 просто викликає forceUTF8 раз і знову, поки рядок не повернеться незмінною. Один виклик fixUTF8 () займає щонайменше вдвічі більше часу виклику до forceUTF8 (), тому він набагато менше виконавця. Я зробив fixUTF8 () просто для того, щоб створити програму командного рядка, яка б виправляла "кодовано-пошкоджені" файли, але в живих умовах рідко потрібна.
Себастьян Гріньолі

3
Як це перетворює символи, що не належать до UTF8, до UTF8, не знаючи, з чого починається кодування недійсних символів?
philfreo

4
Він передбачає ISO-8859-1, відповідь вже говорить про це. Єдина різниця між forceUTF8 () та utf8_encode () полягає в тому, що forceUTF8 () розпізнає символи UTF8 та зберігає їх незмінними.
Себастьян Гріньолі

28
"Вам не потрібно знати, що таке кодування ваших рядків." - Я дуже не згоден. Вгадування та спроби можуть спрацювати, але ви завжди рано чи пізно стикаєтесь із кращими випадками, коли цього не відбувається.
деге

4
Я цілком погоджуюся. Насправді я не хотів заявляти, що як правило, просто поясніть, що цей клас може допомогти вам, якщо це ситуація, в якій ви опинитесь.
Себастьян Гриньолі

74

Спочатку ви повинні виявити, яке кодування було використано. Під час розбору RSS-каналів (можливо, через HTTP), ви повинні прочитати кодування з charsetпараметра Content-Typeполя заголовка HTTP . Якщо його немає, прочитайте кодування з encodingатрибута інструкції з обробки XML . Якщо цього теж немає, використовуйте UTF-8, як визначено в специфікації .


Редагувати    Ось що я, мабуть, зробив би:

Я б використав CURL для надсилання та отримання відповіді. Це дозволяє встановити конкретні поля заголовка та отримати заголовок відповіді. Після отримання відповіді потрібно проаналізувати відповідь HTTP та розділити його на заголовок та тіло. Тоді заголовок повинен міститиContent-Type поле заголовка, яке містить тип MIME і (сподіваємось) charsetпараметр із кодуванням / діаграмою. Якщо ні, то ми проаналізуємо XML PI на наявність encodingатрибута і отримаємо звідти кодування. Якщо цього також немає, специфікації XML визначають використання UTF-8 в якості кодування.

$url = 'http://www.lr-online.de/storage/rss/rss/sport.xml';

$accept = array(
    'type' => array('application/rss+xml', 'application/xml', 'application/rdf+xml', 'text/xml'),
    'charset' => array_diff(mb_list_encodings(), array('pass', 'auto', 'wchar', 'byte2be', 'byte2le', 'byte4be', 'byte4le', 'BASE64', 'UUENCODE', 'HTML-ENTITIES', 'Quoted-Printable', '7bit', '8bit'))
);
$header = array(
    'Accept: '.implode(', ', $accept['type']),
    'Accept-Charset: '.implode(', ', $accept['charset']),
);
$encoding = null;
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
$response = curl_exec($curl);
if (!$response) {
    // error fetching the response
} else {
    $offset = strpos($response, "\r\n\r\n");
    $header = substr($response, 0, $offset);
    if (!$header || !preg_match('/^Content-Type:\s+([^;]+)(?:;\s*charset=(.*))?/im', $header, $match)) {
        // error parsing the response
    } else {
        if (!in_array(strtolower($match[1]), array_map('strtolower', $accept['type']))) {
            // type not accepted
        }
        $encoding = trim($match[2], '"\'');
    }
    if (!$encoding) {
        $body = substr($response, $offset + 4);
        if (preg_match('/^<\?xml\s+version=(?:"[^"]*"|\'[^\']*\')\s+encoding=("[^"]*"|\'[^\']*\')/s', $body, $match)) {
            $encoding = trim($match[1], '"\'');
        }
    }
    if (!$encoding) {
        $encoding = 'utf-8';
    } else {
        if (!in_array($encoding, array_map('strtolower', $accept['charset']))) {
            // encoding not accepted
        }
        if ($encoding != 'utf-8') {
            $body = mb_convert_encoding($body, 'utf-8', $encoding);
        }
    }
    $simpleXML = simplexml_load_string($body, null, LIBXML_NOERROR);
    if (!$simpleXML) {
        // parse error
    } else {
        echo $simpleXML->asXML();
    }
}

Дякую. Це було б просто. Але чи справді це би спрацювало? Часто є неправильні кодування, вказані в заголовках HTTP або в атрибутах XML.
кая

25
Знову: це не ваша проблема. Для уникнення таких неприємностей були встановлені стандарти. Якщо інші не слідують за ними, це їхня проблема, а не ваша.
Gumbo

Гаразд, я думаю, ви зараз остаточно переконали мене. :)
кая

Дякуємо за код. Але чому б просто не використати це? paste.bradleygill.com/index.php?paste_id=9651 Ваш код набагато складніший, що з ним краще?
каре

Ну, по-перше, ви робите два запити, один для заголовка HTTP і один для даних. По- друге, ви шукаєте який - або зовнішній вигляд charset=і , encoding=а не тільки у відповідних положеннях. По-третє, ви не перевіряєте, чи прийняте заявлене кодування.
Gumbo

39

Виявити кодування важко.

mb_detect_encodingпрацює, відгадуючи, виходячи з кількості кандидатів, які ви його передаєте. У деяких кодуваннях певні байтові послідовності є недійсними, тому вони можуть розрізняти різних кандидатів. На жаль, існує багато кодувань, де однакові байти дійсні (але різні). У цих випадках неможливо визначити кодування; Ви можете реалізувати власну логіку, щоб здогадуватися в цих випадках. Наприклад, дані, що надходять з японського сайту, можуть мати більше шансів на японське кодування.

Поки ви маєте справу лише із західноєвропейськими мовами, слід розглянути три основні кодування utf-8, iso-8859-1і cp-1252. Оскільки для багатьох платформ вони є типовими за замовчуванням, про них також найімовірніше повідомляти неправильно. Напр. якщо люди використовують різні кодування, вони, ймовірно, будуть відвертими щодо цього, оскільки в іншому випадку їх програмне забезпечення дуже часто ламається. Тому хорошою стратегією є довіра постачальнику, якщо тільки кодування не повідомляється як одне із цих трьох. Вам слід ще двічі перевірити, чи справді це дійсно, використовуючи mb_check_encoding(зауважте, що дійсність не є такою ж, як їх розрізняти. На щастя, це досить детерміновано; Вам просто потрібно використовувати належну послідовність виявлення, яка є . є - той самий вхід може бути дійсним для багатьох кодувань). Якщо це одне з таких, ви можете використовуватиmb_detect_encodingUTF-8,ISO-8859-1,WINDOWS-1252

Після виявлення кодування вам потрібно перетворити його на внутрішнє представлення ( UTF-8це єдиний розумний вибір). Функція utf8_encodeперетворюється ISO-8859-1на UTF-8, тому її можна використовувати лише для конкретного типу введення. Для інших кодувань використовуйте mb_convert_encoding.


Дуже дякую! Що краще: mb-convert-encoding () або iconv ()? Я не знаю, у чому різниці. Так, мені доведеться лише розбирати західноєвропейські мови, особливо англійську, німецьку та французьку.
каре

7
Я щойно бачив: mb-detect-encoding () istlessless. Він підтримує лише UTF-8, UTF-7, ASCII, EUC-JP, SJIS, eucJP-win, SJIS-win, JIS та ISO-2022-JP. Найважливіші для мене, ISO-8859-1 та WINDOWS-1252, не підтримуються. Тому я не можу використовувати кодування mb-detect ().
кая

1
Мої, ти маєш рацію. Минув час, коли я ним користувався. Тоді вам доведеться написати власний код виявлення або скористатися зовнішньою утилітою. UTF-8 можна досить надійно визначити, оскільки його послідовності втечі досить характерні. wp-1252 та iso-8859-1 можна розрізнити, оскільки wp-1252 може містити байти, незаконні в iso-8859-1. Використовуйте Вікіпедію, щоб отримати детальну інформацію, або подивіться у розділі коментарів php.net під різними функціями, пов’язаними з діаграмою.
troelskn

Я думаю, що ви можете розрізнити різні кодування, коли ви подивитеся на форми, в яких виникають спеціальні співи: Німецька "ß" виникає в різних формах: Іноді "Ÿ", іноді "ß", а іноді "ß". Чому?
каре

Так, але тоді вам потрібно знати вміст рядка, перш ніж порівнювати його, і такий вид перемагає мету в першу чергу. Німецька ß з'являється по-різному, оскільки вона має різні значення в різних кодуваннях. Символи Somce представлені однаково в різних кодуваннях (наприклад, усі символи в діаграмі ascii кодуються однаково в utf-8, iso-8859- * і wp-1252), якщо ви використовуєте просто ті персонажі, всі вони виглядають однаково Ось чому їх іноді називають сумісними ascii.
troelskn

14

Дійсно хороший спосіб для реалізації isUTF8-Функції можна знайти на php.net :

function isUTF8($string) {
    return (utf8_encode(utf8_decode($string)) == $string);
}

16
На жаль, це працює лише тоді, коли рядок складається лише з символів, включених до ISO-8859-1. Але це може спрацювати: @iconv ('utf-8', 'utf-8 // IGNORE', $ str) == $ str
Крістіан Давен

@Christian: Дійсно, це рекомендують і автори MySQL високої продуктивності.
Алікс Аксель

1
Не працює правильно: echo (int) isUTF8 ('z'); # 1 ехо (int) isUTF8 (NULL); # 1
Yousha Aleayoub

1
Хоча це не ідеально, я думаю, що це хороший спосіб здійснити схематичну перевірку UTF-8.
Матенг

1
mb_check_encoding($string, 'UTF-8')
деге

13

Цей шахрайський список містить деякі загальні застереження, пов'язані з обробкою UTF-8 у PHP: http://developer.loftdigital.com/blog/php-utf-8-cheatsheet

Ця функція виявлення багатобайтових символів у рядку також може виявитися корисною ( джерело ):


function detectUTF8($string)
{
    return preg_match('%(?:
        [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
        |\xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
        |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
        |\xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
        |\xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
        |[\xF1-\xF3][\x80-\xBF]{3}         # planes 4-15
        |\xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
        )+%xs', 
    $string);
}


2
Я думаю, що це працює не правильно: echo detectUTF8 ('3٣3'); №1
Юша Алеаюб

10

Трохи голови вгору. Ви сказали, що "Я" має відображатися як "В" у вашій базі даних.

Це, мабуть, тому, що ви використовуєте базу даних з кодуванням символів Latin-1 або, можливо, ваше з'єднання PHP-MySQL встановлено неправильно, це означає, P вважає, що ваш MySQL налаштований на використання UTF-8, тому він надсилає дані як UTF-8 , але ваш MySQL вважає, що PHP надсилає дані, кодовані як ISO 8859-1, тому може знову спробувати кодувати надіслані дані як UTF-8, що спричинить подібні проблеми.

Подивись на mysql_set_charset . Це може вам допомогти.


4

Ваше кодування виглядає так, як ви кодувались в UTF-8 двічі ; тобто від іншого кодування, в UTF-8 і знову в UTF-8. Як би у вас був ISO 8859-1, перетворений з ISO 8859-1 в UTF-8, і новий рядок трактувався як ISO 8859-1 для чергового перетворення в UTF-8.

Ось псевдокод того, що ви зробили:

$inputstring = getFromUser();
$utf8string = iconv($current_encoding, 'utf-8', $inputstring);
$flawedstring = iconv($current_encoding, 'utf-8', $utf8string);

Спробуйте:

  1. виявити кодування за допомогою mb_detect_encoding() або будь-чого, що ви хочете використовувати
  2. якщо це UTF-8, перетворіть його на ISO 8859-1 та повторіть крок 1
  3. нарешті, перетворіть назад в UTF-8

Це припущення, що в "середній" конверсії ви використовували ISO 8859-1. Якщо ви використовували Windows-1252, то конвертуйте в Windows-1252 (латинська1). Початкове кодування джерела не важливо; той, який ви використовували в недоліках, другий - це перетворення.

Це моя здогадка про те, що сталося; ще дуже мало ви могли зробити, щоб отримати чотири байти замість одного розширеного байта ASCII.

У німецькій мові також використовуються ISO 8859-2 та Windows-1250 (латинська-2).


3

Цікаво те , mb_detect_encodingі в mb_convert_encodingтому , що порядок кодувань ти пропонуєш дійсно має значення:

// $input is actually UTF-8

mb_detect_encoding($input, "UTF-8", "ISO-8859-9, UTF-8");
// ISO-8859-9 (WRONG!)

mb_detect_encoding($input, "UTF-8", "UTF-8, ISO-8859-9");
// UTF-8 (OK)

Таким чином, можливо, ви хочете використовувати конкретний порядок, коли вказуєте очікувані кодування. Все ж майте на увазі, що це не є дурним.


2
Це відбувається тому, що ISO-8859-9 на практиці прийме будь-який бінарний ввід. Те саме стосується Windows-1252 та друзів. Потрібно спершу протестувати кодування, які не можуть прийняти дані.
Мікко Ранталайнен

@MikkoRantalainen, так, я думаю, ця частина документів говорить щось подібне: php.net/manual/en/function.mb-detect-order.php#example-2985
Halil Özgür

Враховуючи те, що специфікація HTML WHATWG визначає Windows 1252 як кодування за замовчуванням, припустити це слід досить безпечно if ($input_is_not_UTF8) $input_is_windows1252 = true;. Дивіться також: html.spec.whatwg.org/multipage/…
Mikko Rantalainen

3

Вам потрібно перевірити набір символів на вводі, оскільки відповіді можуть бути закодовані різними кодуванням.

Я змушую весь вміст відправляти в UTF-8, роблячи виявлення та переклад за допомогою наступної функції:

function fixRequestCharset()
{
  $ref = array(&$_GET, &$_POST, &$_REQUEST);
  foreach ($ref as &$var)
  {
    foreach ($var as $key => $val)
    {
      $encoding = mb_detect_encoding($var[$key], mb_detect_order(), true);
      if (!$encoding)
        continue;
      if (strcasecmp($encoding, 'UTF-8') != 0)
      {
        $encoding = iconv($encoding, 'UTF-8', $var[$key]);
        if ($encoding === false)
          continue;
        $var[$key] = $encoding;
      }
    }
  }
}

Ця програма перетворить всі змінні PHP, які надходять від віддаленого хоста, в UTF-8.

Або ігноруйте значення, якщо кодування не вдалося виявити або перетворити.

Ви можете налаштувати його під свої потреби.

Просто зверніться до нього, перш ніж використовувати змінні.


яка мета використання mb_detect_order () без передачі у списку кодування?
giorgio79

Метою є повернення налаштованої системою упорядкованого масиву кодувань, визначених у використаному php.ini. Це потрібно для mb_detect_encoding для заповнення третього параметра.
cavila

2

Опрацювання кодування символів RSS-каналів здається складним . Навіть звичайні веб-сторінки часто пропускають або брешуть про їх кодування.

Тож ви можете спробувати скористатися правильним способом виявлення кодування, а потім повернутися до якоїсь форми автоматичного виявлення (здогадки).


Я не хочу читати кодування з інформації каналу. Тож це рівно, якщо інформація про канали помилкова. Я хотів би виявити кодування з тексту.
кавка

@ marco92w: Це не ваша проблема, якщо заявлене кодування неправильне. Стандарти для розваги не встановлені.
Gumbo

1
@Gumbo: але якщо ти працюєш у реальному світі, ти повинен мати можливість мати справу з такими речами, як неправильно оголошені кодування. Проблема полягає в тому, що дуже важко здогадатися (правильно) кодування лише з якогось тексту. Стандарти чудові, але багато (більшість?) Сторінок / каналів там не відповідають їм.
Кевін ОРурк

@Kevin ORourke: Точно так. Це моя проблема. @Gumbo: Так, це моя проблема. Я хочу прочитати канали та узагальнити їх. Тому я повинен виправити неправильні кодування.
кау

@ marco92w: Але ви не можете виправити кодування, якщо ви не знаєте правильного та поточного кодування. І ось що charset/ encodingдекларація, якщо для: опишіть кодування даних, що кодуються.
Gumbo,

2

Я знаю, що це питання старіше, але я вважаю, що корисна відповідь ніколи не зашкодить. У мене виникли проблеми з кодуванням між настільним додатком, змінними SQLite та GET / POST. Деякі були б в UTF-8, інші - в ASCII, і в основному все б накрутилося, коли іноземні персонажі долучилися.

Ось моє рішення. Він очищає ваш GET / POST / REQUEST (я пропустив файли cookie, але ви можете додати їх за бажанням) на кожній завантаженій сторінці перед обробкою. Він добре працює в заголовку. PHP викине попередження, якщо не зможе автоматично визначити кодування джерела, тому ці попередження будуть придушені символами @.

//Convert everything in our vars to UTF-8 for playing nice with the database...
//Use some auto detection here to help us not double-encode...
//Suppress possible warnings with @'s for when encoding cannot be detected
try
{
    $process = array(&$_GET, &$_POST, &$_REQUEST);
    while (list($key, $val) = each($process)) {
        foreach ($val as $k => $v) {
            unset($process[$key][$k]);
            if (is_array($v)) {
                $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = $v;
                $process[] = &$process[$key][@mb_convert_encoding($k,'UTF-8','auto')];
            } else {
                $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = @mb_convert_encoding($v,'UTF-8','auto');
            }
        }
    }
    unset($process);
}
catch(Exception $ex){}

Дякую за відповідь, jocull. Функція mb_convert_encoding () - це те, що ми вже мали тут, правда? ;) Отже, єдине нове у вашій відповіді - це петлі для зміни кодування у всіх змінних.
каре

2

Я перевіряв рішення кодування з віків , і ця сторінка, мабуть, є висновком років пошуку! Я перевірив деякі згаданих вами пропозицій, і ось мої примітки:

Це мій тестовий рядок:

це рядок "wròng wrìtten", але я потребував спеціальних функцій, щоб побачити те, перетворене fùnctìon !! & Це воно!

Я роблю INSERT, щоб зберегти цю рядок у базі даних у полі, яке встановлено як utf8_general_ci

Набір символів моєї сторінки - UTF-8.

Якщо я роблю INSERT саме так, у своїй базі даних у мене є деякі персонажі, ймовірно, з Марса ...

Тому мені потрібно перетворити їх у якийсь "здоровий" UTF-8. Я спробував utf8_encode(), але все-таки прибульці вторглися в мою базу даних ...

Тому я спробував використовувати функцію, forceUTF8розміщену на номері 8, але в базі даних збережена рядок виглядає приблизно так:

це рядок "wròng wrìtten", який я не мав, щоб побачити "sòme" спеціальні члени, щоб побачити thm, перетворити fd¹ntctìon !! & Це воно!

Тому зібравши ще трохи інформації на цій сторінці та об’єднавши їх з іншою інформацією на інших сторінках, я вирішив свою проблему з цим рішенням:

$finallyIDidIt = mb_convert_encoding(
  $string,
  mysql_client_encoding($resourceID),
  mb_detect_encoding($string)
);

Тепер у моїй базі даних є мій рядок із правильним кодуванням.

ПРИМІТКА. У функціонуванні є лише примітка mysql_client_encoding! Вам потрібно підключитися до бази даних, оскільки ця функція хоче ідентифікатор ресурсу як параметр.

Але добре, я просто роблю це повторне кодування перед моїм INSERT, тому для мене це не є проблемою.


1
Чому ви не просто UTF-8в першу чергу використовуєте кодування клієнта для mysql? Не знадобиться ручне перетворення таким чином
Есаїлія

2

Це просто: коли ви отримуєте щось, що не є UTF-8, ви повинні кодувати це в UTF-8.

Отже, коли ви отримуєте певний канал, який відповідає ISO 8859-1, проаналізуйте його utf8_encode.

Однак якщо ви отримуєте канал UTF-8, вам нічого не потрібно робити.


Дякую! Гаразд, я можу дізнатися, як канал кодується, використовуючи mb-detect-encoding (), правда? Але що я можу зробити, якщо канал - ASCII? utf8-encode () є лише для ISO-8859-1 до UTF-8, чи не так?
кая

ASCII - це підмножина ISO-8859-1 AND UTF-8, тому використання utf8-encode () не повинно вносити змін - ЯКЩО це насправді лише ASCII
Майкл Боргвардт,

Тож я завжди можу використовувати utf8_encode, якщо це не UTF-8? Це було б справді просто. Текст, який був ASCII відповідно до mb-detect-encoding (), містив "& # 228;". Це символ ASCII? Або це HTML?
кау

Це HTML. Насправді це закодовано, тому коли ви друкуєте його на певній сторінці, це показує нормально. Якщо ви хочете, ви можете спочатку ut8_encode (), а потім html_entity_decode ().
Себ

1
Символ ß кодується в UTF-8 із послідовністю байтів 0xC39F. Інтерпретована Windows-1252, ця послідовність представляє два символи  (0xC3) та Ÿ (0x9F). І якщо ви знову кодуєте цю послідовність байтів за допомогою UTF-8, ви отримаєте 0xC383 0xC29F, що являє собою ß в Windows-1252. Отже, ваша помилка полягає в тому, щоб обробляти ці кодовані дані UTF-8 як щось із кодуванням, відмінним від UTF-8. Те, що ця послідовність байтів представлена ​​як персонаж, якого ви бачите, - лише питання інтерпретації. Якщо ви використовуєте інше кодування / діаграму, ви, ймовірно, побачите інші символи.
Gumbo

1

php.net/mb_detect_encoding

echo mb_detect_encoding($str, "auto");

або

echo mb_detect_encoding($str, "UTF-8, ASCII, ISO-8859-1");

Я дійсно не знаю, які результати, але я б запропонував вам просто взяти деякі свої канали з різними кодуваннями і спробувати, mb_detect_encodingпрацює чи ні.


Автоматичне оновлення коротке для "ASCII, JIS, UTF-8, EUC-JP, SJIS". він повертає виявлену діаграму, яку ви можете використовувати для перетворення рядка в utf-8 за допомогою iconv .

<?php
function convertToUTF8($str) {
    $enc = mb_detect_encoding($str);

    if ($enc && $enc != 'UTF-8') {
        return iconv($enc, 'UTF-8', $str);
    } else {
        return $str;
    }
}
?>

я не перевіряв його, тому жодної гарантії. і, можливо, є простіший спосіб.


Дякую. Яка різниця між "auto" та "UTF-8, ASCII, ISO-8859-1" як другий аргумент? Чи має "авто" більше кодування? Тоді було б краще використовувати "авто", чи не так? Якщо це дійсно працює без помилок, я повинен змінити лише "ASCII" або "ISO-8859-1" на "UTF-8". Як?
каре

2
Ваша функція працює не в усіх випадках. Іноді я отримую помилку: Примітка: iconv (): Виявлено незаконний символ у вхідному рядку в ...
caw

1

@harpax, який працював на мене. У моєму випадку це досить добре:

if (isUTF8($str)) { 
    echo $str; 
}
else
{
    echo iconv("ISO-8859-1", "UTF-8//TRANSLIT", $str);
}

0

Після сортування ваших скриптів для php, не забудьте сказати mysql, яку діаграму ви передаєте та хотіли б отримати.

Приклад: встановити набір символів utf8

Передача даних utf8 в таблицю latin1 під час сеансу введення-виводу Latin1 дає цим неприємним пташиним ногам. Я це бачу через день у магазинах, що займаються комерцією. Назад і по-четверте, це може здатися правильним. Але phpmyadmin покаже правду. Повідомляючи mysql, яку шаблону ви передаєте, він буде обробляти перетворення даних mysql.

Як відновити існуючі скремтовані дані mysql - це ще одна тема для обговорення. :)


0

Ця версія призначена для німецької мови, але ви можете змінити $ CHARSETS і $ TESTCHARS

class CharsetDetector
{
private static $CHARSETS = array(
"ISO_8859-1",
"ISO_8859-15",
"CP850"
);
private static $TESTCHARS = array(
"€",
"ä",
"Ä",
"ö",
"Ö",
"ü",
"Ü",
"ß"
);
public static function convert($string)
{
    return self::__iconv($string, self::getCharset($string));
}
public static function getCharset($string)
{
    $normalized = self::__normalize($string);
    if(!strlen($normalized))return "UTF-8";
    $best = "UTF-8";
    $charcountbest = 0;
    foreach (self::$CHARSETS as $charset) {
        $str = self::__iconv($normalized, $charset);
        $charcount = 0;
        $stop   = mb_strlen( $str, "UTF-8");

        for( $idx = 0; $idx < $stop; $idx++)
        {
            $char = mb_substr( $str, $idx, 1, "UTF-8");
            foreach (self::$TESTCHARS as $testchar) {

                if($char == $testchar)
                {

                    $charcount++;
                    break;
                }
            }
        }
        if($charcount>$charcountbest)
        {
            $charcountbest=$charcount;
            $best=$charset;
        }
        //echo $text."<br />";
    }
    return $best;
}
private static function __normalize($str)
{

$len = strlen($str);
$ret = "";
for($i = 0; $i < $len; $i++){
    $c = ord($str[$i]);
    if ($c > 128) {
        if (($c > 247)) $ret .=$str[$i];
        elseif ($c > 239) $bytes = 4;
        elseif ($c > 223) $bytes = 3;
        elseif ($c > 191) $bytes = 2;
        else $ret .=$str[$i];
        if (($i + $bytes) > $len) $ret .=$str[$i];
        $ret2=$str[$i];
        while ($bytes > 1) {
            $i++;
            $b = ord($str[$i]);
            if ($b < 128 || $b > 191) {$ret .=$ret2; $ret2=""; $i+=$bytes-1;$bytes=1; break;}
            else $ret2.=$str[$i];
            $bytes--;
        }
    }
}
return $ret; 
}
private static function __iconv($string, $charset)
{
    return iconv ( $charset, "UTF-8" , $string );
}
}


0

Отримайте кодування із заголовків та перетворіть його у utf-8.

$post_url='http://website.domain';

/// Get headers ////////////////////////////////////////////////////////////
function get_headers_curl($url) 
{ 
    $ch = curl_init(); 

    curl_setopt($ch, CURLOPT_URL,            $url); 
    curl_setopt($ch, CURLOPT_HEADER,         true); 
    curl_setopt($ch, CURLOPT_NOBODY,         true); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($ch, CURLOPT_TIMEOUT,        15); 

    $r = curl_exec($ch); 
    return $r; 
}
$the_header = get_headers_curl($post_url);
/// check for redirect /////////////////////////////////////////////////
if (preg_match("/Location:/i", $the_header)) {
    $arr = explode('Location:', $the_header);
    $location = $arr[1];

    $location=explode(chr(10), $location);
    $location = $location[0];

$the_header = get_headers_curl(trim($location));
}
/// Get charset /////////////////////////////////////////////////////////////////////
if (preg_match("/charset=/i", $the_header)) {
    $arr = explode('charset=', $the_header);
    $charset = $arr[1];

    $charset=explode(chr(10), $charset);
    $charset = $charset[0];
    }
///////////////////////////////////////////////////////////////////////////////
// echo $charset;

if($charset && $charset!='UTF-8') { $html = iconv($charset, "UTF-8", $html); }

0

Ÿє Mojibake для ß. У вашій базі даних може бути шістнадцятковий

DF if the column is "latin1",
C39F if the column is utf8 -- OR -- it is latin1, but "double-encoded"
C383C5B8 if double-encoded into a utf8 column

Ви не повинні використовувати жодних функцій кодування / декодування в PHP; натомість слід правильно налаштувати базу даних та підключення до неї.

Якщо задіяний MySQL, див.: Проблема з символами utf8; те, що я бачу, - це не те, що я зберігав


0

Тут я знаходжу рішення http://deer.org.ua/2009/10/06/1/

class Encoding
{
    /**
     * http://deer.org.ua/2009/10/06/1/
     * @param $string
     * @return null
     */
    public static function detect_encoding($string)
    {
        static $list = ['utf-8', 'windows-1251'];

        foreach ($list as $item) {
            try {
                $sample = iconv($item, $item, $string);
            } catch (\Exception $e) {
                continue;
            }
            if (md5($sample) == md5($string)) {
                return $item;
            }
        }
        return null;
    }
}

$content = file_get_contents($file['tmp_name']);
$encoding = Encoding::detect_encoding($content);
if ($encoding != 'utf-8') {
    $result = iconv($encoding, 'utf-8', $content);
} else {
    $result = $content;
}

Я думаю, що @ - це неправильне рішення, і внести деякі зміни в рішення з deer.org.ua;


0

Відповідь, яка найбільше голосує, не працює. Ось моя і сподіваюся, що це допомагає.

function toUTF8($raw) {
    try{
        return mb_convert_encoding($raw, "UTF-8", "auto"); 
    }catch(\Exception $e){
        return mb_convert_encoding($raw, "UTF-8", "GBK"); 
    }
}

1
Чи маєте ви зрозуміти, чому або як ваші файли відрізнялися? Які частини не працювали для вас? Наприклад: Великі символи німецької мови не були конвертовані правильно. Цікаво, що таке "GBK"?
SherylHohman

-1

Якщо ви намагаєтеся працювати з різними мовами, такими як японська та корейська, ви можете зіткнутися з проблемою. mb_convert_encoding з параметром 'auto' не працює добре. Встановлення mb_detect_order ("ASCII, UTF-8, JIS, EUC-JP, SJIS, EUC-KR, UHC") не допомагає, оскільки воно виявить EUC- * неправильно.

Я зробив висновок, що доки вхідні рядки надходять з HTML, він повинен використовувати "charset" у метаелементі. Я використовую Simple HTML DOM Parser, оскільки він підтримує недійсний HTML.

Нижче фрагмент витягує заголовковий елемент із веб-сторінки. Якщо ви хочете перетворити всю сторінку, ви можете видалити деякі рядки.

<?php
require_once 'simple_html_dom.php';

echo convert_title_to_utf8(file_get_contents($argv[1])), PHP_EOL;

function convert_title_to_utf8($contents)
{
    $dom = str_get_html($contents);
    $title = $dom->find('title', 0);
    if (empty($title)) {
        return null;
    }
    $title = $title->plaintext;
    $metas = $dom->find('meta');
    $charset = 'auto';
    foreach ($metas as $meta) {
        if (!empty($meta->charset)) { // html5
            $charset = $meta->charset;
        } else if (preg_match('@charset=(.+)@', $meta->content, $match)) {
            $charset = $match[1];
        }
    }
    if (!in_array(strtolower($charset), array_map('strtolower', mb_list_encodings()))) {
        $charset = 'auto';
    }
    return mb_convert_encoding($title, 'UTF-8', $charset);
}

-1

У мене була проблема з phpQuery ( ISO-8859-1 замість UTF-8 ), і цей злом допоміг мені:

$html = '<?xml version="1.0" encoding="UTF-8" ?>' . $html;

mb_internal_encoding('UTF-8'), phpQuery::newDocumentHTML($html, 'utf-8'), mbstring.internal_encodingІ інші маніпуляції не приймали ніякого ефекту.


-1

Спробуйте без "авто"

Це є:

mb_detect_encoding($text)

замість:

mb_detect_encoding($text, 'auto')

Більше інформації можна знайти тут: mb_detect_encoding

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.