Perl Language
Unicode
Поиск…
замечания
Предупреждение о кодировке имени файла
Стоит отметить, что Filename Encoding - это не только специфичная для платформы, но и специфичная для файловой системы .
Он никогда полностью безопасно предположить (но часто обычно) , что только потому , что вы можете кодировать и записывать заданное имя файла, что когда вы позже попытаться открыть тот же имя файла для чтения, он все равно будет называться то же самое.
Например, если вы пишете в файловую систему, такую как FAT16
которая не поддерживает юникод, ваши имена файлов могут быть беззвучно переведены в ASCII-совместимые формы.
Но еще менее безопасно предположить, что файл, который вы можете создавать, читать и писать с помощью явного именования, будет называться тем же самым при запросе через другие вызовы, например, readdir
может возвращать разные байты для вашего имени файла, чем вы указали для open
,
В некоторых системах, таких как VAX, вы даже не можете предположить, что readdir
вернет то же имя файла, которое вы указали с open
для имен файлов, просто как foo.bar
, поскольку расширения имен файлов могут быть искажены операционной системой.
Кроме того, в UNIX существует очень либеральный набор юридических символов для имен файлов, которые разрешена ОС, исключая только /
и \0
, где, как и в Windows, существуют определенные диапазоны символов, которые запрещены в именах файлов и будут вызывать ошибки.
Здесь проявляйте большую осторожность, избегайте причудливых трюков с именами файлов, если у вас есть выбор , и всегда проводите тесты, чтобы убедиться, что любые причудливые трюки, которые вы используете, непротиворечивы.
Выполняйте вдвойне осторожность, если вы пишете код, предназначенный для запуска на платформах вне вашего контроля, например, если вы пишете код, предназначенный для CPAN
, и предположите, что по крайней мере 5% вашей пользовательской базы будут застревать с помощью некоторых древних или сломанных технологий, либо по выбору, случайно, либо силами вне их контроля, и что они будут сговариваться создавать ошибки для них.
: encoding (utf8) vs: utf8
Поскольку UTF-8 является одним из внутренних форматов представления строк в Perl, шаг кодирования / декодирования часто может быть пропущен. Вместо :encoding(utf-8)
вы можете просто использовать :utf8
, если ваши данные уже находятся в UTF-8. :utf8
можно безопасно использовать с выходными потоками, тогда как для входного потока это может быть опасно, потому что это вызывает внутреннюю несогласованность, когда у вас есть недопустимые последовательности байтов. Кроме того, использование :utf8
для ввода может привести к нарушениям безопасности, поэтому рекомендуется использовать :encoding(utf-8)
.
Подробнее: В чем разница между: encoding и: utf8
UTF-8 vs utf8 vs UTF8
Начиная с Perl v5.8.7
, "UTF-8"
(с тире) означает UTF-8 в строгом и безопасном виде, тогда как "utf8"
означает UTF-8 в его либеральной и свободной форме.
Например, "utf8"
может использоваться для кодовых точек, которые не существуют в Юникоде, например, 0xFFFFFFFF
. Соответственно, недопустимые байтовые последовательности UTF-8, такие как "\x{FE}\x{83}\x{BF}\x{BF}\x{BF}\x{BF}\x{BF}"
будут декодироваться в недействительный Unicode (но действует Perl) элемент кода ( 0xFFFFFFFF
) при использовании "utf8"
, в то время как "UTF-8"
кодирования не допустит декодирования для кодовых значений за пределами диапазона допустимых Unicode и даст вам замену символа ( 0xFFFD
) вместо этого.
Поскольку имена кодировок нечувствительны к регистру, "UTF8"
совпадает с "utf8"
(т.е. нестрогим вариантом).
Более подробная информация: UTF-8 vs. utf8 vs. UTF8
Подробнее Чтение
Подробная информация о работе с Unicode в Perl описана более подробно в следующих источниках:
- perlunicode
- perlunitut
- perluniintro
- perlunifaq
- perlunicook
- utf8 прагма
- Функция unicode_strings
- открытая прагма
- PerlIO
- PerlIO :: кодирование
- открытая функция
- шифровать
- perlrun - ключи командной строки
- Глава 6, Программирование Perl
Сообщения из stackoverflow.com (caveat: возможно, не обновлены):
YouTube видео:
- Million Billion Squiggly Characters от Ricardo Signes на YAPC NA 2016.
Создание имен файлов
В следующих примерах используется кодировка UTF-8 для представления имен файлов (и имен каталогов) на диске. Если вы хотите использовать другую кодировку, вы должны использовать Encode::encode(...)
.
use v5.14;
# Make Perl recognize UTF-8 encoded characters in literal strings.
# For this to work: Make sure your text-editor is using UTF-8, so
# that bytes on disk are really UTF-8 encoded.
use utf8;
# Ensure that possible error messages printed to screen are converted to UTF-8.
# For this to work: Check that your terminal emulator is using UTF-8.
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';
my $filename = 'æ€'; # $filename is now an internally UTF-8 encoded string.
# Note: in the following it is assumed that $filename has the internal UTF-8
# flag set, if $filename is pure ASCII, it will also work since its encoding
# overlaps with UTF-8. However, if it has another encoding like extended ASCII,
# $filename will be written with that encoding and not UTF-8.
# Note: it is not necessary to encode $filename as UTF-8 here
# since Perl is using UTF-8 as its internal encoding of $filename already
# Example1 -- using open()
open ( my $fh, '>', $filename ) or die "Could not open '$filename': $!";
close $fh;
# Example2 -- using qx() and touch
qx{touch $filename};
# Example3 -- using system() and touch
system 'touch', $filename;
# Example4 -- using File::Touch
use File::Touch;
eval { touch( $filename ) }; die "Could not create file '$filename': $!" if $@;
Чтение имен файлов
Perl не пытается декодировать имена файлов, возвращаемые встроенными функциями или модулями. Такие строки, представляющие имена файлов, всегда должны быть явно декодированы, чтобы Perl распознавал их как Unicode.
use v5.14;
use Encode qw(decode_utf8);
# Ensure that possible error messages printed to screen are converted to UTF-8.
# For this to work: Check that you terminal emulator is using UTF-8.
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';
# Example1 -- using readdir()
my $dir = '.';
opendir(my $dh, $dir) or die "Could not open directory '$dir': $!";
while (my $filename = decode_utf8(readdir $dh)) {
# Do something with $filename
}
close $dh;
# Example2 -- using getcwd()
use Cwd qw(getcwd);
my $dir = decode_utf8( getcwd() );
# Example3 -- using abs2rel()
use File::Spec;
use utf8;
my $base = 'ø';
my $path = "$base/b/æ";
my $relpath = decode_utf8( File::Spec->abs2rel( $path, $base ) );
# Note: If you omit $base, you need to encode $path first:
use Encode qw(encode_utf8);
my $relpath = decode_utf8( File::Spec->abs2rel( encode_utf8( $path ) ) );
# Example4 -- using File::Find::Rule (part1 matching a filename)
use File::Find::Rule;
use utf8;
use Encode qw(encode_utf8);
my $filename = 'æ';
# File::Find::Rule needs $filename to be encoded
my @files = File::Find::Rule->new->name( encode_utf8($filename) )->in('.');
$_ = decode_utf8( $_ ) for @files;
# Example5 -- using File::Find::Rule (part2 matching a regular expression)
use File::Find::Rule;
use utf8;
my $pat = '[æ].$'; # Unicode pattern
# Note: In this case: File::Find::Rule->new->name( qr/$pat/ )->in('.')
# will not work since $pat is Unicode and filenames are bytes
# Also encoding $pat first will not work correctly
my @files;
File::Find::Rule->new->exec( sub { wanted( $pat, \@files ) } )->in('.');
$_ = decode_utf8( $_ ) for @files;
sub wanted {
my ( $pat, $files ) = @_;
my $name = decode_utf8( $_ );
my $full_name = decode_utf8( $File::Find::name );
push @$files, $full_name if $name =~ /$pat/;
}
Примечание. Если вы обеспокоены недопустимым UTF-8 в именах файлов, использование decode_utf8( ... )
в приведенных выше примерах, вероятно, должно быть заменено decode( 'utf-8', ... )
. Это объясняется тем, что decode_utf8( ... )
является синонимом decode( 'utf8', ... )
и существует разница между кодировками utf-8
и utf8
(см. Ниже замечания для получения дополнительной информации), где utf-8
больше строгого, что приемлемо, чем utf8
.
Переключатели командной строки для однострочных
Включить прагму utf8
Чтобы включить прагму utf8
в одном -Mutf8
, интерпретатор perl должен вызываться с параметром -Mutf8
:
perl -Mutf8 -E 'my $人 = "human"; say $人'
Работа с Unicode с ключом -C
Флаг командной строки -C
позволяет вам управлять функциями Unicode. За ним может следовать список букв опций.
Стандартный ввод-вывод
-
I
-STDIN
будет в UTF-8 -
O
-STDOUT
будет в UTF-8 -
E
-STDERR
будет в UTF-8 -
S
- сокращение дляIOE
, стандартные потоки ввода-вывода будут в UTF-8
echo "Ματαιότης ματαιοτήτων" | perl -CS -Mutf8 -nE 'say "ok" if /Ματαιότης/'
Аргументы сценария
-
A
- обрабатывает@ARGV
как массив строк с кодировкой UTF-8
perl -CA -Mutf8 -E 'my $arg = shift; say "anteater" if $arg eq "муравьед"' муравьед
Уровень по умолчанию PerlIO
-
i
- UTF-8 - это уровень PerlIO по умолчанию для входных потоков -
o
- UTF-8 является уровнем PerlIO по умолчанию для выходных потоков -
D
- сокращенноеio
perl -CD -Mutf8 -e 'open my $fh, ">", "utf8.txt" or die $!; print $fh "개미 조심해"'
-M
и -C
могут быть объединены:
perl -CASD -Mutf8 -E 'say "Ματαιότης ματαιοτήτων\n"';
Стандартный ввод-вывод
Кодировка, используемая для стандартных дескрипторов ввода / вывода ( STDIN
, STDOUT
и STDERR
), может быть задана отдельно для каждого дескриптора с использованием binmode
:
binmode STDIN, ':encoding(utf-8)';
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';
Примечание: при чтении, в общем, предпочтительнее :encoding(utf-8)
over :utf8
, см. Примечания для получения дополнительной информации.
Кроме того, вы можете использовать open
прагму.
# Setup such that all subsequently opened input streams will use ':encoding(utf-8)'
# and all subsequently opened output streams will use ':utf8'
# by default
use open (IN => ':encoding(utf-8)', OUT => ':utf8');
# Make the (already opened) standard file handles inherit the setting
# given by the IO settings for the open pragma
use open ( :std );
# Now, STDIN has been converted to ':encoding(utf-8)', and
# STDOUT and STDERR have ':utf8'
Кроме того, для установки всех дескрипторов файлов (как тех, которые еще предстоит открыть, так и стандартных) :encoding(utf-8)
:
use open qw( :encoding(utf-8) :std );
Ручки файлов
Установка кодировки с помощью open ()
При открытии текстового файла вы можете явно указать его кодировку с помощью трех аргументов open()
. Этот en- / decoder, прикрепленный к дескриптору файла, называется «слоем ввода / вывода»:
my $filename = '/path/to/file';
open my $fh, '<:encoding(utf-8)', $filename or die "Failed to open $filename: $!";
См. Замечания для обсуждения различий между :utf8
и :encoding(utf-8)
.
Настройка кодировки с помощью binmode ()
Кроме того, вы можете использовать binmode () для установки кодировки для отдельного дескриптора файла:
my $filename = '/path/to/file';
open my $fh, '<', $filename or die "Failed to open $filename: $!";
binmode $fh, ':encoding(utf-8)';
открытая прагма
Чтобы избежать установки кодировки для каждого дескриптора файла отдельно, вы можете использовать open
прагму для установки уровня ввода-вывода по умолчанию, используемого всеми последующими вызовами функции open()
и аналогичных операторов в лексической области этой прагмы:
# Set input streams to ':encoding(utf-8)' and output streams to ':utf8'
use open (IN => ':encoding(utf-8)', OUT => ':utf8');
# Or to set all input and output streams to ':encoding(utf-8)'
use open ':encoding(utf-8)';
Установка кодировки с помощью командной строки -C
Наконец, также возможно запустить интерпретатор perl с флагом -CD
который применяет UTF-8 в качестве уровня ввода-вывода по умолчанию. Однако этого варианта следует избегать, поскольку он зависит от поведения конкретного пользователя, которое невозможно предсказать и не контролировать.
Прагма utf8: использование Unicode в ваших источниках
utf8
указывает, что исходный код будет интерпретироваться как UTF-8. Конечно, это будет работать, только если ваш текстовый редактор также сохранит исходный код в кодировке UTF-8.
Теперь строковые литералы могут содержать произвольные символы Unicode; идентификаторы могут также содержать Unicode, но только словарные символы (см. perldata и perlrecharclass для получения дополнительной информации):
use utf8;
my $var1 = '§я§©😄'; # works fine
my $я = 4; # works since я is a word (matches \w) character
my $p§2 = 3; # does not work since § is not a word character.
say "ya" if $var1 =~ /я§/; # works fine (prints "ya")
Примечание . При печати текста на терминал убедитесь, что он поддерживает UTF-8. *
Могут быть сложные и противоинтуитивные отношения между кодированием вывода и источника. Находясь на терминале UTF-8, вы можете обнаружить, что добавление utf8
похоже, нарушает:
$ perl -e 'print "Møøse\n"'
Møøse
$ perl -Mutf8 -e 'print "Møøse\n"'
M��se
$ perl -Mutf8 -CO -e 'print "Møøse\n"'
Møøse
В первом случае Perl обрабатывает строку как необработанные байты и печатает их так. Поскольку эти байты являются действительными UTF-8, они выглядят правильными, хотя Perl действительно не знает, какие символы они (например, length("Møøse")
вернет 7, а не 5). Когда вы добавляете -Mutf8
, Perl правильно декодирует исходный код UTF-8 для символов, но выход по умолчанию работает в режиме Latin-1, а печать Latin-1 на терминал UTF-8 не работает. Только при переключении STDOUT
на UTF-8 с использованием -CO
выход будет правильным.
use utf8
не влияет на стандартную кодировку ввода-вывода и файлы.
Обработка недействительных UTF-8
Чтение недопустимого UTF-8
При чтении кодированных данных UTF-8 важно знать, что кодированные данные UTF-8 могут быть недействительными или искаженными. Такие данные обычно не принимаются вашей программой (если вы не знаете, что делаете). При неожиданном столкновении с искаженными данными можно рассмотреть различные действия:
- Распечатайте stacktrace или сообщение об ошибке и прекратите программу изящно, или
- Вставьте символ замещения в том месте, где появилась некорректная последовательность байтов, напечатайте предупреждающее сообщение в STDERR и продолжайте читать, поскольку ничего не произошло.
По умолчанию Perl warn
вас о сбоях в кодировке, но не прерывает вашу программу. Вы можете сделать свою программу прерванной, сделав предупреждения UTF-8 фатальными, но помните о предостережениях в Fatal Warnings .
Следующий пример записывает 3 байта в кодировке ISO 8859-1 на диск. Затем он пытается прочитать байты снова как кодированные данные UTF-8. Один из байтов, 0xE5
, является недопустимой последовательностью в 1 байт UTF-8:
use strict;
use warnings;
use warnings FATAL => 'utf8';
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';
my $bytes = "\x{61}\x{E5}\x{61}"; # 3 bytes in iso 8859-1: aåa
my $fn = 'test.txt';
open ( my $fh, '>:raw', $fn ) or die "Could not open file '$fn': $!";
print $fh $bytes;
close $fh;
open ( $fh, "<:encoding(utf-8)", $fn ) or die "Could not open file '$fn': $!";
my $str = do { local $/; <$fh> };
close $fh;
print "Read string: '$str'\n";
Программа будет прервана с фатальным предупреждением:
utf8 "\xE5" does not map to Unicode at ./test.pl line 10.
Строка 10 здесь вторая вторая строка, и ошибка возникает в части строки с помощью <$fh>
при попытке прочитать строку из файла.
Если вы не делаете предупреждений, фатальных в вышеуказанной программе, Perl все равно будет печатать предупреждение. Однако в этом случае он попытается восстановить из искаженного байта 0xE5
, вставив в поток четыре символа \xE5
, а затем продолжит следующий байт. В результате программа напечатает:
Read string: 'a\xE5a'