Zoeken…


Opmerkingen

Een waarschuwing voor bestandsnaamcodering


Het is vermeldenswaard dat bestandsnaamcodering niet alleen platformspecifiek is, maar ook bestandssysteemspecifiek .

Het is nooit helemaal veilig om aan te nemen (maar is dat meestal ook) dat alleen omdat je kunt coderen en naar een bepaalde bestandsnaam kunt schrijven, dat wanneer je later probeert dezelfde bestandsnaam te openen om te lezen, het nog steeds hetzelfde wordt genoemd.

Als u bijvoorbeeld naar een bestandssysteem zoals FAT16 schrijft dat geen unicode ondersteunt, worden uw bestandsnamen mogelijk geruisloos vertaald in ASCII-compatibele formulieren.

Maar het is zelfs minder veilig om aan te nemen dat een bestand dat je kunt maken, lezen en schrijven door expliciete naamgeving hetzelfde wordt genoemd wanneer je het via andere aanroepen readdir , bijvoorbeeld readdir kan verschillende bytes voor je bestandsnaam retourneren dan je hebt opgegeven om te open .

Op sommige systemen, zoals VAX, kun je niet eens altijd van uitgaan dat readdir dezelfde naam die u hebt opgegeven met keert terug open voor bestandsnamen zo simpel als foo.bar , omdat bestandsextensies kunnen worden gemangeld door het besturingssysteem.

Ook is er op UNIX een zeer liberale set legale tekens voor bestandsnamen die het besturingssysteem toestaat, met uitzondering van / en \0 , terwijl er zoals bij Windows specifieke reeksen tekens zijn verboden in bestandsnamen en fouten veroorzaken.

Oefening veel voorzichtigheid hier, vermijd buitensporige trucs met bestandsnamen als je een keuze hebt, en hebben altijd tests om ervoor te zorgen dat elke zin trucs die u doen gebruik consistent zijn.

Wees dubbel zo voorzichtig als u code schrijft die bedoeld is om te worden uitgevoerd op platforms buiten uw controle, zoals als u code schrijft die is bedoeld voor CPAN en ervan uitgaat dat ten minste 5% van uw gebruikersbestand vastloopt met oude of gebroken technologie, hetzij door keuze, per ongeluk, of door krachten buiten hun controle, en dat deze zullen samenspannen om bugs voor hen te creëren.

: encoding (utf8) vs: utf8


Omdat UTF-8 een van de interne indelingen is voor de weergave van tekenreeksen in Perl, kan de coderings- / decoderingsstap vaak worden overgeslagen. In plaats van :encoding(utf-8) , kunt u eenvoudig gebruiken :utf8 , als uw gegevens zich al in UTF-8 bevinden. :utf8 kan veilig worden gebruikt met uitvoerstromen, terwijl het voor invoerstromen gevaarlijk kan zijn, omdat het interne inconsistentie veroorzaakt wanneer u ongeldige byte-reeksen hebt. Het gebruik van :utf8 voor invoer kan ook leiden tot inbreuken op de beveiliging, dus het gebruik van :encoding(utf-8) is aan te raden.

Meer details: Wat is het verschil tussen: codering en: utf8

UTF-8 vs utf8 vs UTF8


Vanaf Perl v5.8.7 "UTF-8" (met streepje) UTF-8 in zijn strikte en veiligheidsbewuste vorm, terwijl "utf8" UTF-8 in zijn liberale en losse vorm betekent.

"utf8" kan bijvoorbeeld worden gebruikt voor codepunten die niet bestaan in Unicode, zoals 0xFFFFFFFF . Dienovereenkomstig zullen ongeldige UTF-8 byte-reeksen zoals "\x{FE}\x{83}\x{BF}\x{BF}\x{BF}\x{BF}\x{BF}" decoderen in een ongeldig Unicode (maar geldig Perl) codepunt ( 0xFFFFFFFF ) bij gebruik van "utf8" , terwijl de "UTF-8" -codering geen codering mogelijk maakt voor codepunten buiten het bereik van geldige Unicode en in plaats daarvan een vervangend teken ( 0xFFFD ) zou geven.

