Определите кодировку и сделайте все в UTF-8


304

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

Конечно, в каналах используется несколько разных кодировок символов, например, UTF-8 и ISO 8859-1.

К сожалению, иногда возникают проблемы с кодировкой текстов. Пример:

  1. «Ss» в «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
«Ss» в «Fußball» в моей базе данных должно выглядеть так: «Ÿ». Нет, это должно выглядеть как Убедитесь, что параметры сортировки и соединения настроены правильно. В противном случае сортировка и поиск будут нарушены для вас.
Рич Брэдшоу

5
Ваша база данных плохо настроена. Если вы хотите хранить контент Unicode, просто настройте его для этого. Поэтому вместо того, чтобы пытаться обойти проблему в вашем PHP-коде, вы должны сначала исправить базу данных.
Дольмен

2
USE: $ from = mb_detect_encoding ($ text); $ текст = mb_convert_encoding ($ текст, 'UTF-8', $ с);
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, не зная, с какой кодировки вводятся недопустимые символы?
Филфрео

4
Предполагается ISO-8859-1, ответ уже говорит об этом. Единственная разница между forceUTF8 () и utf8_encode () заключается в том, что forceUTF8 () распознает символы UTF8 и сохраняет их без изменений.
Себастьян Гриньоли

28
«Вам не нужно знать, какова кодировка ваших строк». - Я очень не согласен. Гадание и попытка могут сработать, но вы всегда рано или поздно столкнетесь с крайними случаями, когда это не так.
deceze

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

74

Сначала вы должны определить, какая кодировка была использована. Когда вы анализируете RSS-каналы (вероятно, через HTTP), вы должны прочитать кодировку из charsetпараметра Content-Typeполя заголовка HTTP . Если его нет, прочитайте кодировку из encodingатрибута инструкции обработки XML . Если этого тоже не хватает, используйте UTF-8, как определено в спецификации .


Редактировать    Вот что я, вероятно, сделал бы:

Я бы использовал cURL для отправки и получения ответа. Это позволяет вам установить определенные поля заголовка и получить заголовок ответа. После получения ответа вы должны проанализировать HTTP-ответ и разделить его на заголовок и тело. Затем заголовок должен содержать Content-Typeполе заголовка, содержащее тип MIME и (надеюсь) charsetпараметр с кодировкой / 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 даются неправильные кодировки.
caw

25
Опять же: это не твоя проблема. Стандарты были установлены, чтобы избежать таких неприятностей. Если другие не следуют за ними, это их проблема, а не ваша.
Гамбо

Хорошо, я думаю, ты наконец убедил меня. :)
caw

Спасибо за код. Но почему бы просто не использовать это? paste.bradleygill.com/index.php?paste_id=9651 Ваш код намного сложнее, что с ним лучше?
Caw

Ну, во-первых, вы делаете два запроса, один для заголовка HTTP и один для данных. Во- вторых, вы ищете какой - либо внешний вид charset=и , encoding=а не только в соответствующих положениях. И в-третьих, вы не проверяете, принята ли заявленная кодировка.
Гамбо

39

Определить кодировку сложно.

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

До тех пор пока вы имеете дело только с западноевропейскими языками, три основных кодировок , чтобы рассмотреть utf-8, iso-8859-1и cp-1252. Поскольку они являются значениями по умолчанию для многих платформ, о них также, скорее всего, сообщают неправильно. Например. если люди используют разные кодировки, они, вероятно, будут откровенны в этом, поскольку в противном случае их программное обеспечение будет очень часто ломаться. Поэтому хорошей стратегией является доверие к поставщику, если только кодировка не указана как одна из этих трех. Вы должны все еще перепроверить , что это действительно действует, используя mb_check_encoding(обратите внимание , что действует не то же самое , как быть - тот же вход может быть справедливо для многих кодировок). Если это один из них, вы можете использоватьmb_detect_encodingразличать их. К счастью, это довольно детерминистично; Вам просто нужно использовать правильную последовательность обнаружения, которая есть UTF-8,ISO-8859-1,WINDOWS-1252.

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


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

