Perl:tr ///没有做我期望的事情,而s ///是

时间:2016-10-23 15:11:37

标签: perl tr

我想在某些字符串中删除变音符号。 tr///应该完成这项工作但却失败了(见下文)。我以为我有编码/解码问题,但我注意到s///正如我所期望的那样工作。有人可以解释一下原因吗?

以下是我得到的结果示例:

my $str1 = 'èîü';
my $str2 = $str1;
$str1 =~ tr/î/i/;
print "$str1\n"; # => i�iii�
$str2 =~ s/î/i/;
print "$str2\n"; # => èiü

请注意tr///还修改了字符串的第一个和第三个字符,而不仅仅是中间字符。

编辑:我将Ubuntu 16.04与Mate桌面环境结合使用。

2 个答案:

答案 0 :(得分:18)

当你没有use utf8;,但是你正在使用utf8文本编辑器查看代码时,你并没有像perl看到的那样看到它。您认为s///tr///的左半部分只有一个字符,但由于它是多个字节,因此perl将其视为多个字符。

你认为perl看到了什么:

my $str1 = "\xE8\xEE\xFC";
my $str2 = $str1;
$str1 =~ tr/\xEE/i/;
print "$str1\n";
$str2 =~ s/\xEE/i/;
print "$str2\n";

perl实际上看到了什么:

my $str1 = "\xC3\xA8\xC3\xAE\xC3\xBC";
my $str2 = $str1;
$str1 =~ tr/\xC3\xAE/i/;
print "$str1\n";
$str2 =~ s/\xC3\xAE/i/;
print "$str2\n";

使用s///,因为所有字符都不是正则表达式运算符,所以您只是进行子字符串搜索。您正在搜索多字符子字符串。并且你找到它,因为你的s///中发生的同样的事情也发生在你的字符串文字中:你认为那里的字符确实不存在,但是多字符序列

另一方面,在tr///中,多个字符不被视为序列,它们被视为一组。每个字符(字节)在找到时单独处理。这并不能得到你想要的结果,因为改变utf8字符串的单个字节绝不是你想要的。

你可以运行简单的面向ASCII的子字符串搜索,它对utf8一无所知,并在utf8字符串上得到正确的结果,这被认为是utf8的一个很好的向后兼容功能,而不是像其他编码如ucs2 / utf16或ucs4。

解决方案是告诉perl通过添加use utf8;使用UTF-8对源进行编码。您还需要对输出进行编码以匹配终端所期望的输出。

use utf8;                             # The source is encoded using UTF-8.
use open ':std', ':encoding(UTF-8)';  # The terminal provides/expects UTF-8.
my $str1 = 'èîü';
my $str2 = $str1;
$str1 =~ tr/î/i/;
print "$str1\n";
$str2 =~ s/î/i/;
print "$str2\n";

答案 1 :(得分:3)

这对我有用:

use v5.10;
use utf8;
use open qw/:std :utf8/;

my $str1 = 'èîü';
my $str2 = $str1;
$str1 =~ tr/î/i/;
say $str1; # èiü
$str2 =~ s/î/i/;
say $str2; # èiü

use utf8 pragma在源代码中为文字提供UTF-8,use open pragma将STDOUT切换为UTF-8。