Szukaj…


Uwagi

Ostrzeżenie o kodowaniu nazwy pliku


Warto wspomnieć, że Kodowanie nazw plików dotyczy nie tylko platformy, ale także systemu plików .

Nigdy nie jest całkowicie bezpieczne zakładanie (ale często jest), że tylko dlatego, że możesz kodować i zapisywać do podanej nazwy pliku, że kiedy później spróbujesz otworzyć tę samą nazwę pliku do odczytu, nadal będzie to nazywało się tak samo.

Na przykład, jeśli piszesz do systemu plików takiego jak FAT16 który nie obsługuje Unicode, twoje nazwy plików mogą po cichu zostać przetłumaczone na formularze kompatybilne z ASCII.

Ale jeszcze mniej bezpieczne jest założenie, że plik, który można utworzyć, odczytać i zapisać przez jawne nazewnictwo, będzie nazywał się tak samo, gdy kwerenda zostanie przeprowadzona za pomocą innych wywołań, na przykład readdir może zwrócić inne bajty dla nazwy pliku, niż podano w celu open .

W niektórych systemach, takich jak VAX, nie zawsze możesz nawet założyć, że readdir zwróci taką samą nazwę pliku, którą podałeś dla open dla nazw plików tak prostych jak foo.bar , ponieważ rozszerzenia plików mogą być zniekształcane przez system operacyjny.

Ponadto w systemie UNIX istnieje bardzo liberalny zestaw znaków prawnych dla nazw plików, na który zezwala system operacyjny, z wyjątkiem tylko / i \0 , gdzie, podobnie jak w systemie Windows, istnieją określone zakresy znaków, które są zabronione w nazwach plików i powodują błędy.

Zachowaj tutaj ostrożność, unikaj fantazyjnych sztuczek z nazwami plików, jeśli masz wybór , i zawsze wykonuj testy, aby upewnić się, że wszystkie fantazyjne sztuczki, których używasz, są spójne.

Zachowaj podwójną ostrożność, jeśli piszesz kod przeznaczony do uruchamiania na platformach poza Twoją kontrolą, na przykład jeśli piszesz kod przeznaczony dla CPAN i zakładasz, że co najmniej 5% twojej bazy użytkowników utknie przy użyciu niektórych starożytna lub zepsuta technologia, albo z wyboru, przez przypadek, albo przez siły poza ich kontrolą, i że spiskują, by stworzyć dla nich błędy.

: kodowanie (utf8) vs: utf8


Ponieważ UTF-8 jest jednym z wewnętrznych formatów reprezentujących ciągi znaków w Perlu, etap kodowania / dekodowania może być często pomijany. Zamiast :encoding(utf-8) , możesz po prostu użyć :utf8 , jeśli twoje dane są już w UTF-8. :utf8 może być bezpiecznie używany ze strumieniami wyjściowymi, podczas gdy dla strumienia wejściowego może być niebezpieczny, ponieważ powoduje wewnętrzną niespójność, gdy masz nieprawidłowe sekwencje bajtów. Ponadto użycie :utf8 jako danych wejściowych może spowodować naruszenie bezpieczeństwa, dlatego zaleca się użycie :encoding(utf-8) .

Więcej informacji: Jaka jest różnica między: kodowaniem a: utf8

UTF-8 vs utf8 vs UTF8


Począwszy od Perla v5.8.7 , "UTF-8" (z myślnikiem) oznacza UTF-8 w jego ścisłej i świadomej bezpieczeństwa formie, podczas gdy "utf8" oznacza UTF-8 w jego liberalnej i luźnej formie.

Na przykład "utf8" może być użyte dla punktów kodowych, które nie istnieją w Unicode, takich jak 0xFFFFFFFF . Odpowiednio, niepoprawne sekwencje bajtów UTF-8, takie jak "\x{FE}\x{83}\x{BF}\x{BF}\x{BF}\x{BF}\x{BF}" dekodują się w niepoprawny kodod Unicode (ale prawidłowy Perl) ( 0xFFFFFFFF ) przy użyciu "utf8" , podczas gdy "UTF-8" nie pozwoliłoby na dekodowanie 0xFFFD poza zakresem prawidłowego Unicode i zamiast tego dałoby ci znak podstawienia ( 0xFFFD ).

Ponieważ w nazwach kodowania nie jest rozróżniana wielkość liter, "UTF8" jest taki sam, jak "utf8" (tzn. Wariant nie ścisły ).

Więcej szczegółów: UTF-8 vs. utf8 vs. UTF8

