Как правильно получить графему? - PullRequest
7 голосов
/ 24 февраля 2012

Почему это печатает 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 ]

8 голосов
/ 24 февраля 2012

Ваш код правильный.

Вам действительно нужно играть в эти вещи по номерам;не верьте тому, что отображает «терминал».Пропустите его через программу 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
3 голосов
/ 24 февраля 2012

Это работает для меня, хотя у меня есть более старая версия Perl, 5.012, на Ubuntu. Мое единственное изменение в вашем сценарии: use 5.012;

$ perl so.pl 
Ü
1 голос
/ 24 февраля 2012

1) Очевидно, ваш терминал не может отображать расширенные символы. На моем терминале печатается:

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
1 голос
/ 24 февраля 2012

Могу ли я предположить, что это неверный вывод?Это легко проверить: замените код цикла на:

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..

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...