Юникод жонглирует Perl

У меня есть проблема, которую я считал тривиальной. Мне приходится иметь дело с умляутами из немецкого алфавита (äöü). В Юникоде вроде бы есть несколько способов их отображения, один из них — комбинирование символов. Мне нужно нормализовать эти разные способы, заменить их все односимвольным кодом.

Такой девиантный умлаут легко найти: это буква aou, за которой следует символ UTF-8 \uCC88. Поэтому я подумал, что регулярного выражения будет достаточно.

Это моя функция преобразования, использующая пакет Encoding.

# This sub can be extended to include more conversions
sub convert {
    local $_;
    $_ = shift;

    $_ = encode( "utf-8", $_ );

    s/u\xcc\x88/ü/g;
    s/a\xcc\x88/ä/g;
    s/o\xcc\x88/ö/g;
    s/U\xcc\x88/Ü/g;
    s/A\xcc\x88/Ä/g;
    s/O\xcc\x88/Ö/g;

    return $_;
}

Но получившийся напечатанный умлаут представляет собой какой-то еще более коварный символ (теперь он занимает 4 байта), а не тот, что в этом список.

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

Даже изменение строк замены на

s/u\xcc\x88/\xc3\xbc/g;
s/a\xcc\x88/\xc3\xa4/g;
s/o\xcc\x88/\xc3\xb6/g;
s/U\xcc\x88/\xc3\x9c/g;
s/A\xcc\x88/\xc3\x84/g;
s/O\xcc\x88/\xc3\x96/g;

не помогло, они конвертируются правильно, но затем в байтах следует "\xC2\xA4".

Любая помощь?


person Lanbo    schedule 23.11.2011    source источник


Ответы (1)


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

Первый шаг — изучить тему кодирования на Perl. Это необходимо для понимания термина «символьные строки», который я буду использовать в следующем абзаце.

Когда у вас есть строка символов, она может находиться в любом из различных состояний (де)композиции. Используйте модуль Unicode::Normalize, чтобы изменить строку символов, и прочитайте соответствующие главы об эквивалентности и нормализации в спецификации Unicode. для кровавых подробностей они связаны внизу документации этого модуля.

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

use charnames qw(:full);
use Unicode::Normalize qw(NFC);
my $original_character_string = "In des Waldes tiefsten Gr\N{LATIN SMALL LETTER U WITH DIAERESIS}nden ist kein R\N{LATIN SMALL LETTER A}\N{COMBINING DIAERESIS}uber mehr zu finden.";
my $modified_character_string = NFC($original_character_string);
# "In des Waldes tiefsten Gr\x{fc}nden ist kein R\x{e4}uber mehr zu finden."
person daxim    schedule 23.11.2011
comment
$NFC_string = NFC($string) -> Он возвращает форму нормализации C (сформированную канонической декомпозицией, за которой следует каноническая композиция). Так что NFD здесь бесполезен. - person Hynek -Pichi- Vychodil; 23.11.2011
comment
Нет-нет, сначала NFD, а потом NFC может быть безопаснее. Я знаю, что может быть как-то много однобуквенных представлений некоторых символов, которые затем будут разложены, а затем объединены в одно представление во всем документе. - person Lanbo; 23.11.2011