Perl Language
Unicode
Sök…
Anmärkningar
En varning om kodnamn för filnamn
Det bör vara värt att nämna att kodnamn för filnamn inte bara är plattformspecifik utan också filsystemspecifikt .
Det är aldrig helt säkert att anta (men ofta är det) att bara för att du kan koda och skriva till ett givet filnamn, att när du senare försöker öppna samma filnamn för läsning, kommer det fortfarande att kallas samma sak.
Om du till exempel skriver till ett filsystem som FAT16
som inte stöder unicode, kan dina filnamn tyst översättas till ASCII-kompatibla former.
Men det är ännu mindre säkert att anta att en fil du kan skapa, läsa och skriva till genom uttrycklig namngivning kommer att kallas samma sak när du fråges genom andra samtal, till exempel kan readdir
returnera andra byte för ditt filnamn än du angav för att open
.
På vissa system såsom VAX kan du inte ens alltid utgå från att readdir
kommer tillbaka samma filnamn du angav med open
för filnamn så enkelt som foo.bar
, eftersom filnamnstillägg kan manglas av operativsystemet.
På UNIX finns det också en mycket liberal uppsättning lagliga tecken för filnamn som OS tillåter, exklusive endast /
och \0
, där som på Windows finns det specifika intervall med tecken som är förbjudna i filnamn och kommer att orsaka fel.
Motion mycket försiktig här, undvika fina tricks med filnamn om du har ett val, och alltid har tester för att se till att någon fancy tricks du använder är konsekventa.
Var dubbelt så mycket försiktig om du skriver kod som är avsedd att köras på plattformar utanför din kontroll, till exempel om du skriver kod som är avsedd för CPAN
, och antar att minst 5% av din användarbas kommer att sitta fast med vissa antik eller trasig teknik, antingen genom val, av misstag eller av makter utanför deras kontroll, och att dessa kommer att konspirera för att skapa buggar för dem.
: kodning (utf8) vs: utf8
Eftersom UTF-8 är ett av de interna formaten för representation av strängar i Perl, kan kodnings- / avkodningssteget ofta hoppas över. Istället för :encoding(utf-8)
kan du helt enkelt använda :utf8
, om dina data redan finns i UTF-8. :utf8
kan användas säkert med utgångsströmmar, medan det för inmatningsström kan vara farligt, eftersom det orsakar intern inkonsekvens när du har ogiltiga bytesekvenser. Att använda :utf8
för inmatning kan också leda till säkerhetsbrott, så användningen av :encoding(utf-8)
rekommenderas.
Mer information: Vad är skillnaden mellan: kodning och: utf8
UTF-8 vs utf8 vs UTF8
Från Perl v5.8.7
"UTF-8"
(med streck) UTF-8 i sin strikta och säkerhetsmedvetna form, medan "utf8"
betyder UTF-8 i sin liberala och lösa form.
Till exempel kan "utf8"
användas för kodpunkter som inte finns i Unicode, som 0xFFFFFFFF
. På motsvarande sätt kommer ogiltiga UTF-8-bytesekvenser som "\x{FE}\x{83}\x{BF}\x{BF}\x{BF}\x{BF}\x{BF}"
att avkodas till en ogiltig Unicode-kod (men giltig Perl) -kodpunkt ( 0xFFFFFFFF
) när du använder "utf8"
, medan "UTF-8"
-kodningen inte tillåter avkodning till kodpunkterna utanför området för giltigt Unicode och skulle ge dig ett substitutionstecken ( 0xFFFD
) istället.
Eftersom kodningsnamn är okänsliga för "UTF8"
är "UTF8"
detsamma som "utf8"
(dvs. icke-strikt variant).
Mer information: UTF-8 vs. utf8 vs. UTF8
Mer läsning
Detaljer om Perls Unicode-hantering beskrivs mer detaljerat i följande källor:
- perlunicode
- perlunitut
- perluniintro
- perlunifaq
- perlunicook
- utf8 pragma
- unicode_strings-funktionen
- öppen pragma
- PerlIO
- PerlIO :: kodning
- öppen funktion
- Koda
- perlrun - kommandoradsväxlar
- Kapitel 6, Programmering Perl
Inlägg från stackoverflow.com (varning: kanske inte är uppdaterade):
Youtube videor:
- En miljon miljarder squiggly karaktärer av Ricardo Signes på YAPC NA 2016.
Skapa filnamn
Följande exempel använder UTF-8-kodningen för att representera filnamn (och katalognamn) på disken. Om du vill använda en annan kodning bör du använda 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 $@;
Läs filnamn
Perl försöker inte avkoda filnamn som returneras av inbyggda funktioner eller moduler. Sådana strängar som representerar filnamn bör alltid avkodas uttryckligen för att Perl ska känna igen dem som 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/;
}
Obs: Om du är orolig för ogiltiga UTF-8 i filnamnen, decode_utf8( ... )
användningen av decode_utf8( ... )
i exemplen ovan förmodligen ersättas med decode( 'utf-8', ... )
. Detta beror på att decode_utf8( ... )
är en synonym för decode( 'utf8', ... )
och det finns en skillnad mellan kodningarna utf-8
och utf8
(se anmärkningar nedan för mer information) där utf-8
är mer strikt för vad som är acceptabelt än utf8
.
Kommandoradsväxlar för enfoder
Aktivera utf8-pragma
För att aktivera utf8
pragma i enfodring, bör perl-tolk anropas med -Mutf8
alternativet:
perl -Mutf8 -E 'my $人 = "human"; say $人'
Unicode-hantering med -C-omkopplare
Med kommandoradflaggan -C
kan du styra Unicode-funktioner. Det kan följas av en lista med alternativbokstäver.
Standard I / O
-
I
-STDIN
kommer att vara i UTF-8 -
O
-STDOUT
finns i UTF-8 -
E
-STDERR
finns i UTF-8 -
S
- korthet förIOE
kommer standard I / O-strömmar att finnas i UTF-8
echo "Ματαιότης ματαιοτήτων" | perl -CS -Mutf8 -nE 'say "ok" if /Ματαιότης/'
Skriptets argument
-
A
- behandlar@ARGV
som en matris med UTF-8- kodade strängar
perl -CA -Mutf8 -E 'my $arg = shift; say "anteater" if $arg eq "муравьед"' муравьед
Standard PerlIO-lager
-
i
- UTF-8 är standard PerlIO-lagret för ingångsströmmar -
o
- UTF-8 är standard PerlIO-lagret för utgångsströmmar -
D
- korthet förio
perl -CD -Mutf8 -e 'open my $fh, ">", "utf8.txt" or die $!; print $fh "개미 조심해"'
-M
och -C
omkopplare kan kombineras:
perl -CASD -Mutf8 -E 'say "Ματαιότης ματαιοτήτων\n"';
Standard I / O
Kodningen som ska användas för standard I / O-filhandtag ( STDIN
, STDOUT
och STDERR
) kan ställas in separat för varje handtag med binmode
:
binmode STDIN, ':encoding(utf-8)';
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';
Obs: när man läser skulle man i allmänhet föredra :encoding(utf-8)
framför :utf8
, se Anmärkningar för mer information.
Alternativt kan du använda det open
pragmet.
# 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'
Alternativt, för att ställa in alla filhandtag (både de som ännu inte har öppnats och även standard) för att använda :encoding(utf-8)
:
use open qw( :encoding(utf-8) :std );
Filhandtag
Ställer in kodning med öppen ()
När du öppnar en textfil kan du ange att den kodas uttryckligen med ett tre-argument open()
. Denna en- / avkodare som är ansluten till ett filhandtag kallas ett "I / O-lager":
my $filename = '/path/to/file';
open my $fh, '<:encoding(utf-8)', $filename or die "Failed to open $filename: $!";
Se Anmärkningar för en diskussion om skillnaderna mellan :utf8
och :encoding(utf-8)
.
Ställer in kodning med binmode ()
Alternativt kan du använda binmode () för att ställa in kodningen för enskilt filhandtag:
my $filename = '/path/to/file';
open my $fh, '<', $filename or die "Failed to open $filename: $!";
binmode $fh, ':encoding(utf-8)';
öppen pragma
För att undvika att ställa in kodning för varje filhandtag separat, kan du använda det open
pragmat för att ställa in ett standard I / O-lager som används av alla efterföljande samtal till den open()
-funktionen och liknande operatörer inom det leksikala räckvidden för detta pragma:
# 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)';
Ställer in kodning med kommandorad -C-flagga
Slutligen är det också möjligt att köra perl-tolk med en -CD
flagga som använder UTF-8 som standard I / O-lager. Detta alternativ bör dock undvikas eftersom det förlitar sig på specifikt användarbeteende som inte kan förutsägas eller kontrolleras.
Pragmaet utf8: använder Unicode i dina källor
utf8
utf8 indikerar att källkoden tolkas som UTF-8. Naturligtvis fungerar detta bara om din texteditor också sparar källan som UTF-8 kodad.
Nu kan strängbokstäver innehålla godtyckliga Unicode-tecken; identifierare kan också innehålla Unicode men endast ordliknande tecken (se perldata och perlrecharclass för mer information):
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")
Obs! När du skriver ut text till terminalen, se till att den stöder UTF-8. *
Det kan finnas komplexa och motintuitiva förhållanden mellan utgångs- och källkodning. Om du kör på en UTF-8-terminal kan du komma att lägga till utf8
pragma verkar bryta saker:
$ 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
I det första fallet behandlar Perl strängen som råbyte och skriver ut dem så. Eftersom dessa byte råkar vara giltiga UTF-8 ser de korrekta ut trots att Perl inte riktigt vet vilka karaktärer de är (t.ex. length("Møøse")
kommer att returnera 7, inte 5). När du lägger till -Mutf8
avkodar Perl korrekt UTF-8-källan till tecken, men utmatningen är som standard i Latin-1-läge och att skriva ut Latin-1 till en UTF-8-terminal fungerar inte. Endast när du byter STDOUT
till UTF-8 med -CO
kommer utgången att vara korrekt.
use utf8
påverkar inte standard I / O-kodning eller filhandtag!
Hantering av ogiltig UTF-8
Läser ogiltig UTF-8
När du läser UTF-8-kodade data är det viktigt att vara medveten om att UTF-8-kodade data kan vara ogiltiga eller felformade. Sådana uppgifter ska vanligtvis inte accepteras av ditt program (såvida du inte vet vad du gör). När man oväntat stöter på missbildad data kan olika åtgärder övervägas:
- Skriv ut stacktrace eller felmeddelande, och avbryt programmet graciöst, eller
- Sätt in ett substitutionstecken på den plats där den missbildade bytesekvensen dök upp, skriv ut ett varningsmeddelande till STDERR och fortsätt läsa eftersom ingenting hände.
Som standard warn
Perl dig för att koda glitches, men det avbryter inte ditt program. Du kan göra ditt program avbrutet genom att göra varningar från UTF-8 dödliga, men var medvetna om varningar i Fatal Warnings .
Följande exempel skriver 3 byte i kodning av ISO 8859-1 till disk. Den försöker sedan läsa tillbaka byte igen som UTF-8-kodad data. En av byte, 0xE5
, är en ogiltig UTF-8-bytesekvens:
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";
Programmet avbryts med en dödlig varning:
utf8 "\xE5" does not map to Unicode at ./test.pl line 10.
Linje 10 är här den näst sista raden och felet uppstår i delen av raden med <$fh>
när du försöker läsa en rad från filen.
Om du inte gör varningar dödliga i ovanstående program kommer Perl fortfarande att skriva ut varningen. I det här fallet kommer det dock att försöka återhämta sig från den missbildade byten 0xE5
genom att sätta in de fyra tecknen \xE5
i strömmen och sedan fortsätta med nästa byte. Som ett resultat kommer programmet att skriva ut:
Read string: 'a\xE5a'