Omdat coderingsnamen niet hoofdlettergevoelig zijn, is "UTF8" hetzelfde als "utf8" (dwz niet-strikte variant).

Meer informatie: UTF-8 versus utf8 versus UTF8

Meer lezen


Details over de Unicode-verwerking van Perl worden in meer detail beschreven in de volgende bronnen:

Berichten van stackoverflow.com (waarschuwing: mogelijk niet actueel):

Youtube filmpjes:

Maak bestandsnamen

In de volgende voorbeelden wordt de UTF-8-codering gebruikt om bestandsnamen (en mapnamen) op schijf weer te geven. Als u een andere codering wilt gebruiken, moet u 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 $@;

Bestandsnamen lezen

Perl probeert geen bestandsnamen te decoderen die worden teruggestuurd door ingebouwde functies of modules. Dergelijke tekenreeksen die bestandsnamen vertegenwoordigen, moeten altijd expliciet worden gedecodeerd, zodat Perl ze als Unicode kan herkennen.

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/;
}

Opmerking: als u zich zorgen maakt over ongeldige UTF-8 in de bestandsnamen, moet het gebruik van decode_utf8( ... ) in de bovenstaande voorbeelden waarschijnlijk worden vervangen door decode( 'utf-8', ... ) . Dit komt omdat decode_utf8( ... ) een synoniem is voor decode( 'utf8', ... ) en er is een verschil tussen de coderingen utf-8 en utf8 (zie Opmerkingen hieronder voor meer informatie) waar utf-8 meer is strikt wat acceptabel is dan utf8 .

Opdrachtregelschakelaars voor one-liners

Schakel utf8 pragma in

Om utf8 pragma in één voering in te schakelen, moet perl-interpreter worden aangeroepen met de optie -Mutf8 :

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

Unicode-verwerking met -C-schakelaar

Met de -C opdrachtregelvlag kunt u Unicode-functies beheren. Dit kan worden gevolgd door een lijst met optieletters.

Standaard I / O

  • I - STDIN zal in UTF-8 zijn
  • O - STDOUT staat in UTF-8
  • E - STDERR staat in UTF-8
  • S - steno voor IOE , standaard I / O-streams zijn in UTF-8
echo "Ματαιότης ματαιοτήτων" | perl -CS -Mutf8 -nE 'say "ok" if /Ματαιότης/'

Argumenten van Script

  • A - behandelt @ARGV als een reeks UTF-8- gecodeerde tekenreeksen
perl -CA -Mutf8 -E 'my $arg = shift; say "anteater" if $arg eq "муравьед"' муравьед

Standaard PerlIO-laag

  • i - UTF-8 is de standaard PerlIO-laag voor invoerstromen
  • o - UTF-8 is de standaard PerlIO-laag voor uitvoerstromen
  • D - steno voor io
perl -CD -Mutf8 -e 'open my $fh, ">", "utf8.txt" or die $!; print $fh "개미 조심해"'

-M en -C schakelaars kunnen worden gecombineerd:

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

Standaard I / O

De codering die moet worden gebruikt voor de standaard I / O-bestandshandles ( STDIN , STDOUT en STDERR ), kan voor elke handle met binmode afzonderlijk worden ingesteld:

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

Opmerking: bij het lezen geeft men in het algemeen de voorkeur aan :encoding(utf-8) boven :utf8 , zie Opmerkingen voor meer informatie.

Als alternatief kunt u het open pragma gebruiken.

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

Als alternatief kunt u alle te gebruiken filehandles (zowel de nog te openen als de standaard) instellen :encoding(utf-8) :

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

Bestandshandvatten

Codering instellen met open ()

Bij het openen van een tekstbestand kunt u de codering expliciet opgeven met een open() argument van drie argumenten open() . Deze en- / decoder gekoppeld aan een bestandsingang wordt een "I / O-laag" genoemd:

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

Zie Opmerkingen voor een bespreking van de verschillen tussen :utf8 en :encoding(utf-8) .

Codering instellen met binmode ()

U kunt ook binmode () gebruiken om de codering in te stellen voor de afzonderlijke bestandsingang:

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

open pragma

