Perl 命令行替换 unicode



我正在尝试使用shell脚本将每个单词(存储在名为_id的tmp文件中(替换为一个数字。它工作正常,除了 unicode 单词,生成了一个数字,但使用 Perl 替换不起作用。有问题的 bash 代码如下:

x=0
for id in `cat _id`; do
echo $x $id
perl -p -i -e "s/b$idb/$x/g" x_graph.dot
x=$(($x + 1))
done 

有人可以指出错误在哪里吗?

假设您使用 UTF-8 编码了é(U+00E9(:C3 A9。由于您不执行任何解码,因此您将获得由"xC3xA9"生成的字符串。

正则表达式(或者更确切地说是bwd等(期望输入是 Unicode 码位,这意味着您有效地提供了 U+00C3 和 U+00A9 而不是 U+00E9。U+00C3 是一个单词字符,但 U+00A9 不是,因此第二个b与预期匹配的位置不匹配。

因此,您需要解码输入并对输出进行编码。-C为 UTF-8 提供了一种方便的方法。

perl -i -CSDA -pe'
BEGIN {
($id, $x) = splice(@ARGV, 0, 2);
die "Bad id" if $id !~ /^w(?:.*w)?z/s;
}
s/bQ$idEb/$x/g
' "$id" "$x" x_graph.dot

笔记:

  • 通过使用命令行参数传递参数,我修复了一个注入错误。

  • 使用b假定$id始终以w字符开头,始终以w字符结尾,因此我添加了一个检查来验证该假设。

  • 通过使用Q..E将id转换为正则表达式模式,我修复了一个注入错误。


测试:

$ printf "én" >_id
$ printf "[é]n" >x_graph.dot
$ x=0
$ id=`cat _id`
$ perl -i -CSDA -pe'
BEGIN {
($id, $x) = splice(@ARGV, 0, 2);
die "Bad id" if $id !~ /^w(?:.*w)?z/s;
}
s/bQ$idEb/$x/g
' "$id" "$x" x_graph.dot
$ cat x_graph.dot
[0]

参见 perldoc perlrun:

-C[数量/列表]

-C标志控制着一些Perl Unicode的特性:

I     1   STDIN is assumed to be in UTF-8
O     2   STDOUT will be in UTF-8
E     4   STDERR will be in UTF-8
S     7   I + O + E
i     8   UTF-8 is the default PerlIO layer for input streams
o    16   UTF-8 is the default PerlIO layer for output streams
D    24   i + o
A    32   the @ARGV elements are expected to be strings encoded
in UTF-8

所以,至少,你想要perl -COi,但perl -CSD看起来更整洁。

此外,您可能希望使用

根据 Unicode 规则进行u匹配

与你的s///.或者,写:

perl -CSD -Mutf8 -Mfeature=unicode_strings -p -i -e "s/b$idb/$x/g" x_graph.dot

请注意使用单引号而不是双引号,以避免意外插值。

  1. 添加-Mutf8(相当于use utf8;(:这将在源代码中启用 UTF-8(在您的情况下-e单行(。

  2. 添加-CSDA:这将使perl使用 UTF-8 作为输入和输出流的默认层。

以下测试在LANG=en_US.UTF-8下产生了预期的结果

echo "a ó b" > z.txt
id=ó
x=ń
perl -CD -Mutf8 -p -i -e "s/b$idb/$x/g" z.txt
cat z.txt

man perlrun

-C [number/list]
-C 标志控制着一些 Perl Unicode 的特性。
...
S 8 I + O + E [ STDIN 假定为 UTF-8,STDOUT 和 STDERR 将为 UTF-8]
D 24 i + o [ UTF-8 是输入和输出流的默认 PerlIO 层]
A 32 @ARGV元素应为字符串编码 在 UTF-8 中

最新更新