为什么我不能在perl tr/表达式中使用引号?

uxhixvfz  于 2023-10-24  发布在  Perl
关注(0)|答案(2)|浏览(153)

我想将文档中的 curl 单引号和双引号音译为中性引号。我认为它应该像perl -pe 'tr/“”’/""\047/'一样简单,但这不起作用。例如:

snafu$ echo '“' | perl -pe 'tr/“”’/""\047/'
""'
snafu$ echo '“”’' | perl -pe 'tr/“”’/""\047/'
""'""'""'

注意,一个“在右边变成了完整的字符集。在第二个例子中,它发生了三次。
而且,(对我来说)更不期望的是,即使在这个微不足道的情况下,也会出现三重:

snafu$ echo '“' | perl -pe 'tr/“/"/'
"""

这种行为似乎与我看到的ASCII字符非常不同,如下所示:

snafu$ echo "Larry Wall" | perl -pe 'tr/ay/AY/'
LArrY WAll

我也试过用perl -Mutf8调用,但也没有达到我的预期:

# not triplicated, but also not transliterated
snafu$ echo '“' | perl -Mutf8 -pe 'tr/“”’/""\047/'
“

如何解释tr///的上述行为?

rkttyhzu

rkttyhzu1#

您需要以下内容:

perl -CS -Mutf8 -pe 'tr/“”’/""\047/'

如果没有use utf8;,Perl期望源代码使用ASCII编码。所以你的第一个代码片段不可能包含。由于字符串文字是“8位干净”的,你的第一个代码片段相当于

tr/\xE2\x80\x9C\xE2\x80\x9D\xE2\x80\x99/""'/

这显然是不正确的。要解决这个问题,请像在上一个代码片段中那样添加use utf8;
那么为什么最后一个片段不起作用呢?那是因为它实际上

"\xE2\x80\x9C" =~ tr/\x{201C}\x{201D}\x{2019}/""'/;

这显然也是不正确的。您正在搜索编码的文本(UTF-8字节的字符串)以获得解码的文本(Unicode Code Points的字符串)。您需要解码您的输入,并编码您的输出。然后可以使用use open ":std", ":encoding(UTF-8)";实现,但-CS可以在这里使用。
最后是

echo '“' | perl -pe 'tr/“/"/'

从上面我们知道它相当于以下内容:

"\xE2\x80\x9C" =~ tr/\xE2\x80\x9C/"/;

除非使用/d,否则如果右侧的字符数少于左侧,tr///将重复最后一个字符。

"\xE2\x80\x9C" =~ tr/\xE2\x80\x9C/"""/;

这就解释了"""的输出。

enyaitl3

enyaitl32#

为了解释你的不太复杂的例子

snafu$ echo '“' | perl -pe 'tr/“/"/'
"""

的UTF-8是序列e2 80 9c。因为所有内容都被视为ASCII字符(字节),所以您的翻译命令会将这些字符替换为"。这就是为什么您会得到三个双引号。
在第一个例子中也发生了类似的事情。但是因为搜索字符串有9个ASCII字符,而替换有3个,所以只考虑Map的替换。所有字符的前两个UTF-8字节(“”’)是相同的,所以当被视为ASCII时,它们Map到替换字符串中的前两个字符。的第三个字节Map到替换字符串中的第三个字符。但是其他两个的第三个字节没有Map,而是被替换字符串的最后一个字符替换。如果在替换字符串中添加第四个字符,您可以更清楚地看到这一点。例如,如果输入是“”’,则tr/“”’/""\047z/将输出""'""z""z
你的代码没有错,如果你把脚本写进一个文件,并正确使用utf8binmode,它会像预期的那样工作:

use utf8;
binmode STDIN, ":utf8";
my $s = <STDIN>;
$s =~ tr/“”’/""'/;
print "$s";

输出:""'
因此,您需要从命令行告诉Perl将STDIN视为UTF-8。您可以使用-C1或更常见的选项-CS来执行此操作,该选项将STDINSTDOUTSTDERR视为UTF-8。

`echo '“”’' | perl -Mutf8 -CS -pe 'tr/“”’/""\047/'

相关问题