Почему мой Perl плохо работает с Unicode?

Почему мой Perl плохо работает с Unicode?

На моей новой установке Arch, perlпохоже, не очень хорошо работает с Unicode. Например, учитывая этот входной файл:

ελα ρε
王小红

Эта команда должна выдать мне последние два символа каждой строки:

$ perl -CIO -pe 's/.*(..)$/$1/' file
ε
º¢

Однако, как вы видите выше, я получаю бессмыслицу. Правильный вывод:

ρε
小红

Я знаю, что мой терминал ( gnome-terminator) поддерживает UTF-8, поскольку оба эти кода работают так, как и ожидалось:

$ cat file
ελα ρε
王小红
$ perl -pe '' file
ελα ρε
王小红

К сожалению, без него -CIOтакже perlнекорректно обрабатывается файл:

$ perl -pe 's/.*(..)$/$1/' file
ε
��

Это также не должно быть проблемой локали:

$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

Я предполагаю, что мне нужно установить некоторые пакеты Perl, но я не знаю, какие именно. Некоторая важная информация:

$ perl --version | grep subversion
This is perl 5, version 22, subversion 0 (v5.22.0) built for x86_64-linux-thread-multi

$ pacman -Qs unicode
local/fribidi 0.19.7-1
    A Free Implementation of the Unicode Bidirectional Algorithm
local/icu 55.1-1
    International Components for Unicode library
local/libunistring 0.9.6-1
    Library for manipulating Unicode strings and C strings
local/perl 5.22.0-1 (base)
    A highly capable, feature-rich programming language
local/perl-unicode-stringprep 1.105-1
    Preparation of Internationalized Strings (RFC 3454)
local/perl-unicode-utf8simple 1.06-5
    Conversions to/from UTF8 from/to characterse
local/ttf-arphic-uming 0.2.20080216.1-5
    CJK Unicode font Ming style

Как мне заставить мою установку Perl корректно работать с Unicode?

решение1

Проблема, которую вы описываете, является стандартным поведением в системах, на которых я тестировал, Iи Oвлияет на stdin и stdout, поэтому это должно сработать:

→ cat data | perl -CIO -pe 's/.*(..)$/$1/'
ρε
小红

В то время как это может и не быть:

→ perl -CIO -pe 's/.*(..)$/$1/' data
ε
º¢

Естьеще два вариантаperl -Cкоторые вызывают желаемое вами поведение.

i     8   UTF-8 is the default PerlIO layer for input streams
o    16   UTF-8 is the default PerlIO layer for output streams

По сути, это говорит Perl использовать форму открытия файла:

open(F, "<:utf8", "data");

или вы можете использовать perl -CSDчто является сокращением дляperl -CIOEio

S     7   I + O + E
D    24   i + o

Тогда вы получите

→ perl -CSD -pe 's/.*(..)$/$1/' data
ρε
小红

Если PERLIOпеременная окружения установлена ​​и включена, :utf8это поведение также будет включено.

Похоже, это поведение по умолчанию дляperl также не может быть изменено во время конфигурации/компиляции (комментарий cuonglm ниже). Arch определенно не можетустановить что угодно.Я сомневаюсь, что пакеты Debian Perl изменят поведение по умолчанию.

решение2

Это не проблема системы, а проблема perlсама по себе.

-CIOустановите только кодировку UTF-8 для STDINи STDOUT, двух из трех perlпредопределенных дескрипторов файлов (у вас также есть -Eдля ).STDERR

Когда вы используете:

perl -CIO -pe 's/.*(..)$/$1/' file

perlиспользовать оператор алмаз <>для обработки файла. С каких пор оператор алмаз<> используетсяоткрытая (с двумя аргументами форма)чтобы создать новый дескриптор файла для каждого файла из командной строки, эти дескрипторы файлов не будут зависеть от кодировки UTF-8, которую вы установили для STDINи STDOUT.

Итак, вы можете передать содержимое файла perlчерез его stdin, и это будет работать:

perl -CIO -pe 's/.*(..)$/$1/' <file

Другие варианты см.@Ответ Мэтта.


Если вы хотите perlиспользовать свою локаль для слоя кодирования по умолчанию, вы можете использовать:

perl -Mopen=:locale -pe 's/.*(..)$/$1/' file

Когда вы используетеPERLIO для настройки слоя кодирования вам следуетиспользовать :encoding(uf8)вместо:utf8.

Использование :utf8пропускает этап кодирования и может вызвать проблемы при чтении недопустимых последовательностей байтов UTF-8, что приведет к проблемам безопасности.

Связанный контент