Как правильно получить графему?
Почему это печатает U
и не Ü
?
#!/usr/bin/env perl
use warnings;
use 5.014;
use utf8;
binmode STDOUT, ':utf8';
use charnames qw(:full);
my $string = "\N{LATIN CAPITAL LETTER U}\N{COMBINING DIAERESIS}";
while ( $string =~ /(\X)/g ) {
say $1;
}
# Output: U
4 ответа
Это работает для меня, хотя у меня есть старая версия Perl, 5.012
на убунту. Мое единственное изменение в вашем сценарии: use 5.012;
$ perl so.pl
Ü
Ваш код правильный.
Вы действительно должны играть в эти вещи по номерам; не верьте тому, что отображает "терминал". Передайте это через программу uniquote, вероятно, с -x
или же -v
и посмотреть, что он на самом деле делает.
Глаза обманывают, а программы еще хуже. Ваша терминальная программа глючит, так что врет вам. Нормализация не должна иметь значения.
$ perl -CS -Mutf8 -MUnicode::Normalize -E 'say "crème brûlée"'
crème brûlée
$ perl -CS -Mutf8 -MUnicode::Normalize -E 'say "crème brûlée"' | uniquote -x
cr\x{E8}me br\x{FB}l\x{E9}e
$ perl -CS -Mutf8 -MUnicode::Normalize -E 'say NFD "crème brûlée"'
crème brûlée
$ perl -CS -Mutf8 -MUnicode::Normalize -E 'say NFD "crème brûlée"' | uniquote -x
cre\x{300}me bru\x{302}le\x{301}e
$ perl -CS -Mutf8 -MUnicode::Normalize -E 'say NFC scalar reverse NFD "crème brûlée"'
éel̂urb em̀erc
$ perl -CS -Mutf8 -MUnicode::Normalize -E 'say NFC scalar reverse NFD "crème brûlée")' | uniquote -x
\x{E9}el\x{302}urb em\x{300}erc
$ perl -CS -Mutf8 -MUnicode::Normalize -E 'say scalar reverse NFD "crème brûlée"'
éel̂urb em̀erc
$ perl -CS -Mutf8 -MUnicode::Normalize -E 'say scalar reverse NFD "crème brûlée"' | uniquote -x
e\x{301}el\x{302}urb em\x{300}erc
1) Очевидно, ваш терминал не может отображать расширенные символы. На моем терминале он печатает:
U¨
2) \X
не делает то, что вы думаете, что делает. Он просто выбирает персонажей, которые идут вместе. Если вы используете строку "fu\N{COMBINING DIAERESIS}r"
, ваша программа отображает:
f
u¨
r
Обратите внимание, что диакритический знак печатается не отдельно, а с соответствующим символом.
3) Чтобы объединить все связанные символы в один, используйте модуль Unicode:: Normalize:
use Unicode::Normalize;
my $string = "fu\N{COMBINING DIAERESIS}r";
$string = NFC($string);
while ( $string =~ /(\X)/g ) {
say $1;
}
Он отображает:
f
ü
r
Могу ли я предположить, что это неверный вывод? Это легко проверить: замените код вашего цикла на:
my $counter;
while ( $string =~ /(\X)/g ) {
say ++$counter, ': ', $1;
}
... и посмотрите, сколько раз регулярное выражение будет соответствовать. Я думаю, что это все равно будет соответствовать только один раз.
Кроме того, вы можете использовать этот код:
use Encode;
sub codepoint_hex {
sprintf "%04x", ord Encode::decode("UTF-8", shift);
}
... и затем выведите codepoint_hex ($1) вместо простого $ 1 в цикле while.