我想在某些字符串中删除变音符号。 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桌面环境结合使用。
答案 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。