【问题标题】:Perl: tr/// is not doing what I expect whereas s/// isPerl: tr/// 没有做我期望的事情,而 s/// 是
【发布时间】:2016-10-25 00:37:42
【问题描述】:

我想删除某些字符串中的变音符号。 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 桌面环境。

【问题讨论】:

    标签: perl tr


    【解决方案1】:

    当您没有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/// 中发生的同样的事情也发生在你的字符串文字中:你认为那里的字符确实没有,但是多字符序列 is .

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

    您可以运行对 utf8 一无所知的简单的面向 ASCII 的子字符串搜索,并在 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";
    

    【讨论】:

      【解决方案2】:

      这对我来说按预期工作:

      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。

      【讨论】:

      • 它也适用于我,谢谢。知道为什么tr 似乎需要这些编译指示,而s 不需要吗?
      • 我只是想说一些关于字符串与字节字符串语义的事情,但请参阅@Wumpus 的回答,我认为它更好地解释了这个问题。
      • @zoul,很高兴你没有这样做;这与两种内部存储格式无关。
      • 我不知道内部存储,但在我看来,这个错误是由于程序员将字符串视为 UTF-8 字符和 Perl 的集合(没有 Unicode pragma ) 将它们视为 ASCII 字符串 - 或字节集合。这就是我所说的字符与字节字符串语义的意思。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多