7
Я только что видел: mb-detect-encoding () бесполезен. Он поддерживает только UTF-8, UTF-7, ASCII, EUC-JP, SJIS, eucJP-win, SJIS-win, JIS и ISO-2022-JP. Самые важные для меня, ISO-8859-1 и WINDOWS-1252, не поддерживаются. Поэтому я не могу использовать mb-detect-encoding ().
Caw

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

Я думаю, что вы можете различать разные кодировки, когда смотрите на формы, в которых появляются особые поющие: немецкий «ß» встречается в разных формах: иногда «Ÿ», иногда «ƒ» и иногда «ß». Зачем?
Caw

Да, но тогда вам нужно знать содержимое строки, прежде чем сравнивать ее, и это в первую очередь побеждает цель. Немецкий ß выглядит по-разному, потому что он имеет разные значения в разных кодировках. Символы 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: Действительно, именно это рекомендуют и авторы High Performance MySQL.
Аликс Аксель

1
Это не работает правильно: echo (int) isUTF8 ('z'); # 1 echo (int) isUTF8 (NULL); # 1
Юша Алеауб

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

1
mb_check_encoding($string, 'UTF-8')
deceze

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 deteUTF8 ('3٣3'); # 1
Yousha Aleayoub

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 (latin1). Оригинальная исходная кодировка не важна; тот, который вы использовали в ущербном, второе преобразование.

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

Немецкий язык также использует ISO 8859-2 и Windows-1250 (Latin-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
Халил Озгюр,

Учитывая, что спецификация WHATWG HTML определяет Windows 1252 как кодировку по умолчанию, это должно быть довольно безопасно предположить if ($input_is_not_UTF8) $input_is_windows1252 = true;. См. Также: html.spec.whatwg.org/multipage/…
Микко Ранталайнен,

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 для заполнения третьего параметра.
кавила

2

Разработка кодировки символов RSS-каналов кажется сложной . Даже нормальные веб-страницы часто опускают или лгут об их кодировке.

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


Я не хочу считывать кодировку из информации канала. Так что это равно, если информация о подаче неверна. Я хотел бы обнаружить кодировку из текста.
Caw

@ marco92w: Это не ваша проблема, если заявленная кодировка неверна. Стандарты не были установлены для развлечения.
Гамбо

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

@Kevin ORourke: Точно, верно. Это моя проблема. @ Гамбо: Да, это моя проблема. Я хочу зачитать каналы и объединить их. Поэтому я должен исправить неправильные кодировки.
caw

@ marco92w: Но вы не можете исправить кодировку, если не знаете правильную кодировку и текущую кодировку. И это то, что декларация charset/, encodingесли для: опишите кодировку, в которой закодированы данные.
Гамбо

2

Я знаю, что это старый вопрос, но я считаю, что полезный ответ никогда не повредит. У меня были проблемы с моей кодировкой между настольным приложением, SQLite и переменными GET / POST. Некоторые из них будут в UTF-8, другие - в ASCII, и в основном все облажается, когда в игру вступают иностранные символы.

Вот мое решение. Он очищает ваш GET / POST / REQUEST (я пропустил куки, но вы можете добавить их при желании) на каждой странице загрузки перед обработкой. Хорошо работает в шапке. 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){}

Спасибо за ответ, Джокул. Функция mb_convert_encoding () - это то, что у нас уже есть, верно? ;) Так что единственное новое в вашем ответе - это циклы для изменения кодировки во всех переменных.
Caw

2

Я проверял решения для кодирования, так как незапамятных времен , и эта страница, вероятно, завершает годы поиска! Я проверил некоторые из упомянутых вами предложений и вот мои заметки:

Это моя тестовая строка:

это строка с записью, которая мне нужна, чтобы увидеть их, чтобы увидеть их, конвертированные по !! & Это оно!

Я делаю INSERT, чтобы сохранить эту строку в базе данных в поле, которое установлено как utf8_general_ci

Набор символов моей страницы - UTF-8.

Если я сделаю INSERT просто так, в моей базе данных у меня есть персонажи, вероятно, с Марса ...

