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

Why can't I use quotes in perl tr/// expressions?

提问人:Reece 提问时间:8/24/2023 最后编辑:zdimReece 更新时间:8/24/2023 访问量:77

问:

我想在文档中将卷曲的单引号和双引号音译为中性引号。我以为它应该像 一样简单,但这不起作用。例如: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/// 的上述行为?

Perl 编码 UTF-8

评论


答:

3赞 ikegami 8/24/2023 #1

您需要满足以下条件:

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

如果没有 ,Perl 期望源代码使用 ASCII 进行编码。因此,您的第一个代码段不可能包含 和 。由于字符串文字是“8 位干净的”,因此您的第一个代码段等效于use utf8;

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 码位字符串)。您需要对输入进行解码,并对输出进行编码。然后可以使用 来实现,但可以在这里使用。use open ":std", ":encoding(UTF-8)";-CS


最后,还有

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

综上所述,我们知道它相当于以下内容:

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

除非使用 ,否则如果右边的字符比左边的字符少,则重复最后一个字符。这使得上述等同于/dtr///

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

这解释了输出。"""

2赞 giusti 8/24/2023 #2

解释您不太复杂的示例

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

的 UTF-8 是序列 。由于所有内容都被视为 ASCII 字符(字节),因此您的翻译命令会将其中每个字符替换为 .这就是为什么你得到三个双引号的原因。e2 80 9c"

在你的第一个例子中也发生了类似的事情。但是,由于搜索字符串有 9 个 ASCII 字符,而替换字符串有 3 个字符,因此仅考虑映射的替换。所有字符 () 的前两个 UTF-8 字节是相同的,因此当被视为 ASCII 时,它们会映射到替换字符串中的前两个字符。然后,第三个字节映射到替换字符串中的第三个字符。但是其他两个字节的第三个字节没有映射,并被替换字符串的最后一个字符替换。如果在替换字符串中添加第四个字符,则可以更清楚地看到这一点。例如,如果输入是 ,则输出。“”’tr/“”’/""\047z/""'""z""z“”’

你的代码没有错。如果将脚本写入文件并正确使用 和 ,它将按预期工作:utf8binmode

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

输出:""'

因此,您需要从命令行告诉Perl将其视为UTF-8。您可以使用 或 更常见的选项 来做到这一点,该选项将把 、 和 视为 UTF-8。STDIN-C1-CSSTDINSTDOUTSTDERR

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

评论

0赞 ikegami 8/24/2023
你不应该使用 ;你应该使用:utf8:encoding(UTF-8)
0赞 ikegami 8/24/2023
Nit:视为(特定于 Perl 的扩展),而不是(标准)。-CSutf8UTF-8
0赞 ikegami 8/24/2023
-CS这是一个很好的电话,因为无论如何都不够。那可以做 STDIN,但你也需要做 STDOUT。这意味着您至少需要 aka。您可以使用 进行测试。-C1-C3-CIO“é”
0赞 ikegami 8/24/2023
我从答案中删除了关于 1 是 STDOUT 的 fd 的令人困惑的评论评论。这些数字是位掩码(1:STDIN,2:STDOUT,4:STDERR),而不是对文件描述符的引用。(如果是,解码 STDIN 将是 ,不是!-C-C0-C1