当我尝试在regex测试器中使用下面的简单替换时,它工作正常。但是当我在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 :(得分: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
)
始终解码您的输入。始终对输出进行编码。
我们已经解码了一个输入(程序本身)。现在我们需要对文件的内容做同样的事情。
同样,我们需要对输出进行编码。这不仅包括文件的内容,还包括输出到STDERR的警告。
大部分是由
完成的use open ':std', ':encoding(UTF-8)';
它向STDIN,STDOUT和STDERR添加编码层,并为在pragma的词法范围内打开的文件设置默认编码层。
#!/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 $_);
}
如果你使用File :: Slurp,你需要告诉它解码文件(或自己解码),因为它的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);
答案 1 :(得分: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);
答案 2 :(得分:0)
我怀疑这里有几个问题。首先,您使用的是File :: Slurp,并没有告诉它数据是UTF编码的。这意味着您的双字节“ù”字符将被解释为两个单字节字符。其次,你的代码中有一个文字“ù”,但是你没有告诉Perl将你的源代码解释为UTF8,所以你可能会有单字节的ISO-8859表示。
输入字符串中的两个单字节字符与源代码中的单字节字符不匹配,因此替换不起作用。
你需要a)告诉Perl您的源代码是UTF8,b)正确处理输入和输出编码的解码。我建议抛出File :: Slurp并自己动手。
我还建议不要啜饮文件,但应尽可能一次处理它们。
#!/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];
}
如果你将它添加到你的代码并传递它'ù' - 你得到输出“249”(这是ISO-8859-1中'ù'的代码点)。如果您将$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中'ù'的双字节表示。