標準入力からのものかファイルからのものかに関係なく、ダイヤモンド演算子while(<>){...}
を使用して、PerlでUTF-8入力を読み取りたい。
したがって、私のスクリプトは、通常どおり、次の2つの方法で呼び出し可能であり、同じ出力が得られます。
./script.pl utf8.txt
cat utf8.txt | ./script.pl
しかし、出力は異なります! 2番目の呼び出し(cat
を使用)のみが設計どおりに機能し、UTF-8を正しく読み取っているようです。スクリプトは次のとおりです。
#!/usr/bin/Perl -w
binmode STDIN, ':utf8';
binmode STDOUT, ':utf8';
while(<>){
my @chars = split //, $_;
print "$_\n" foreach(@chars);
}
どちらの場合もUTF-8を正しく読み取るにはどうすればよいですか?ダイヤモンド演算子を使い続けたい<>
可能であれば、読むために。
編集:
私はおそらくさまざまな出力を説明する必要があることに気づきました。私の入力ファイルには次のシーケンスが含まれています:a\xCA\xA7b
。 cat
を使用するメソッドは、次を正しく出力します。
a
\xCA\xA7
b
しかし、他の方法は私にこれを与えます:
a
\xC3\x8A
\xC2\xA7
b
代わりに、プラグマopenを使用してみてください。
use strict;
use warnings;
use open qw(:std :utf8);
while(<>){
my @chars = split //, $_;
print "$_" foreach(@chars);
}
<>演算子は魔法なので、これを行う必要があります。ご存知のとおり、STDINまたは@ARGVのファイルから読み取られます。 STDINはすでに開いているため、STDINからの読み取りは問題なく、binmodeは問題なく機能します。問題は、@ ARGV内のファイルから読み取るときに、スクリプトが開始してbinmodeを呼び出すときに、ファイルが開かれていないことです。これにより、STDINがUTF-8に設定されますが、@ ARGVにファイルがある場合、このIOチャネルは使用されません。この場合、<>演算子は、@内の各ファイルの新しいファイルハンドルを開きます。 ARGV。各ファイルハンドルがリセットされ、UTF-8属性が失われます。プラグマopenを使用すると、新しいSTDINをUTF-8にする必要があります。
これを行うと、スクリプトが機能します。
#!/usr/bin/Perl -w
binmode STDOUT, ':utf8';
while(<>){
binmode ARGV, ':utf8';
my @chars = split //, $_;
print "$_\n" foreach(@chars);
}
<>が読み取る魔法のファイルハンドルは*ARGV
と呼ばれ、readlineを呼び出すと開かれます。
しかし、実際には、必要に応じてEncode::decode
とEncode::encode
を明示的に使用するのが好きです。
-C
フラグを使用してデフォルトでUTF8をオンに切り替えることができます。
Perl -CSD -ne 'print join("\n",split //);' utf8.txt
スイッチ-CSD
はUTF8を無条件にオンにします。単に-C
を使用すると、関連する環境変数(LC_ALL
、LC_TYPE
、およびLANG
)がそのように示している場合にのみUTF8がオンになります。詳細については、 perlrun を参照してください。
Perlを直接呼び出さない場合、これは推奨されません(特に、Shebang行からPerlにオプションを渡すと確実に機能しない可能性があります)。その場合は他の回答を参照してください。
Whileループ内でbinmodeを呼び出すと、最初の行が読み込まれた後、ハンドルがutf8モードに切り替わります。これはおそらくあなたがしたいことではありません。
次のようなものがうまくいくかもしれません:
#!/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();
}
<>で使用される疑似ファイルハンドルでファイルの終わりをチェックするため、parensを使用したeof()の呼び出しは魔法のようです。必要に応じて、読み取る必要のある次のハンドルを開きます。これは通常、* ARGVを有効にする効果がありますが、そこから何も読み取ることはありません。これにより、何かが読み取られる前に、最初に読み取られたファイルをbinmodeすることができます。
後で、eof(親なし)が使用されます。これにより、最後に読み取られたハンドルのファイルの終わりがチェックされます。コマンドラインから各ファイルの最後の行を処理した後(またはstdinが最後に到達したとき)にtrueになります。
明らかに、1つのファイルの最後の行を処理したばかりの場合、eof()を(parensを使用して)呼び出すと、次のファイルが開き(存在する場合)、* ARGVが有効になり(可能な場合)、ファイルの終わりをテストします。その次のファイルに。その次のファイルが存在し、ファイルの終わりにない場合は、ARGVでbinmodeを安全に使用できます。