Comment lire UTF-8 avec un opérateur diamant ()?

Je veux lire l’entrée UTF-8 dans Perl, peu importe si elle provient de l’entrée standard ou d’un fichier, en utilisant l’opérateur diamant: while(){...} .

Donc, mon script devrait pouvoir être appelé de ces deux manières, comme d’habitude, en donnant le même résultat:

 ./script.pl utf8.txt cat utf8.txt | ./script.pl 

Mais les résultats diffèrent! Seul le second appel (avec un cat ) semble fonctionner comme prévu, en lisant correctement UTF-8. Voici le script:

 #!/usr/bin/perl -w binmode STDIN, ':utf8'; binmode STDOUT, ':utf8'; while(){ my @chars = split //, $_; print "$_\n" foreach(@chars); } 

Comment puis-je le faire lire correctement UTF-8 dans les deux cas? Je voudrais continuer à utiliser l’opérateur de diamant pour lire, si possible.

MODIFIER:

J’ai réalisé que je devrais probablement décrire les différentes sorties. Mon fichier d’entrée contient cette séquence: a\xCA\xA7b . La méthode avec cat génère correctement:

 a \xCA\xA7 b 

Mais l’autre méthode me donne ceci:

 a \xC3\x8A \xC2\xA7 b 

Essayez d’utiliser le pragma ouvert à la place:

 use ssortingct; use warnings; use open qw(:std :utf8); while(<>){ my @chars = split //, $_; print "$_" foreach(@chars); } 

Vous devez le faire car l’opérateur <> est magique. Comme vous le savez, il sera lu depuis STDIN ou depuis les fichiers de @ARGV. La lecture de STDIN ne pose aucun problème car STDIN est déjà ouvert, donc binmode fonctionne correctement. Le problème est lors de la lecture des fichiers dans @ARGV, lorsque votre script démarre et appelle binmode, les fichiers ne sont pas ouverts. Cela entraîne la définition de STDIN sur UTF-8, mais ce canal IO n’est pas utilisé lorsque @ARGV contient des fichiers. Dans ce cas, l’opérateur <> ouvre un nouveau descripteur de fichier pour chaque fichier dans @ARGV. Chaque descripteur de fichier est réinitialisé et perd son atsortingbut UTF-8. En utilisant le pragma ouvert, vous forcez chaque nouveau STDIN à être en UTF-8.

Votre script fonctionne si vous faites ceci:

 #!/usr/bin/perl -w binmode STDOUT, ':utf8'; while(<>){ binmode ARGV, ':utf8'; my @chars = split //, $_; print "$_\n" foreach(@chars); } 

Le *ARGV magique que <> lit à partir de s’appelle *ARGV , et il est ouvert lorsque vous appelez readline.

Mais en réalité, je suis fan d’utiliser explicitement Encode::decode et Encode::encode cas échéant.

Vous pouvez activer UTF8 par défaut avec le drapeau -C :

 perl -CSD -ne 'print join("\n",split //);' utf8.txt 

Le commutateur -CSD active UTF8 sans condition; Si vous utilisez simplement -C il LC_TYPE UTF8 uniquement si les variables d’environnement pertinentes ( LC_ALL , LC_TYPE et LANG ) l’indiquent. Voir perlrun pour plus de détails.

Ceci n’est pas recommandé si vous n’invoquez pas directement perl (en particulier, cela peut ne pas fonctionner correctement si vous passez des options à perl à partir de la ligne shebang). Voir les autres réponses dans ce cas.

Si vous appelez binmode dans la boucle while, le handle passera en mode utf8 APRÈS que la première ligne soit lue. Ce n’est probablement pas ce que vous voulez faire.

Quelque chose comme ce qui suit pourrait mieux fonctionner:

 #!/usr/bin/env perl -w binmode STDOUT, ':utf8'; eof() ? exit : binmode ARGV, ':utf8'; while( <> ) { my @chars = split //, $_; print "$_\n" foreach(@chars); } continue { binmode ARGV, ':utf8' if eof && !eof(); } 

L’appel à eof () avec parens est magique, car il vérifie la fin du fichier sur le pseudo-descripteur de fichier utilisé par <>. Si nécessaire, il ouvrira le descripteur suivant qui doit être lu, ce qui a pour effet de rendre * ARGV valide, mais sans rien lire. Cela nous permet de binmoder le premier fichier lu, avant que tout ne soit lu.

Plus tard, eof (sans parens) est utilisé; Cela vérifie le dernier handle lu depuis la fin du fichier. Ce sera vrai après avoir traité la dernière ligne de chaque fichier à partir de la ligne de commande (ou lorsque stdin aura atteint sa fin).

Évidemment, si nous venons de traiter la dernière ligne d’un fichier, l’appel à eof () (avec parens) ouvre le fichier suivant (s’il y en a un), rend * ARGV valide (si possible), et teste la fin du fichier. sur ce fichier suivant. Si ce fichier suivant est présent et n’est pas en fin de fichier, nous pouvons utiliser binmode en toute sécurité sur ARGV.