Я выполняю 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?
Прежде всего, следуйте документации - модуль 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).
Как вы получаете струны? Вы уверены, что Perl уже считает их UTF-8? Если они еще не декодированы (то есть октеты интерпретируются как некоторая кодировка), вам нужно сделать это самостоятельно:
use Encode;
my $ustring =
eval { decode( 'utf8', $byte_string, FB_CROAK ) }
or die "Could not decode string: $@";
Еще лучше, если вы знаете, что ваш источник строк уже UTF-8, вам нужно прочитать это исходный код как UTF-8. Посмотрите на код, который у вас есть, чтобы увидеть, правильно ли вы это делаете.
Как указано в документации для utf8 :: valid
, он возвращает истину, если строка помечена как UTF-8 и действительна UTF-8, или, если строка вообще не UTF-8 . Хотя это невозможно сказать, не видя код в контексте и не зная, что это за данные, скорее всего, то, что вам нужно, вовсе не проверка «действительный utf8»; возможно вам просто нужно сделать
$data->title( Encode::encode("UTF-8", $title) )