PHP: преобразуйте любую строку в UTF-8, не зная исходного набора символов, или хотя бы попробуйте



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



основная проблема для меня заключается в том, что я не знаю, какая кодировка источника любой строки будет - это может быть из текстового поля (используя <form accept-charset="utf-8"> полезно только в том случае, если пользователь действительно отправил форму), или это может быть из загруженного текстового файла, поэтому у меня действительно нет контроля над вводом.



Что Я need-это функция или класс, который гарантирует, что материал, поступающий в мою базу данных, насколько это возможно, кодируется UTF-8. Я пробовал iconv(mb_detect_encoding($text), "UTF-8", $text);
но у этого есть проблемы (если вход "невеста", он возвращает "жених"). Я много чего перепробовал=/



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



Я читал другие вопросы SO по этому вопросу, но все они, похоже, имеют тонкие различия, такие как "мне нужно анализировать RSS-каналы" или "я очищаю данные с веб-сайтов" (или, действительно, "Вы не можете").



но должно же быть что-то, что хотя бы имеет хороший попробовать!

1701   10  

10 ответов:

то, что вы просите, чрезвычайно трудно. Если возможно, лучше всего заставить пользователя указать кодировку. Предотвращение нападения не должно быть намного проще или сложнее таким образом.

однако, вы можете попробовать сделать это:

iconv(mb_detect_encoding($text, mb_detect_order(), true), "UTF-8", $text);

установка его в строгий может помочь вам получить лучший результат.

в Родине России у нас есть 4 популярных кодировки, поэтому ваш вопрос пользуется большим спросом здесь.

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

единственный способ работы с неизвестными кодировками-это работа с вероятностями. Поэтому мы не хотим ответить на вопрос "Что такое кодировка текста?"мы пытаясь понять"какова наиболее вероятная кодировка этого текста?".

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

построить диапазон вероятностей кодов символов в каждой кодировке, которую вы хотите поддерживать. Вы можете построить его, используя некоторые большие тексты на вашем языке (например, некоторые художественные произведения, использовать Шекспира для английского и Толстого для русского, lol ). Вы получите что-то вроде этого:

    encoding_1:
    190 => 0.095249209893009,
    222 => 0.095249209893009,
    ...
    encoding_2:
    239 => 0.095249209893009,
    207 => 0.095249209893009,
    ...
    encoding_N:
    charcode => probabilty

далее. Вы берете текст в неизвестной кодировке и для каждой кодировки в вашем "вероятностном словаре" вы ищете частоту каждого символа в неизвестном закодированном тексте. Сумма вероятностей символов. Кодирование с большим рейтингом, скорее всего, победитель. Лучшие результаты для больших текстов.

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

кстати. mb_detect_encoding certanly не работает. Да, вообще. Пожалуйста, возьмите посмотрите исходный код mb_detect_encoding в "ext/mbstring/libmbfl/mbfl / mbfl_ident.с."

Вы, наверное, пробовали это, но почему бы просто не использовать функцию mb_convert_encoding? Он попытается автоматически определить набор символов предоставленного текста или вы можете передать ему список.

кроме того, я попытался запустить:

$text = "fiancée";
echo mb_convert_encoding($text, "UTF-8");
echo "<br/><br/>";
echo iconv(mb_detect_encoding($text), "UTF-8", $text);

и результаты одинаковы для обоих. Как вы видите, что ваш текст усечен до "жениха"? это в БД или в браузере?

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

возьмите кодировку ISO-8859-1 против ISO-8859-15 ( http://en.wikipedia.org/wiki/ISO/IEC_8859-15#Changes_from_ISO-8859-1)

есть только несколько разных символов, и что еще хуже, они представлены одними и теми же байтами. Нет никакого способа обнаружить, будучи заданной строкой, не зная, что это кодировка, должен ли байт 0xA4 обозначать ¤ или € в вашей строке, поэтому нет никакого способа узнать, что это точная кодировка.

(Примечание: Вы можете добавить человеческий фактор или еще более расширенное сканирование техника (например, что предлагает Oroboros102), чтобы попытаться выяснить, основываясь на окружающем контексте, если персонаж должен быть ¤ или€, хотя это кажется слишком далеким мостом)

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

интересно читать: http://kore-nordmann.de/blog/php_charset_encoding_FAQ.html#how-do-i-determine-the-charset-encoding-of-a-string

есть и другие способы обеспечения правильной кодировки, хотя. Что касается форм, попробуйте применить UTF-8 как можно больше (проверьте snowman, чтобы убедиться, что вы отправите UTF-8 в каждом браузере:http://intertwingly.net/blog/2010/07/29/Rails-and-Snowmen ) Это делается, по крайней мере, вы можете быть уверены, что каждый текст, представленный через свои формы-это utf_8. Что касается загруженных файлов, попробуйте запустить команду unix "file-i" на нем, например, через exec() (если это возможно на вашем сервере), чтобы помочь обнаружению (используя спецификацию документа.) Что касается очистки данных, вы можете прочитать заголовки HTTP, которые обычно указывают кодировку. При анализе XML-файлов проверьте, содержат ли XML-метаданные определение кодировки.

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

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

Я не думаю, что это проблема. Приложение знает источник входных данных. Если это из формы, используйте кодировку UTF-8 в вашем случае. Эта работа. Просто проверьте данные при условии правильно закодирован (проверка). Имейте в виду, что не все базы данных поддерживают UTF-8 в полном диапазоне.

Если это файл, вы не сохраните его в кодировке UTF-8 в базе данных, но в двоичном виде. Когда вы снова выводите файл, также используйте двоичный вывод, тогда это полностью прозрачно.

ваша идея хороша тем, что пользователь может сказать кодировку, будь он/она может сказать в любом случае после загрузки файла, так как он двоичный.

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

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

если вы готовы "взять это на консоль", я бы рекомендовал enca. В отличие от упрощенных mb_detect_encoding, он использует " смесь разбора, статистического анализа, угадывания и черной магии для определения их кодировок "(lol - см. на странице). Однако обычно необходимо передать язык входного файла, если вы хотите обнаружить такие кодировки для конкретной страны. (Однако, mb_detect_encoding по существу имеет то же требование, что и кодировка, которая должна появиться " справа поместите" в список передаваемых кодировок, чтобы он был обнаружен вообще.)

enca и пришел сюда: как найти кодировку файла в Unix через скрипт(ы)

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

моя ситуация сводилась к "я просто хочу, чтобы мои дезинфицирующие средства, валидаторы, бизнес-логика и подготовленные заявления имели дело с UTF-8, когда данные поступают из HTML-форм, или ссылки для регистрации по электронной почте."Итак, в моем простом способе, я начал с этой идеи:

  1. попытка определить кодировку:$encodings = ['UTF-8', 'ISO-8859-1', 'ASCII'];
  2. если кодировка не может быть обнаружена,throw new RuntimeException
  3. если вход UTF-8, продолжай.
  4. иначе, если это ISO-8859-1 или ASCII

    A. попытка преобразования в UTF-8 (подождите, не закончено)

    b. определите кодировку преобразованного значения

    c. если сообщенный кодирование и преобразованное значение как UTF-8, продолжай.

    d. Else,throw new RuntimeException

из моего абстрактного класса Sanitizer

Sanitizer

    private function isUTF8($encoding, $value)
    {
        return (($encoding === 'UTF-8') && (utf8_encode(utf8_decode($value)) === $value));
    }

    private function utf8tify(&$value)
    {
        $encodings = ['UTF-8', 'ISO-8859-1', 'ASCII'];

        mb_internal_encoding('UTF-8');
        mb_substitute_character(0xfffd); //REPLACEMENT CHARACTER
        mb_detect_order($encodings);

        $stringEncoding = mb_detect_encoding($value, $encodings, true);

        if (!$stringEncoding) {
            $value = null;
            throw new \RuntimeException("Unable to identify character encoding in sanitizer.");
        }

        if ($this->isUTF8($stringEncoding, $value)) {
            return;
        } else {
            $value = mb_convert_encoding($value, 'UTF-8', $stringEncoding);
            $stringEncoding = mb_detect_encoding($value, $encodings, true);

            if ($this->isUTF8($stringEncoding, $value)) {
                return;
            } else {
                $value = null;
                throw new \RuntimeException("Unable to convert character encoding from ISO-8859-1, or ASCII, to UTF-8 in Sanitizer.");
            }
        }

        return;
    }

можно было бы сделать аргумент, что я должен отдельная кодировка касается из моего аннотация Sanitizer класс и просто залить Encoder объект в конкретный дочерний экземпляр Sanitizer. Однако, главная проблема с моим подходом заключается в том, что, без дополнительных знаний я просто отвергаю типы кодирования, которые мне не нужны (и я полагаюсь на функции PHP mb_*). Без дальнейшего изучения я не могу знать, вредит ли это некоторым группам населения или нет (или, если я теряю важную информацию). Так что мне нужно узнать больше. Я нашел эту статью.

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

более того, что происходит когда зашифрованные данные добавляются в мои ссылки регистрации электронной почты (используя OpenSSL или mcrypt)? Может ли это помешать декодированию? А как насчет Windows-1252? А что насчет безопасности? Использование utf8_decode() и utf8_encode() на Sanitizer::isUTF8 сомнительный.

люди указали на недостатки в функциях PHP mb_*. Я никогда не тратил время на расследование iconv, но если он работает лучше, чем функции mb_*, дайте мне знать.

public function convertToUtf8($text) {
    if(!$this->html)
        $this->html = cURL('http://'.$this->url, array('timeout' => 15));

    $html = $this->html;
    preg_match('/<meta.*?charset=(|\")(.*?)("|\")/i', $html, $matches);

    $charset = $matches[2];

    if($charset)
        return mb_convert_encoding($text, 'UTF-8', $charset);
    else
        return $text;
}

cURL параметры по умолчанию:

curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);

Я пробовал что-то вроде этого. Это помогло мне. Если найдено на meta charset info, я конвертирую, иначе ничего не делаю.

Кажется, что ваш вопрос вполне ответили, но у меня есть подход, который может упростить вам случай:

У меня была аналогичная проблема, пытаясь вернуть строковые данные из mysql, даже настроив как базу данных, так и php для возврата строк, отформатированных в utf-8. Единственный способ, которым я получил ошибку, на самом деле возвращал их из базы данных.

наконец, проплывая через интернет, я нашел очень простой способ справиться с этим:

давая, что вы можете сохранить все эти типы строковые данные в вашем mysql в разных форматах и параметрах сортировки, что вам нужно только сделать, прямо в вашем файле подключения php, установите параметры сортировки в utf-8, например:

$connection = new mysqli($server, $user, $pass, $db);
$connection->set_charset("utf8");

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

надеюсь, что это было полезно!

Comments

    Ничего не найдено.