为什么在我的替换中没有替换特殊字符“ù”?

时间:2017-03-03 16:05:16

标签: regex perl

当我尝试在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

非常感谢任何帮助和建议。

3 个答案:

答案 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

的Unicode代码点进行比较

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

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

同样,我们需要对输出进行编码。这不仅包括文件的内容,还包括输出到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中'ù'的双字节表示。