2012-02-24 17 views
7

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

it [works] (http://ideone.com/tw9Qr) on perl 5.12. – jfs

+6

Вам нужно сыграть эти вещи по номерам; не доверяйте тому, что отображается «терминал». Проведите его через [uniquote] (http://training.perl.com/scripts/uniquote), возможно, с '-x' или' -v', и посмотрите, что он действительно делает. Глаза обманывают, а программы еще хуже. Ваша терминальная программа глючит, так и лжет вам. – tchrist

ответ

3

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

$ perl so.pl 
Ü 
+0

использовать 5.010; также выполняет свою работу. Протестировано на Ubuntu с perl 5.012; –

+0

Он не работал с 'Konsole' (2.7.2) из ​​KDE. Теперь я попробовал его с помощью 'xterm' и там он работал. –

1

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

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 в то время цикла.

+0

Вы почти никогда не должны вызывать кодирование/декодирование, пожалуйста. – tchrist

+0

Я не понимаю, почему я должен расшифровать матч. –

+0

@sid_com Вы не должны, конечно. – tchrist

1

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

** ПЕРВЫЙ: ** Это не то, что делает NFC. Это просто происходит здесь. Он многое делает; люди ошибаются в своих общих целях и целях. ** ВТОРОЕ: ** Если ваша программа терминалов не отображает совпадающие символы правильно, она обрабатывает канонически эквивалентные последовательности по-разному, что является злом и неправильным. См. Требование соответствия C6 на стр. 60 стандарта Unicode. Ваш багги: вам не нужно его использовать, иначе вы не можете писать: 'perl -CS -Mutf8 -MUnicode :: Normalize -E 'скалярный обратный NFD (" crème brûlée ")'' => 'éelurb emerc'. – tchrist

+1

Обратите внимание, что запуск NFC на «éelurb emerc» не будет «объединять все связанные символы в один». – tchrist

+1

Как вы думаете, что он считает '\ X'? – tchrist

8

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

Вам действительно нужно играть в эти вещи по номерам; не доверяйте тому, что отображается «терминал». Протяните его через the uniquote program, возможно, с -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 
+0

Я согласен. Никакие изменения не требуются для кода. Это проблема с терминалом OP (и мой тоже, «Konsole» KDE от Debian). – ikegami

Смежные вопросы