Perl:tr/// 没有按照我的预期进行,而 s/// 是
Perl: tr/// is not doing what I expect whereas s/// is
我想删除某些字符串中的变音符号。 tr///
应该可以完成工作但失败了(见下文)。我以为我遇到了 encoding/decoding 问题,但我注意到 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 桌面环境。
这符合我的预期:
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。
如果您没有 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///
,因为 none 个字符是正则表达式运算符,您只是在进行子字符串搜索。您正在搜索多字符子字符串。你找到了,因为在你的 s///
中发生的同样的事情也在你的字符串文字中发生:你认为在那里的字符实际上并不存在,但是多字符序列 是.
另一方面,在 tr///
中,多个字符不被视为一个序列,而是被视为一个集合。每个字符(字节)在找到时单独处理。这不会让您得到想要的结果,因为更改 utf8 字符串的各个字节永远不是您想要的。
事实上,您可以 运行 简单的面向 ASCII 的子字符串搜索,对 utf8 一无所知,并在 utf8 字符串上获得正确的结果,这被认为是 utf8 的一个良好的向后兼容功能,相反其他编码如 ucs2/utf16 或 ucs4.
解决方案是通过添加 use utf8;
告诉 perl 源是使用 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";
我想删除某些字符串中的变音符号。 tr///
应该可以完成工作但失败了(见下文)。我以为我遇到了 encoding/decoding 问题,但我注意到 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 桌面环境。
这符合我的预期:
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。
如果您没有 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///
,因为 none 个字符是正则表达式运算符,您只是在进行子字符串搜索。您正在搜索多字符子字符串。你找到了,因为在你的 s///
中发生的同样的事情也在你的字符串文字中发生:你认为在那里的字符实际上并不存在,但是多字符序列 是.
另一方面,在 tr///
中,多个字符不被视为一个序列,而是被视为一个集合。每个字符(字节)在找到时单独处理。这不会让您得到想要的结果,因为更改 utf8 字符串的各个字节永远不是您想要的。
事实上,您可以 运行 简单的面向 ASCII 的子字符串搜索,对 utf8 一无所知,并在 utf8 字符串上获得正确的结果,这被认为是 utf8 的一个良好的向后兼容功能,相反其他编码如 ucs2/utf16 或 ucs4.
解决方案是通过添加 use utf8;
告诉 perl 源是使用 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";