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

Я выполняю Perl 5.10.0 и Пост-ГРЭС 8.4.3, и представляю в виде строки в базу данных, которая находится позади DBIx:: Класс.

Эти строки должны быть в UTF-8, и поэтому моя база данных работает в UTF-8. Unfortunatly некоторые из этих строк плохи, содержа уродливый 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?

9
задан brian d foy 21 February 2016 в 21:03
поделиться

3 ответа

Прежде всего, следуйте документации - модуль utf8 должен использоваться только в '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, как объясняет brian d foy:

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, СИМВОЛ ЗАМЕНЫ Unicode (ромб с вопросительным знаком).

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

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

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

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

9
ответ дан 4 December 2019 в 11:04
поделиться

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

    use Encode;

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

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

8
ответ дан 4 December 2019 в 11:04
поделиться

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

$data->title( Encode::encode("UTF-8", $title) )
2
ответ дан 4 December 2019 в 11:04
поделиться
Другие вопросы по тегам:

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