48

ダイヤモンド演算子を使用して、標準入力からのものかファイルからのものかに関係なく、PerlでUTF-8入力を読み取りたいwhile(<>){...}

したがって、私のスクリプトは、通常どおり、次の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\xA7bcat正しく出力されるメソッド:

a
\xCA\xA7
b

しかし、他の方法は私にこれを与えます:

a
\xC3\x8A
\xC2\xA7
b
4

4 に答える 4

63

代わりに、プラグマ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にする必要があります。

于 2009-02-06T06:52:09.027 に答える
18

これを行うと、スクリプトが機能します。

#!/usr/bin/perl -w

binmode STDOUT, ':utf8';

while(<>){
    binmode ARGV, ':utf8';

    my @chars = split //, $_;
    print "$_\n" foreach(@chars);
}

<> が読み取る魔法のファイルハンドルは と呼ばれ*ARGV、readline を呼び出すと開かれます。

しかし、実際には、適切な場合はEncode::decodeand を明示的に使用するのが好きです。Encode::encode

于 2009-02-06T08:33:17.873 に答える
10

-C次のフラグを使用して、デフォルトで UTF8 をオンにすることができます。

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

このスイッチ-CSDは無条件に UTF8 をオンにします。単純に使用すると、関連する環境変数 ( 、および) が示す-C場合にのみ UTF8 が有効になります。詳細については、 perlrunを参照してください。LC_ALLLC_TYPELANG

これは、perl を直接呼び出さない場合にはお勧めできません (特に、シバン行から perl にオプションを渡すと、確実に動作しない可能性があります)。その場合は他の回答を参照してください。

于 2009-02-06T08:50:27.480 に答える
4

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();
}

<> によって使用される疑似ファイルハンドルでファイルの終わりをチェックするため、括弧を使用した eof() の呼び出しは魔法のようです。必要に応じて、読み取る必要がある次のハンドルを開きます。これにより、通常は *ARGV が有効になりますが、何も読み取られません。これにより、何かを読み取る前に、最初に読み取ったファイルをビンモードにすることができます。

その後、eof (括弧なし) が使用されます。これは、最後に読み取られたハンドルでファイルの終わりをチェックします。コマンドラインから各ファイルの最後の行を処理した後 (または stdin が最後に達したとき) に true になります。

明らかに、1 つのファイルの最後の行を処理したばかりの場合、eof() (括弧付き) を呼び出すと、次のファイルが開かれ (存在する場合)、*ARGV が有効になり (有効な場合)、ファイルの終わりがテストされます。その次のファイルで。その次のファイルが存在し、ファイルの最後にない場合、ARGV で binmode を安全に使用できます。

于 2011-02-02T23:44:18.647 に答える