Как я преобразовываю хранившие misencoded данные?

Просто поместите этот код в цикл for следующим образом:

for(int i=0;i<10;i++){
  int random = (int)(Math.random()* (50 + 1));
  System.out.println (random);
}
5
задан ssd 10 May 2009 в 00:36
поделиться

1 ответ

В примере с четырьмя баллами это почти наверняка данные с двойным кодированием. Это выглядит так:

  1. данные cp1252, которые дважды проходили через процесс cp1252 - utf8, либо
  2. данные utf8, которые проходили через процесс cp1252 - utf8

(естественно, оба случая выглядят одинаково)

Итак, это то, чего вы ожидали, так почему же ваш код не работал?

Во-первых, я хотел бы отослать вас к этой таблице , в которой показано преобразование cp1252 в Unicode. Я хочу, чтобы вы обратили внимание на то, что некоторые байты (например, 0x9D) недопустимы в cp1252.

Когда я представляю, как написать преобразователь cp1252 в utf8, мне нужно что-то сделать с этими байтами, которые нет в cp1252. Единственное, что я могу придумать, - это преобразовать неизвестные байты в символы Юникода с тем же значением. На самом деле, похоже, именно это и произошло. Давайте вернемся к вашему примеру с «четырьмя оценками» на один шаг назад.

Во-первых, поскольку это действительный код utf-8, давайте декодируем его с помощью:

$ perl -CO -MEncode -e '$a=decode("utf-8", 
  "\xC3\xA2\xE2\x82\xAC\xC5\x93" .
  "four score" .
  "\xC3\xA2\xE2\x82\xAC\xC2\x9D");
  for $c (split(//,$a)) {printf "%x ",ord($c);}' | fmt

Это дает следующую последовательность кодовых точек Unicode:

e2 20ac 153 66 6f 75 72 20 73 63 6f 72 65 e2 20ac 9d

("fmt "- это команда unix, которая просто переформатирует текст, чтобы у нас были хорошие разрывы строк с длинными данными)

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

$ perl -CO -MEncode -e '$a=decode("utf-8",
  "\xC3\xA2\xE2\x82\xAC\xC5\x93" .
  "four score" .
  "\xC3\xA2\xE2\x82\xAC\xC2\x9D");
  $a=encode("cp-1252", $a, sub { chr($_[0]) } );
  for $c (split(//,$a)) {printf "%x ",ord($c);}' | fmt

Третий аргумент для кодирования - когда он является подгруппой - сообщает, что делать с непредставимыми символами.

Это дает:

e2 80 9c 66 6f 75 72 20 73 63 6f 72 65 e2 80 9d

Теперь это допустимый поток байтов utf8. Не можете сказать это при осмотре? Что ж, давайте попросим perl декодировать этот поток байтов как utf8:

$ perl -CO -MEncode -e '$a=decode("utf-8",
  "\xC3\xA2\xE2\x82\xAC\xC5\x93" .
  "four score" .
  "\xC3\xA2\xE2\x82\xAC\xC2\x9D");
  $a=encode("cp-1252", $a, sub { chr($_[0]) } );
  $a=decode("utf-8", $a, 1);
  for $c (split(//,$a)) {printf "%x ",ord($c);}' | fmt

Передача «1» в качестве третьего аргумента декодирования гарантирует, что наш код будет квакать, если поток байтов недействителен. Это дает:

201c 66 6f 75 72 20 73 63 6f 72 65 201d

Или напечатано:

$ perl -CO -MEncode -e '$a=decode("utf-8",
  "\xC3\xA2\xE2\x82\xAC\xC5\x93" .
  "four score" .
  "\xC3\xA2\xE2\x82\xAC\xC2\x9D");
  $a=encode("cp-1252", $a, sub { chr($_[0]) } );
  $a=decode("utf-8", $a, 1);
  print "$a\n"'
“four score”

Итак, я думаю, что полный алгоритм должен быть таким:

  1. Захватите поток байтов из mysql. Назначьте это $ bytestream.
  2. Пока $ bytestream является допустимым потоком байтов utf8:
    1. Присвойте текущее значение $ bytestream переменной $ good
    2. Если $ bytestream является полностью ASCII (т. Е. Каждый байт меньше 0x80), выйдите из этого цикла «while ... valid utf8».
    3. Установите $ bytestream равным результату "demangle ($ bytestream)", где demangle указан ниже. Эта процедура отменяет преобразователь cp1252-to-utf8, от которого, по нашему мнению, пострадали эти данные.
  3. Поместите $ good обратно в базу данных, если это не undef. Если $ good никогда не назначался, предположим, что $ bytestream был потоком байтов cp1252 и преобразуем его в utf8. (Конечно, оптимизируйте и не делайте этого, если цикл на шаге 2 ничего не изменил и т. Д.)

.

sub demangle {
  my($a) = shift;
  eval { # the non-string form of eval just traps exceptions
         # so that we return undef on exception
    local $SIG{__WARN__} = sub {}; # No warning messages
    $a = decode("utf-8", $a, 1);
    encode("cp-1252", $a, sub {$_[0] <= 255 or die $_[0]; chr($_[0])});
  }
}

Это основано на предположении, что на самом деле это очень редко для строки, которая не ' t all-ASCII, чтобы быть допустимым потоком байтов utf-8, если это действительно не utf-8. То есть это ' это не из тех вещей, которые случаются случайно.

РЕДАКТИРОВАТЬ ДОБАВИТЬ:

Обратите внимание, что эта техника, к сожалению, не слишком помогает в вашем примере с «Бобом». Я думаю, что эта строка также прошла два раунда преобразования cp1252-to-utf8, но, к сожалению, также были некоторые повреждения. Используя ту же технику, что и раньше, мы сначала читаем последовательность байтов как utf8 и смотрим на последовательность ссылок на символы Unicode, которые получаем:

$ perl -CO -MEncode -e '$a=decode("utf-8",
  "bob\xC3\xAF\xC2\xBF\xC2\xBDs");
  for $c (split(//,$a)) {printf "%x ",ord($c);}' | fmt

Это дает:

62 6f 62 ef bf bd 73

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

62 6f 62 ef bf bd 73

То есть та же последовательность чисел. На самом деле это действительный поток байтов utf-8, но то, что он декодирует, может вас удивить:

$ perl -CO -MEncode -e '$a=decode("utf-8",
  "bob\xC3\xAF\xC2\xBF\xC2\xBDs");
  $a=encode("cp-1252", $a, sub { chr(shift) } );
  $a=decode("utf-8", $a, 1);
  for $c (split(//,$a)) {printf "%x ",ord($c);}' | fmt

62 6f 62 fffd 73

То есть поток байтов utf-8, хотя и законный поток байтов utf-8, закодирован символ 0xFFFD, который обычно используется для «непереводимого символа». Я подозреваю, что здесь произошло следующее: при первом преобразовании * -to-utf8 был обнаружен символ, который он не распознал, и он был заменен на «непереводимый». Невозможно затем программно восстановить исходный символ.

Следствием этого является то, что вы не можете определить, является ли поток байтов допустимым utf8 (необходимый для этого алгоритма, который я дал выше), просто выполнив декодирование и затем ища 0xFFFD . Вместо этого вы должны использовать что-то вроде этого:

sub is_valid_utf8 {
  defined(eval { decode("utf-8", $_[0], 1) })
}
6
ответ дан 14 December 2019 в 13:46
поделиться
Другие вопросы по тегам:

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