Поэтому мне нужно конвертировать их в какой-то «вменяемый» UTF-8. Я попыталсяutf8_encode() , но все еще инопланетные персонажи вторгались в мою базу данных ...

Поэтому я попытался использовать функцию, forceUTF8опубликованную под номером 8, но в базе данных сохраненная строка выглядит так:

это «записанная» строка, которую я хочу написать специальным чарам, чтобы увидеть их, преобразовать в фонтан !! & Это оно!

Таким образом, собирая дополнительную информацию на этой странице и объединяя ее с другой информацией на других страницах, я решил свою проблему с помощью этого решения:

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

Теперь в моей базе данных есть строка с правильной кодировкой.

ПРИМЕЧАНИЕ: только примечание, чтобы заботиться, в функции mysql_client_encoding! Вам необходимо подключиться к базе данных, потому что эта функция хочет идентификатор ресурса в качестве параметра.

Но я просто делаю эту перекодировку перед вставкой, так что для меня это не проблема.


1
Почему вы не просто используете UTF-8клиентскую кодировку для MySQL в первую очередь? Не нужно было бы ручное преобразование таким образом
Esailija

2

Все просто: когда вы получаете что-то, что не является UTF-8, вы должны закодировать это в UTF-8.

Итак, когда вы выбираете определенный канал, это ISO 8859-1, проанализируйте его utf8_encode.

Однако, если вы получаете канал UTF-8, вам не нужно ничего делать.


Спасибо! Хорошо, я могу узнать, как кодируется канал, используя mb-detect-encoding (), верно? Но что я могу сделать, если корм ASCII? utf8-encode () ist только для ISO-8859-1 до UTF-8, не так ли?
caw

ASCII является подмножеством ISO-8859-1 И UTF-8, поэтому использование utf8-encode () не должно вносить изменений - ЕСЛИ это на самом деле просто ASCII
Майкл Боргвардт,

Так что я всегда могу использовать utf8_encode, если это не UTF-8? Это было бы действительно легко. Текст, который был ASCII в соответствии с mb-detect-encoding (), содержал «& # 228;». Это персонаж ASCII? Или это HTML?
Caw

Это HTML. На самом деле это закодировано, поэтому, когда вы печатаете его на заданной странице, это показывает, что все в порядке. Если вы хотите, вы можете сначала ut8_encode (), а затем html_entity_decode ().
Себ

1
Символ ß кодируется в UTF-8 с помощью последовательности байтов 0xC39F. Интерпретируемая в Windows-1252, эта последовательность представляет собой два символа Â (0xC3) и Ÿ (0x9F). И если вы снова закодируете эту последовательность байтов с помощью UTF-8, вы получите 0xC383 0xC29F, что представляет собой в Windows-1252. Таким образом, ваша ошибка состоит в том, что вы обрабатываете эти данные в кодировке UTF-8 как нечто с кодировкой, отличной от UTF-8. То, что эта последовательность байтов представлена ​​как символ, который вы видите, является просто вопросом интерпретации. Если вы используете другую кодировку / кодировку, вы, вероятно, увидите другие символы.
Гамбо

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» в качестве второго аргумента? Есть ли в 'auto' больше кодировок? Тогда было бы лучше использовать «авто», не так ли? Если это действительно работает без каких-либо ошибок, тогда я должен только изменить «ASCII» или «ISO-8859-1» на «UTF-8». Как?
Caw

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 дает эти неприятные проблемы с птицами. Я вижу это через день в магазинах oscommerce. Назад и четвертое это может показаться правильным. Но 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

Ÿявляется кракозябры для ß. В вашей базе данных вы можете иметь шестнадцатеричный

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
Есть ли у вас понимание, почему, или как ваши файлы отличались? Какие части не работали для вас? Например: заглавные немецкие символы не конвертировались правильно. Любопытно, что такое "ГБК"?
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.

Приведенный ниже фрагмент извлекает элемент title из веб-страницы. Если вы хотите конвертировать всю страницу, вы можете удалить некоторые строки.

<?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.