Więcej lektur


Szczegóły dotyczące obsługi Unicode w Perlu opisano bardziej szczegółowo w następujących źródłach:

Wpisy z stackoverflow.com (zastrzeżenie: może nie być aktualne):

Filmy na youtube:

Utwórz nazwy plików

Poniższe przykłady wykorzystują kodowanie UTF-8 do reprezentowania nazw plików (i nazw katalogów) na dysku. Jeśli chcesz użyć innego kodowania, powinieneś użyć 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 $@;

Czytaj nazwy plików

Perl nie próbuje dekodować nazw plików zwracanych przez wbudowane funkcje lub moduły. Takie ciągi znaków reprezentujące nazwy plików powinny zawsze być jawnie dekodowane, aby Perl rozpoznał je jako 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/;
}

Uwaga: jeśli decode_utf8( ... ) nieprawidłowego UTF-8 w nazwach plików, użycie decode_utf8( ... ) w powyższych przykładach powinno prawdopodobnie zostać zastąpione przez decode( 'utf-8', ... ) . Wynika to z faktu, że decode_utf8( ... ) jest synonimem decode( 'utf8', ... ) i istnieje różnica między kodowaniem utf-8 i utf8 (więcej informacji w uwagach poniżej), gdzie utf-8 jest bardziej ściśle na tym, co jest dopuszczalne niż utf8 .

Przełączniki wiersza poleceń dla jedno-liniowych

Włącz utf8 pragma

Aby włączyć utf8 w jednym -Mutf8 , interpreter perla powinien być wywoływany z opcją -Mutf8 :

perl -Mutf8 -E 'my $人 = "human"; say $人'

Obsługa Unicode z przełącznikiem -C

Flaga wiersza poleceń -C pozwala kontrolować funkcje Unicode. Po nim może być lista liter opcji.

Standardowe we / wy

  • I - STDIN będzie w UTF-8
  • O - STDOUT będzie w UTF-8
  • E - STDERR będzie w UTF-8
  • S - skrót dla IOE , standardowe strumienie I / O będą w UTF-8
echo "Ματαιότης ματαιοτήτων" | perl -CS -Mutf8 -nE 'say "ok" if /Ματαιότης/'

Argumenty skryptu

  • A - traktuje @ARGV jako tablicę ciągów kodowanych w UTF-8
perl -CA -Mutf8 -E 'my $arg = shift; say "anteater" if $arg eq "муравьед"' муравьед

Domyślna warstwa PerlIO

  • i - UTF-8 jest domyślną warstwą PerlIO dla strumieni wejściowych
  • o - UTF-8 jest domyślną warstwą PerlIO dla strumieni wyjściowych
  • D - skrót od io
perl -CD -Mutf8 -e 'open my $fh, ">", "utf8.txt" or die $!; print $fh "개미 조심해"'

Przełączniki -M i -C można łączyć:

perl -CASD -Mutf8 -E 'say "Ματαιότης ματαιοτήτων\n"';

Standardowe we / wy

Kodowanie używane dla standardowych uchwytów plików I / O ( STDIN , STDOUT i STDERR ), można ustawić osobno dla każdego uchwytu za pomocą binmode :

binmode STDIN, ':encoding(utf-8)';
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';

Uwaga: podczas czytania wolisz :encoding(utf-8) niż :utf8 , zobacz Uwagi, aby uzyskać więcej informacji.

Alternatywnie możesz użyć open pragmy.

# 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'

Alternatywnie, aby ustawić wszystkie uchwyty plików (zarówno te otwarte, jak i standardowe), należy użyć :encoding(utf-8) :

use open qw( :encoding(utf-8) :std );

Uchwyty do plików

Ustawianie kodowania za pomocą open ()

Podczas otwierania pliku tekstowego możesz jawnie określić jego kodowanie za pomocą trzyargumentowej funkcji open() . Ten en- / dekoder podłączony do uchwytu pliku nazywa się „warstwą we / wy”:

my $filename = '/path/to/file';
open my $fh, '<:encoding(utf-8)', $filename or die "Failed to open $filename: $!";

Zobacz Uwagi, aby omówić różnice między :utf8 i :encoding(utf-8) .

Ustawianie kodowania za pomocą binmode ()

Alternatywnie możesz użyć binmode (), aby ustawić kodowanie dla pojedynczego uchwytu pliku:

my $filename = '/path/to/file';
open my $fh, '<', $filename or die "Failed to open $filename: $!";
binmode $fh, ':encoding(utf-8)';

