Обнаружьте кодирование и сделайте все UTF-8

Я считываю много текстов от различных каналов 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;
}

Я протестировал его, но это не работает. Что случилось с ним?

293
задан Peter Mortensen 8 July 2019 в 06:28
поделиться

7 ответов

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 или нет.

update
auto - это сокращение от "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;
    }
}
?>

Я не тестировал ее, поэтому никаких гарантий. и, может быть, есть способ попроще.

1
ответ дан 23 November 2019 в 01:38
поделиться

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

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

2
ответ дан 23 November 2019 в 01:38
поделиться

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

Итак, когда вы загружаете определенный канал, который ISO 8859-1 анализирует с помощью utf8_encode .

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

]
2
ответ дан 23 November 2019 в 01:38
поделиться

Обнаружить кодировку сложно.

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 .

36
ответ дан 23 November 2019 в 01:38
поделиться

Сначала вы должны определить, какая кодировка была использована. При синтаксическом анализе RSS-каналов (возможно, через HTTP) вам следует прочитать кодировку из параметра charset в поле HTTP-заголовка Content-Type . Если его нет, считайте кодировку из атрибута 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();
    }
}
72
ответ дан 23 November 2019 в 01:38
поделиться

Ваша кодировка выглядит так, как если бы вы дважды кодировали 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
ответ дан 23 November 2019 в 01:38
поделиться

В этой памятке перечислены некоторые общие предостережения, связанные с обработкой 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);
}
12
ответ дан 23 November 2019 в 01:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: