php - строки - кодировки




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

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

Я тестировал его, но он не работает. Что с этим не так?


@harpax, который работал для меня. В моем случае это достаточно хорошо:

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

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 работает или нет.

Обновить
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;
    }
}
?>

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


Вам нужно проверить кодировку на входе, поскольку ответы могут быть закодированы с различными кодировками.
Я заставляю все содержимое отправляться в 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.
Или игнорируйте значение, если кодирование невозможно обнаружить или преобразовать.
Вы можете настроить его в соответствии с вашими потребностями.
Просто вызовите его перед использованием переменных.


Ваша кодировка выглядит так, как будто вы закодированы в 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 (latin2).


Интересная вещь о 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)

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


Когда вы пытаетесь обрабатывать несколько языков, таких как японский и корейский, вы можете столкнуться с проблемами. mb_convert_encoding с параметром «auto» не работает. Установка mb_detect_order ('ASCII, UTF-8, JIS, EUC-JP, SJIS, EUC-KR, UHC') не помогает, так как он неправильно обнаружит EUC- *.

Я пришел к выводу, что до тех пор, пока входные строки поступают из HTML, он должен использовать «charset» в метаэлементе.Я использую простой 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);
}

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

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 .


Получите кодировку из заголовков и преобразуйте ее в 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); }

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

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


Самый проголосовавший ответ не работает. Вот моя и надеюсь, что это поможет.

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

Эта версия предназначена для немецкого языка, но вы можете модифицировать $ 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 );
}
}


Это просто: когда вы получаете что-то не UTF8, вы должны ENCODE, что INTO utf8.

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

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


Я нахожу решение здесь 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;


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

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

это «wròng wrìtten» string bùt I nèed to pù 'sòme' special chàrs, чтобы увидеть thèm, convertèd by fùnctìon !! & вот и все!

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

Шифр моей страницы - UTF-8

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

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

это «wrà ² wrìtten» string bùt I nèed to pù 'sà ²me' специальные чары, чтобы увидеть thèm, convertèd by fùnctìon !! & вот и все!

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

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

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

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

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

Надеюсь, это поможет кому-то, как эта страница, помогла мне!

Спасибо всем!

Mauro


Очень хороший способ реализации функции isUTF8 можно найти на php.net :

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

Попробуйте без 'auto'

То есть:

mb_detect_encoding($text)

вместо:

mb_detect_encoding($text, 'auto')

Более подробную информацию можно найти здесь: mb_detect_encoding





character-encoding