otwarta pragma

Aby uniknąć ustawiania kodowania dla każdego uchwytu pliku osobno, możesz użyć open pragmy, aby ustawić domyślną warstwę we / wy używaną przez wszystkie kolejne wywołania funkcji open() i podobnych operatorów w zakresie leksykalnym tej pragmy:

# 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)';

Ustawienie kodowania z flagą -C linii poleceń

Wreszcie możliwe jest również uruchomienie interpretera perla z flagą -CD która stosuje UTF-8 jako domyślną warstwę we / wy. Tej opcji należy jednak unikać, ponieważ zależy ona od określonego zachowania użytkownika, którego nie można przewidzieć ani kontrolować.

Utf8 pragma: używanie Unicode w swoich źródłach

utf8 wskazuje, że kod źródłowy będzie interpretowany jako UTF-8. Oczywiście zadziała to tylko wtedy, gdy twój edytor tekstowy zapisuje również źródło w formacie UTF-8.

Teraz literały łańcuchowe mogą zawierać dowolne znaki Unicode; identyfikatory mogą również zawierać Unicode, ale tylko znaki podobne do słów (więcej informacji można znaleźć w perldata i 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")

Uwaga : Podczas drukowania tekstu na terminalu upewnij się, że obsługuje on UTF-8. *

Mogą istnieć złożone i sprzeczne z intuicją relacje między kodowaniem wyjściowym a źródłowym. Działając na terminalu UTF-8, może się okazać, że dodanie utf8 wydaje się psować:

$ 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

W pierwszym przypadku Perl traktuje ciąg znaków jako surowe bajty i drukuje je w ten sposób. Ponieważ te bajty okazują się być poprawnymi UTF-8, wyglądają one poprawnie, mimo że Perl tak naprawdę nie wie, jakie to znaki (np. length("Møøse") zwróci 7, a nie 5). Po dodaniu -Mutf8 Perl poprawnie dekoduje źródło UTF-8 na znaki, ale wyjście jest domyślnie w trybie Latin-1 i drukowanie Latin-1 na terminalu UTF-8 nie działa. Dopiero po przełączeniu STDOUT na UTF-8 za pomocą -CO wynik będzie prawidłowy.

use utf8 nie wpływa na standardowe kodowanie I / O ani uchwyty plików!

Obsługa nieprawidłowego UTF-8

Odczyt nieprawidłowego UTF-8

Podczas odczytywania danych zakodowanych w UTF-8 ważne jest, aby zdawać sobie sprawę z faktu, że dane zakodowane w UTF-8 mogą być nieprawidłowe lub zniekształcone. Takie dane zwykle nie powinny być akceptowane przez Twój program (chyba że wiesz, co robisz). W przypadku nieoczekiwanego napotkania zniekształconych danych można rozważyć różne działania:

  • Wydrukuj komunikat śledzenia śladu na stosie lub komunikat o błędzie i zakończ program z wdziękiem, lub
  • Wstaw znak podstawienia w miejscu, w którym pojawiła się zniekształcona sekwencja bajtów, wydrukuj komunikat ostrzegawczy do STDERR i kontynuuj czytanie, ponieważ nic się nie wydarzyło.

Domyślnie Perl warn przed kodowaniem błędów, ale nie przerywa działania programu. Możesz przerwać program, czyniąc ostrzeżenia UTF-8 śmiertelnymi, ale pamiętaj o ostrzeżeniach zawartych w Ostrzeżeniach śmiertelnych .

Poniższy przykład zapisuje 3 bajty w kodowaniu ISO 8859-1 na dysk. Następnie próbuje ponownie odczytać bajty jako dane zakodowane w UTF-8. Jeden z bajtów, 0xE5 , jest nieprawidłową sekwencją jednobajtową 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";

Program przerwie się ze śmiertelnym ostrzeżeniem:

utf8 "\xE5" does not map to Unicode at ./test.pl line 10.

Linia 10 jest tutaj drugą ostatnią linią, a błąd pojawia się w części linii za pomocą <$fh> podczas próby odczytu linii z pliku.

Jeśli nie spowoduje to, że ostrzeżenia będą śmiertelne w powyższym programie, Perl nadal wydrukuje ostrzeżenie. Jednak w tym przypadku spróbuje odzyskać po zdeformowanym bajcie 0xE5 , wstawiając cztery znaki \xE5 do strumienia, a następnie \xE5 do następnego bajtu. W rezultacie program wydrukuje:

Read string: 'a\xE5a'


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow