2014-08-09 9 views
5

В течение нескольких часов я борюсь с ошибкой в ​​своей программе Perl. Я не уверен, что я делаю что-то неправильно или интерпретатор делает, но код не является детерминированным, в то время как он должен быть детерминированным, ИМО. Также он демонстрирует такое же поведение на древнем Debian Lenny (Perl 5.10.0), и сервер, только что обновленный до Debian Wheezy (Perl 5.14.2). Оно сводилось к этой части коды Perl:Недетерминированность при кодировании при использовании open() со скалярными и I/O-уровнями в Perl

#!/usr/bin/perl 
use warnings; 
use strict; 
use utf8; 
binmode STDOUT, ":utf8"; 
binmode STDERR, ":utf8"; 
my $c = ""; 
open C, ">:utf8", \$c; 
print C "š"; 
close C; 
die "Does not happen\n" if utf8::is_utf8($c); 
print utf8::decode($c) ? "Decoded\n" : "Undecoded\n"; 

Инициализируется Perl 5 интерпретатора в строгом режиме с предупреждениями поддержки, со строками символов (в отличие от байтовых строк) и назвал стандартные потоки закодированных в UTF8 (внутреннее понятие UTF-8, но довольно близко, переход на полный UTF-8 не имеет значения). Затем он открывает дескриптор файла в «файл в памяти» (скалярная переменная), печатает в него один двухбайтовый символ UTF-8 и анализирует переменную после закрытия.

Скалярная переменная теперь всегда имеет бит UTF8. Однако иногда он содержит строку байтов (преобразован в строку символов через utf8::decode()), а иногда и строку символов, которую нужно просто перевернуть на свой бит UTF8 (Encode::_utf8_on()).

Когда я выполняю свой код повторно (1000 раз, через Bash), он печатает Undecoded и Decoded с примерно одинаковыми частотами. Когда я меняю строку, я пишу в «файл», например. добавьте новую строку в конец, Undecoded исчезает. Когда utf8::decode преуспевает, и я пытаюсь использовать его для одной и той же исходной строки в цикле, он сохраняет успех в том же экземпляре интерпретатора; однако, если он терпит неудачу, он продолжает терпеть неудачу.

Какое объяснение наблюдаемого поведения? Как я могу использовать дескриптор файла для скалярной переменной вместе с символьными строками?

Баш площадка:

for i in {1..1000}; do perl -we 'use strict; use utf8; binmode STDOUT, ":utf8"; binmode STDERR, ":utf8"; my $c = ""; open C, ">:utf8", \$c; print C "š"; close C; die "Does not happen\n" if utf8::is_utf8($c); print utf8::decode($c) ? "Decoded\n" : "Undecoded\n";'; done | grep Undecoded | wc -l 

Для справки и быть абсолютно уверены, я также сделал версию с педантичной обработки ошибок - те же результаты.

#!/usr/bin/perl 
use warnings; 
use strict; 
use utf8; 
binmode STDOUT, ":utf8" or die "Cannot binmode STDOUT\n"; 
binmode STDERR, ":utf8" or die "Cannot binmode STDERR\n"; 
my $c = ""; 
open C, ">:utf8", \$c or die "Cannot open: $!\n"; 
print C "š" or die "Cannot print: $!\n"; 
close C or die "Cannot close: $!\n"; 
die "Does not happen\n" if utf8::is_utf8($c); 
print utf8::decode($c) ? "Decoded\n" : "Undecoded\n"; 

ответ

3

Изучение $c в деталях показывает, что не имеет ничего общего с содержанием $c или его внутренностей, и результат decode точно отражает то, что он сделал или не сделал.

$ for i in {1..2}; do 
    perl -MDevel::Peek -we' 
     use strict; use utf8; 
     binmode STDOUT, ":utf8"; 
     binmode STDERR, ":utf8"; 
     my $c = ""; 
     open C, ">:utf8", \$c; 
     print C "š"; 
     close C; 
     die "Does not happen\n" if utf8::is_utf8($c); 
     Dump($c); 
     print utf8::decode($c) ? "Decoded\n" : "Undecoded\n"; 
     Dump($c) 
    ' 
    echo 
    done 

SV = PV(0x17c8470) at 0x17de990 
    REFCNT = 1 
    FLAGS = (PADMY,POK,pPOK) 
    PV = 0x17d7a40 "\305\241" 
    CUR = 2 
    LEN = 16 
Decoded 
SV = PV(0x17c8470) at 0x17de990 
    REFCNT = 1 
    FLAGS = (PADMY,POK,pPOK,UTF8) 
    PV = 0x17d7a40 "\305\241" [UTF8 "\x{161}"] 
    CUR = 2 
    LEN = 16 

SV = PV(0x2d0fee0) at 0x2d26400 
    REFCNT = 1 
    FLAGS = (PADMY,POK,pPOK) 
    PV = 0x2d1f4b0 "\305\241" 
    CUR = 2 
    LEN = 16 
Undecoded 
SV = PV(0x2d0fee0) at 0x2d26400 
    REFCNT = 1 
    FLAGS = (PADMY,POK,pPOK) 
    PV = 0x2d1f4b0 "\305\241" 
    CUR = 2 
    LEN = 16 

Это была ошибка в utf8::decode, но это было зафиксировано в 5.16.3 или ранее, вероятно, 5.16.0, так как он все еще присутствует в 5.14.2.

Соответствующее обходное решение для использования Encode's decode_utf8.

+0

Мне удалось найти ошибку. Его [ошибка в Perl # 82772] (https://rt.perl.org/Public/Bug/Display.html?id=82772) исправлена ​​в [commit f9cf141af97dff1d857ee2f905962a19ac97fc36] (https://github.com/Perl/perl5/commit/f9cf141af97dff1d857ee2f905962a19ac97fc36), который впервые включен в v5.15.8. Благодаря Corion от PerlMonks.org для помощи в поиске «Perl_sv_utf8_decode» в Perl-источнике и GitHub, что позволяет мне работать с репо (в частности, мне нужно прочитать Perl_sv_utf8_decode и прочитать его вину), не загружая тонны данных. – Palec

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

  • Нет связанных вопросов^_^