43 votes

Comment lire UTF-8 avec l'opérateur diamant (<>)?

Je veux lire UTF-8 entrée en Perl, peu importe si il s'agit de l'entrée standard ou à partir d'un fichier, à l'aide de l'opérateur diamant: while(<>){...}.

Donc mon script doit être appelée dans ces deux voies, comme d'habitude, en donnant le même résultat:

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

Mais les sorties diffèrent! Seulement le deuxième appel (à l'aide d' cat) semble fonctionner comme prévu, la lecture correctement l'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 faire lire UTF-8 correctement dans les deux cas? Je voudrais continuer à utiliser le diamant de l'opérateur <> pour la lecture, si possible.

EDIT:

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 correctement les résultats:

a
\xCA\xA7
b

Mais l'autre méthode me donne ceci:

a
\xC3\x8A
\xC2\xA7
b

59voto

potyl Points 1112

Essayez d'utiliser le pragma s'ouvre à la place:

use strict;
use warnings;
use open qw(:std :utf8);

while(<>){
    my @chars = split //, $_;
    print "$_" foreach(@chars);
}

Vous avez besoin de faire cela parce que le <> opérateur est magique. Comme vous le savez, il va lire depuis l'entrée standard ou à partir des fichiers de @ARGV. La lecture de l'entrée standard STDIN ne pose pas de problème comme STDIN est déjà ouvert donc binmode fonctionne bien sur il. Le problème est lors de la lecture à partir des fichiers de @ARGV, lorsque votre script démarre et appels binmode les fichiers ne sont pas ouverts. Cela provoque STDIN pour être mis en UTF-8, mais ce IO canal n'est pas utilisé lors de l' @ARGV a fichiers. Dans ce cas, le <> opérateur ouvre un nouveau descripteur de fichier pour chaque fichier @ARGV. Chaque descripteur de fichier est remis et perd c'est de l'UTF-8 attribut. En utilisant le pragma vous ouvrir de force chaque nouvelle STDIN pour être en UTF-8.

17voto

jrockway Points 23734

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 descripteur de fichier magique que <> lit s'appelle *ARGV et il est ouvert lorsque vous appelez readline.

Mais vraiment, je suis fan d’utiliser explicitement Encode::decode et Encode::encode lorsque cela est approprié.

9voto

Bruno De Fraine Points 11478

Vous pouvez basculer sur l'UTF8 par défaut avec l' -C drapeau:

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

Le commutateur -CSD s'allume UTF8 sans condition; si vous utilisez simplement -C il va tourner sur UTF8 que si les variables d'environnement (LC_ALL, LC_TYPE et LANG) de l'indiquer. Voir perlrun pour plus de détails.

Ce n'est pas recommandé si vous n'avez pas invoquer perl directement (en particulier, il pourrait ne pas fonctionner de manière fiable si vous passer des options à perl à partir de la ligne shebang). Voir les autres réponses dans ce cas.

4voto

Ben Goldberg Points 31

Si vous mettez un appel à binmode à l'intérieur de la boucle while, alors il va changer le descripteur de l'utf8 mode APRÈS la première ligne est lu. 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 à la fonction eof() avec parens est magique, comme il vérifie à la fin du fichier sur le pseudo-descripteur de fichier utilisé par <>. Il sera, si nécessaire, ouvrez la poignée suivante qui doit être lu, ce qui a généralement pour effet de rendre *ARGV valide, mais sans lire quoi que ce soit. Cela nous permet de binmode le premier fichier qui est lu à partir, avant que rien ne soit lu.

Plus tard, eof (sans les parenthèses) est utilisé; ceci permet de vérifier la dernière manche qui a été lu à partir de la fin du fichier. Elle sera fidèle après, nous avons procédé de la dernière ligne de chaque fichier à partir de la ligne de commande (ou lors de l'entrée standard stdin atteint sa fin).

Évidemment, si nous avons juste transformé la dernière ligne d'un fichier, l'appel de la fonction eof() (avec les parenthèses) ouvre le fichier suivant (si il y en a un), rend *ARGV valide (si elle le peut), et les tests de fin de fichier sur le fichier suivant. Si le prochain fichier est présent, et n'est pas à la fin du fichier, puis nous pouvons utiliser binmode sur ARGV.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X