2009-02-06 4 views
39

다이아몬드 입력 연산자 while(<>){...}을 사용하여 펄에서 UTF-8 입력을 읽으 려합니다. 표준 입력 또는 파일에서 입력 한 것이 든 상관 없습니다.다이아몬드 연산자 (<>)로 UTF-8을 읽으려면 어떻게해야합니까?

그래서 내 스크립트는 동일한 출력을 제공, 평소와 같이,이 두 가지 방법으로 호출해야한다 :

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

그러나 출력은 다르다! 두 번째 호출 (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.

a 
\xCA\xA7 
b 

을하지만 다른 방법은 저에게이 제공 : cat와 방법은 제대로 출력하는 대신에 열려있는 프라그를 사용하는

a 
\xC3\x8A 
\xC2\xA7 
b 

답변

54

시도를 :

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 속성을 잃습니다. pragma open을 사용하면 새로운 각 STDIN을 UTF-8로 강제 설정합니다. 당신이 할 경우

16

스크립트가 작동합니다에서 *ARGV라고 읽습니다

#!/usr/bin/perl -w 

binmode STDOUT, ':utf8'; 

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

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

마법의 핸들> < 있음을, 그리고 당신이 작성한 Readline를 호출 할 때 열입니다.

하지만 실제로, 나는 적절하게 Encode::decodeEncode::encode을 명시 적으로 사용하는 팬입니다. 당신은 -C 플래그 기본적으로 UTF8에 전환 할 수 있습니다

+0

ARGV가 여러 파일에 대해 재설정되기 때문에 잠시 동안 binmode가 있어야합니까? –

+1

실험적으로, 예 :) – jrockway

+2

나는 이것을보고 생각했다. "그건 작동하지 않을 것이다! 첫 행이 이미'<>'"에서 읽힌 후에'binmode'를 설정하고있다. 그러나, 나는 그것을 시도하고 * 않습니다 * 작동합니다. 매우 마술 적입니다. – mavit

9

:

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

-CSD 무조건 UTF8 점등 스위치; 단순히 -C 만 사용하면 관련 환경 변수 (LC_ALL, LC_TYPELANG)가 나타내는 경우에만 UTF8을 켭니다. 자세한 내용은 perlrun을 참조하십시오.

perl을 직접 호출하지 않는 경우에는 권장되지 않습니다 (특히, shebang 행에서 perl에 옵션을 전달할 경우 안정적으로 작동하지 않을 수 있음).이 경우 다른 답변을 참조하십시오.

+0

perl 5.10 이후 -C 스위치에 문제가 있습니다. http://www.fi.muni.cz/~kas/blog/index.cgi/computers/too-late-for-cs-howto.html –

+0

Off topic : Using '#!/usr/bin/perl'은 추천 라인이 아닙니다. 자세한 내용은 perlrun을 참조하십시오. #!/usr/bin/perl보다 더 이식성이 좋은 #!/usr/bin/env perl을 사용하는 perlrun 접근법을 사용하지 않으려한다면 –

+0

고마워, 필자가 perl을 직접 호출 할 때만 사용해야한다고 분명히했다. –

4

while 루프 안에 binmod를 호출하면 첫 번째 행을 읽은 후 핸들을 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를 유효하게 만드는 효과가 있지만 그 중 아무 것도 읽지 않습니다. 이렇게하면 읽은 첫 번째 파일을 무엇이든 읽기 전에 binmode 할 수 있습니다.

나중에, eof (괄호 안 함)가 사용됩니다. 이것은 파일 끝으로 읽은 마지막 핸들을 검사합니다. 명령 줄에서 각 파일의 마지막 줄을 처리 한 후에 (또는 stdin이 끝날 때까지) 사실입니다.

분명히 한 파일의 마지막 줄을 처리했다면 eof() (괄호 포함)를 호출하면 다음 파일 (있는 경우)을 열고 * ARGV를 유효하게 만들고 (가능한 경우) 그 다음 파일의 파일 끝. 그 다음 파일이 존재하고 파일의 끝에 있지 않으면 우리는 안전하게 ARGV에서 binmode를 사용할 수 있습니다.