Om te voorkomen dat de codering voor elke bestandsingang afzonderlijk wordt ingesteld, kunt u de open pragma gebruiken om een standaard I / O-laag in te stellen die wordt gebruikt door alle volgende aanroepen van de functie open() en vergelijkbare operatoren binnen het lexicale bereik van deze 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)';

Codering instellen met opdrachtregel -C vlag

Ten slotte is het ook mogelijk om de perl-interpreter uit te voeren met een -CD vlag die UTF-8 als standaard I / O-laag toepast. Deze optie moet echter worden vermeden, omdat deze afhankelijk is van specifiek gebruikersgedrag dat niet kan worden voorspeld of gecontroleerd.

Het utf8-pragma: Unicode gebruiken in uw bronnen

Het utf8 pragma geeft aan dat de broncode wordt geïnterpreteerd als UTF-8. Natuurlijk zal dit alleen werken als uw teksteditor de bron ook opslaat als UTF-8-gecodeerd.

Nu kunnen stringliterals willekeurige Unicode-tekens bevatten; ID's kunnen ook Unicode bevatten, maar alleen woordachtige tekens (zie perldata en perlrecharclass voor meer informatie):

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")

Opmerking : Zorg er bij het afdrukken van tekst naar de terminal voor dat deze UTF-8 ondersteunt. *

Er kunnen complexe en contra-intuïtieve relaties zijn tussen uitvoer en broncodering. Als u op een UTF-8-terminal werkt, merkt u misschien dat het toevoegen van de utf8 pragma dingen lijkt te breken:

$ 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

In het eerste geval behandelt Perl de string als onbewerkte bytes en drukt deze zo af. Omdat deze bytes geldig zijn UTF-8, zien ze er correct uit, hoewel Perl niet echt weet welke tekens ze zijn (bijvoorbeeld length("Møøse") retourneert 7, niet 5). Nadat u -Mutf8 hebt toegevoegd, decodeert Perl de bron UTF-8 correct naar tekens, maar de uitvoer bevindt zich standaard in de modus Latin-1 en afdrukken van Latin-1 naar een UTF-8-terminal werkt niet. Alleen wanneer u STDOUT op UTF-8 overschakelt met -CO is de uitvoer correct.

use utf8 heeft geen invloed op standaard I / O-codering noch bestandshandgrepen!

Ongeldige UTF-8 verwerken

Ongeldige UTF-8 lezen

Bij het lezen van UTF-8-gecodeerde gegevens is het belangrijk om te weten dat de UTF-8-gecodeerde gegevens ongeldig of onjuist kunnen zijn. Dergelijke gegevens moeten meestal niet door uw programma worden geaccepteerd (tenzij u weet wat u doet). Wanneer onverwacht misvormde gegevens worden aangetroffen, kunnen verschillende acties worden overwogen:

  • Stacktrace of foutmelding afdrukken en programma netjes afbreken, of
  • Voeg een vervangend teken in op de plaats waar de verkeerd gevormde byte-reeks verscheen, druk een waarschuwingsbericht af naar STDERR en lees verder terwijl er niets gebeurde.

Perl warn u standaard voor het coderen van glitches, maar uw programma wordt niet afgebroken. U kunt uw programma afbreken door UTF-8-waarschuwingen fataal te maken, maar houd rekening met de kanttekeningen bij Fatal Warnings .

Het volgende voorbeeld schrijft 3 bytes bij het coderen van ISO 8859-1 naar schijf. Vervolgens probeert het de bytes weer terug te lezen als UTF-8-gecodeerde gegevens. Een van de bytes, 0xE5 , is een ongeldige UTF-8-byte-reeks:

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";

Het programma wordt afgebroken met een fatale waarschuwing:

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

Regel 10 is hier de voorlaatste regel en de fout treedt op in het deel van de regel met <$fh> wanneer wordt geprobeerd een regel uit het bestand te lezen.

Als u geen waarschuwingen fataal maakt in het bovenstaande programma, zal Perl de waarschuwing nog steeds afdrukken. In dit geval zal het echter proberen te herstellen van de verkeerd 0xE5 byte 0xE5 door de vier tekens \xE5 in de stream in te \xE5 en vervolgens door te gaan met de volgende byte. Als gevolg hiervan zal het programma afdrukken:

Read string: 'a\xE5a'


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow