Определить, была ли строка дважды закодирована в UTF-8 - PullRequest
7 голосов
/ 17 февраля 2011

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

Некоторые из этих строк будут закодированы вUTF-8 дважды.

Мне нужно надежно определить, является ли данная строка двойной кодировкой, и исправить ее.Я должен сделать это без использования каких-либо внешних библиотек, просто проверяя байты.Обнаружение должно быть максимально быстрым.

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

Обновление:

Оригинальные строки в UTF-8.Вот код AS3, который выполняет вторую кодировку (к сожалению, я не могу контролировать код клиента, поэтому не могу это исправить):

private function toUTF8(s : String) : String {
       var byteArray : ByteArray = new ByteArray();
       byteArray.writeUTFBytes(s);
       byteArray.position = 0;

       var res : String = "";

       while(byteArray.bytesAvailable){
           res += String.fromCharCode(byteArray.readUnsignedByte());
       }

       return res;
}

myString = toUTF8(("" + myString).toLowerCase().substr(0, 64));

Примечание toLowerCase() call.Может быть, это поможет?

Ответы [ 3 ]

6 голосов
/ 17 февраля 2011

В принципе вы не можете, особенно если учесть кошачью фигню.

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

Возьмите не-ASCII символ.UTF-8 кодирует его.Вы получаете несколько байтов, и все эти байты являются действительными символами в CP1251, если только один из них не равен 0x98, единственной дыре в CP1251.

Итак, если вы преобразуете эти байты из CP1251 в UTF-8,результат точно такой же, как если бы вы правильно кодировали UTF-8 строку CP1251, состоящую из этих русских символов.Невозможно определить, является ли результат неправильным двойным кодированием одного символа или правильным одиночным кодированием 2 символов.

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

На практике вы можете догадаться - UTF-8 декодирует его, а затем:

(a) посмотрите начастоты символов, частоты пар символов, количество непечатаемых символов.Это может позволить вам предварительно объявить это чепухой и, следовательно, возможно, дважды закодировать.С достаточным количеством непечатаемых символов это может быть настолько бессмысленным, что вы не сможете реально напечатать его, даже нажимая на клавиатуру, если, возможно, ваша клавиша ALT не застрянет.

(b) попытайтесь выполнить второе декодирование.То есть, начиная с кодовых точек Unicode, которые вы получили, декодируя данные UTF-8, сначала кодируйте их в CP1251 (или что-то еще), а затем декодируйте результат из UTF-8.Если какой-либо шаг завершается неудачно (из-за неверной последовательности байтов), то он определенно не был закодирован дважды, по крайней мере, не использовал CP1251 в качестве ошибочной интерпретации.некоторые байты, которые могут быть UTF-8 или CP1251, и вы не знаете, какие именно.

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

Если в вашей исходной кодировке больше дыр, чем в CP1251, то у вас будет меньше ложных негативов.

Кодировка символов сложная.

4 голосов
/ 20 июня 2013

Вот алгоритм PHP, который работал для меня.

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

if ( mb_detect_encoding( utf8_decode( $value ) ) === 'UTF-8' ) {
    // Double encoded, or bad encoding
    $value = utf8_decode( $value );
}

$value = \ForceUTF8\Encoding::toUTF8( $value );

Библиотека, которую я использую: https://github.com/neitanod/forceutf8/

0 голосов
/ 20 июня 2019
if (mb_detect_encoding(mb_convert_encoding($string,'Windows-1251','UTF-8'),'Windows-1251,UTF-8',true) === 'UTF-8' ){
  $string=mb_convert_encoding($string,'Windows-1251','UTF-8');
};
...