2017-03-03 72 views
0

当我尝试在正则表达式测试中使用下面的简单替换时,它工作正常。但是,当我在Perl代码中使用它,在ù不被取代:为什么特殊字符“ù”在替换中被替换?

#!/usr/bin/perl 

use 5.010; 
use strict; 
use warnings; 

use File::Slurp; 

my $file = 'test.html'; 
my $str = read_file($file); 

$str =~ s/ù/u/g; 

write_file("out/$file", $str); 

这里是我想换一个示例文本:

ùmbrella ùnder ùùùùù ùtters 

umbrella under uuuuu utters 

任何帮助并建议高度赞赏。

+0

@siam的感谢!我不得不添加“使用utf8;”顶部还有 – Xavia

+2

@siam:你在想Python!这只是Perl中的一个评论。 – Borodin

+4

[档案::嘟嘟地喝坏了,错了。(http://blogs.perl.org/users/leon_timmermans/2015/08/fileslurp-is-broken-and-wrong.html) – ThisSuitIsBlackNot

回答

7

如果您的脚本和输入文件使用相同的编码进行编码,则您的代码将按原样运行。

$ cat>test.html 
ùmbrella ùnder ùùùùù ùtters 

$ perl a.pl 

$ cat out/test.html 
umbrella under uuuuu utters 

你的程序是越野车,虽然。假设我们正在谈论UTF-8。Perl的实际看到

$str =~ s/\xC3\xB9/u/g; 

虽然这并没有那么糟糕,想象一下,如果你有

$str =~ s/[ùú]/u/g; 

的Perl会认为这是

$str =~ s/[\xC3\xB9\xC3\xBA]/u/g; 

这会变成ùC3 B9)为uuéC3 A9)插入u<garbage>

对于Perl来识别程序中的任何非ASCII字符,您必须确保程序文件使用UTF-8进行编码,并且您需要在文件顶部添加use utf8;。随着use utf8;,Perl中看到

$str =~ s/[ùú]/u/g; 

或者说

$str =~ s/[\xF9\xFA]/u/g; # F9 and FA are the Unicode Code Points for ù and ú 

然而,增加use utf8;只是解决方案的一半。我们改变了Perl看到正则表达式的方式,但我们没有改变$str,所以它们不可能再匹配。我们比较ùC3 B9)与ùF9)Unicode代码点编码

始终解码你的投入。始终对输出进行编码。

我们已经解码的一个输入(程序本身)。现在我们需要对文件的内容做同样的事情。

同样,我们需要编码输出。这不仅包括文件的内容,还包括输出到STDERR的警告。

大部分由

use open ':std', ':encoding(UTF-8)'; 

做它增加了一个编码层STDIN,STDOUT和STDERR,并设置编译的词法范围内打开文件的默认编码层。

#!/usr/bin/perl 

use utf8; 
use open ':std', ':encoding(UTF-8)'; 

use strict; 
use warnings; 

my $in_qfn = 'test.html'; 
my $out_qfn = 'out/test.html'; 

# :encoding(UTF-8) is added by "use open". 
open(my $in_fh, '<', $in_qfn) or die("Can't open \"$in_qfn\": $!\n"); 
open(my $out_fh, '>', $out_qfn) or die("Can't create \"$out_qfn\": $!\n"); 

while (<$in_fh>) { 
    s/[ùú]/u/g; 
    print($out_fh $_); 
} 

如果使用文件::嘟嘟地喝,你需要告诉它的文件进行解码(或自己对其进行解码),因为它open不在use open的范围。

#!/usr/bin/perl 

use utf8; 
use open ':std', ':encoding(UTF-8)'; 

use strict; 
use warnings; 

use File::Slurp qw(read_file write_file); 

my $in_qfn = 'test.html'; 
my $out_qfn = 'out/test.html'; 

my $file = read_file($in_qfn, binmode => ':encoding(UTF-8)'); 

$file =~ s/[ùú]/u/g; 

write_file($out_qfn, { binmode => ':encoding(UTF-8)' }, $file); 
0

解决方案:

#!/usr/bin/perl 

use 5.010; 
use strict; 
use utf8; # <-- Added this 
use warnings; 
use File::Slurp; 
my $file = test.html; my $str; 

$str = read_file($file); 
$str =~ s/ù/u/g; 

write_file("out/$file",$str); 
+1

的'使用utf8'编译告诉你_source代码_文件一起保存UTF8编码的Perl。 – simbabque

+1

换句话说,您仍然需要告诉Perl将输出编码为UTF-8。 – ThisSuitIsBlackNot

+1

修改的程序保持输入文件不变。虽然添加'use utf8;'是正确的,但它只是解决方案的一部分。看到我的答案更多。 – ikegami

0

我怀疑有几个问题在这里。首先,你使用的是File :: Slurp,而你没有告诉它数据是UTF编码的。这意味着您的双字节“ù”字符将被解释为两个单字节字符。其次,你的代码中有一个字面的“ù”,但你不会告诉Perl把你的源代码解释为UTF8,所以你可能会有单字节的ISO-8859表示。

输入字符串中的两个单字节字符与源代码中的单字节字符不匹配,因此替换不起作用。

您需要a)告诉Perl您的源代码是UTF8并且b)正确处理输入和输出编码的解码。我建议扔掉File :: Slurp并自己做。

我也建议不要sl files文件,但只要有可能就一次处理它们。

#!/usr/bin/perl 

use 5.010; 
use strict; 
use warnings; 
use utf8; 

my $file = 'test.html'; 
open my $in_fh, '<:utf8', $file or die $!; 
open my $out_fh, '>:utf8', "out/$file" or die $!; 

while (<$in_fh>) { 
    s/ù/u/g; 

    print $out_fh $_; 
} 

更新:这里是一个非常简单的子程序,我用它来获取有关字符串的信息。

sub string_chars { 
    say join ':', map { ord } split //, $_[0]; 
} 

如果您添加到您的代码,并把它传递“U” - 你的输出“​​249”(这是在ISO-8859-1“U”码点)。如果你传递你的$str值,您可以:

'195:185:109:98:114:101:108:108:97:32:195:185:110:100:101:114:32:195:185:195:185:195:185:195:185:195:185:32:195:185:116:116:101:114:115:10' 

重复的“195:185”是UTF8“U”的两个字节表示。

+1

这里有一个更简单的例子:'sub string_chars {说sprintf“%vX”,$ _ [0]}'(奖金,数字以十六进制表示!) – ikegami