Подтвердить что ты не робот

Почему я возвращаю последний октет, когда моя программа Perl выводит кодированную строку UTF-8 в cmd.exe?

Update

Как предложил @ikegami, я сообщил об этом как об ошибке.

Ошибка # 121783 для perl5: Windows: кодированный вывод UTF-8 в cmd.exe с кодовой страницей 65001 вызывает неожиданный выход

Рассмотрим следующие программы C и Perl, которые выводят кодировку UTF-8 строки "& alpha; & beta; & gamma;"; на стандартном выходе:

Версия C:

#include <stdio.h>

int main(void) {
    /* UTF-8 encoded alpha, beta, gamma */
    char x[] = { 0xce, 0xb1, 0xce, 0xb2, 0xce, 0xb3, 0x00 };
    puts(x);
    return 0;
}
Вывод:
C:\…> chcp 65001
Active code page: 65001

C:\…> cttt.exe
αβγ

Perl версия:

C:\…>  perl -e "print qq{\xce\xb1\xce\xb2\xce\xb3\n}"
αβγ
�

Из того, что я могу сказать, последний октет 0xb3 выводится снова, на другой строке, который переводится на U+FFFD.

Обратите внимание, что перенаправление вывода устраняет этот эффект.

Я также могу проверить, что это последний октет, повторяющийся:

C:\…>  perl -e "print qq{\xce\xb1\xce\xb2\xce\xb3xyz\n}"
αβγxyz
z

С другой стороны, syswrite устраняет эту проблему.

C:\…>  perl -e "syswrite STDOUT, qq{\xce\xb1\xce\xb2\xce\xb3xyz\n}"
αβγxyz

Я наблюдал это в окнах cmd.exe на 64-битной Windows 8.1 Pro и 32-битной Windows Vista Home, используя как встроенные perl 5.18.2, так и ActiveState 5.16.3.

Я не вижу проблемы в средах Cygwin, Linux или Mac OS X. Кроме того, Cygwin perl 5.14.4 производит правильный вывод в cmd.exe.

Кроме того, когда кодовая страница установлена ​​в 437, вывод из версий C и Perl идентичен:

C:\…> chcp 437
Active code page: 437

C:\…> cttt.exe
╬▒╬▓╬│

C:\…>  perl -e "print qq{\xce\xb1\xce\xb2\xce\xb3\n}"
╬▒╬▓╬│

Что вызывает вывод второго октета дважды при печати из программы perl в cmd.exe, если для кодовой страницы установлено значение 65001?

PS: У меня есть дополнительная информация и скриншоты на в моем блоге. По этому вопросу я попытался перевести все на самые простые случаи.

PPS: вывод \n приводит к чему-то еще более интересному:

C:\…> perl -e "print qq{\xce\xb1\xce\xb2\xce\xb3xyz}"
αβγxyzxyz
C:\…> perl -e "print qq{\xce\xb1\xce\xb2\xce\xb3}"
αβγ�γ�
4b9b3361

Ответ 1

Следующая программа производит правильный вывод:

use utf8;
use strict;
use warnings;
use warnings qw(FATAL utf8);

binmode(STDOUT, ":unix:encoding(utf8):crlf");

print 'αβγxyz', "\n";

Вывод:

C:\…> chcp 65001
Active code page: 65001
C:\…> perl pttt.pl
αβγxyz

который, по-видимому, указывает мне, что есть некоторая забава с слоем :crlf. Я не понимаю, что внутренности достаточно, чтобы комментировать разумно об этом в этот момент.

После многих экспериментов я пришел к выводу, что если консоль уже установлена ​​на кодовую страницу 65001, binmode(STDOUT, ":unix:encoding(utf8):crlf"); будет работать. Однако обратите внимание на следующее:

binmode(STDOUT, ":unix:encoding(utf8):crlf");
print Dump [
    map {
        my $x = defined($_) ? $_ : '';
        $x =~ s/\A([0-9]+)\z/sprintf '0x%08x', $1/eg;
        $x;
    } PerlIO::get_layers(STDOUT, details => 1)
];
print "αβγxyz\n";

дает мне:

---
- unix
- ''
- 0x01205200
- crlf
- ''
- 0x00c85200
- unix
- ''
- 0x01201200
- encoding
- utf8
- 0x00c89200
- crlf
- ''
- 0x00c8d200
αβγxyz

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

I рассмотрел это немного дальше. Вот некоторые замечания из этого сообщения:

Флагов для первого слоя unix являются 0x01205200 = CANWRITE | TRUNCATE | CRLF | OPEN | NOTREG. Почему CRLF установлен для слоя unix в Windows? Я не знаю, что такое внутренности, чтобы понять это.

Однако флаги для второго слоя unix, который был нажат моим явным binmode, равны 0x01201200 = 0x01205200 и ~ CRLF. Это то, что имело бы смысл для меня начать.

Флаги для первого слоя crlf 0x00c85200 = CANWRITE | TRUNCATE | CRLF | LINEBUF | FASTGETS | TTY. Флаги для второго layer, который я нажимаю после слоя :encoding(utf8), 0x00c8d200 = 0x00c85200 | UTF8.

Теперь, если я открою файл с помощью open my $fh, '>:encoding(utf8)', 'ttt' и выгружу ту же информацию, я получаю:

---
- unix
- ''
- 0x00201200
- crlf
- ''
- 0x00405200
- encoding
- utf8
- 0x00409200

Как и ожидалось, слой unix не устанавливает флаг CRLF.