Как я могу заставить Perl обнаруживать плохие последовательности UTF-8? - PullRequest
9 голосов
/ 17 апреля 2010

Я использую Perl 5.10.0 и Postgres 8.4.3 и строки в базе данных, которая находится за DBIx :: Class .

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

DBI Exception: DBD::Pg::st execute failed: ERROR: invalid byte sequence for encoding "UTF8": 0xb5

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

if(not utf8::valid($title)){
   $title="Invalid UTF-8";
}
$data->title($title);
$data->update();

Однако Perl, похоже, считает, что строки допустимы, но все равно выдает исключения.

Как я могу заставить Perl обнаруживать плохой UTF-8?

Ответы [ 3 ]

9 голосов
/ 17 апреля 2010

Прежде всего, пожалуйста, следуйте документации - модуль utf8 должен only использоваться в 'use utf8;' Форма, чтобы указать, что ваш исходный код UTF-8 вместо Latin-1. Не используйте никакие функции utf8.

Perl различает байты и строки UTF-8. В байтовом режиме Perl не знает и не заботится о том, какую кодировку вы используете, и будет использовать Latin-1, если вы его напечатаете. Взять, к примеру, знак евро (€). В UTF-8 это 3 байта, 0xE2, 0x82, 0xAC. Если вы напечатаете длину этих байтов, Perl вернет 3. Опять же, это не заботится о кодировке. Это могут быть любые байты или любая кодировка, легальная или нелегальная.

Если вы используете модуль Encode и вызовете Encode::decode("UTF-8', $bytes), вы получите новую строку с установленным так называемым флагом UTF8. Perl теперь знает, что ваша строка в UTF-8, и вернет длину 1.

Проблема в том, что utf8::valid относится только ко второму типу строки. Ваши строки, вероятно, в первой форме, в байтовом режиме, и utf8::valid просто возвращает true для чего-либо в байтовой форме. Это задокументировано в perldoc.

Решение состоит в том, чтобы заставить Perl декодировать ваши байтовые строки как UTF-8 и обнаруживать любые ошибки. Это можно сделать с помощью FB_CROAK, как объясняет Брайан Д. Фой:

my $ustring =
    eval { decode( 'UTF-8', $byte_string, FB_CROAK ) }
    or die "Could not decode string: $@";

Затем вы можете перехватить эту ошибку и пропустить эти недопустимые строки.

Или, если вы знаете, что ваш код в основном UTF-8 с несколькими недопустимыми последовательностями здесь и там, вы можете использовать:

my $ustring = decode( 'UTF-8', $byte_string );

, который использует режим по умолчанию FB_DEFAULT, заменяя недопустимые символы на U + FFFD, символ замены Юникод (ромб с вопросительным знаком в нем).

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

my $byte_string = encode('UTF-8', $ustring);

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

Наконец, пожалуйста, используйте UTF-8 вместо utf8 в своих вызовах на decode. Последний является более слабым и позволяет некоторым недопустимым последовательностям UTF-8 (таким как последовательности вне диапазона Unicode) быть позволенным через.

8 голосов
/ 17 апреля 2010

Как вы получаете свои строки? Вы уверены, что Perl думает, что они уже UTF-8? Если они еще не декодированы (то есть октеты интерпретируются как некоторая кодировка), вам нужно сделать это самостоятельно:

    use Encode;

    my $ustring =
      eval { decode( 'utf8', $byte_string, FB_CROAK ) }
      or die "Could not decode string: $@";

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

2 голосов
/ 17 апреля 2010

Как указывается в документации для utf8::valid, возвращается значение true, если строка помечена как UTF-8 и является допустимой UTF-8, или если строка вообще не является UTF-8, Хотя невозможно сказать, не увидев код в контексте и не зная, что это за данные, скорее всего, что вам нужно, это вообще не проверка «valid utf8»; наверное тебе просто нужно сделать

$data->title( Encode::encode("UTF-8", $title) )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...