如何在 perl regex 替换命令中使用 unicode 字符?
How can I use unicode characters in perl regex substitution command?
这在使用 unicode 字符时不起作用(在 Ubuntu bash 中):
$ perl -pC -e's/[à]/a/gu' <<< 'à'
à
$ perl -pC -e's/[b]/a/gu' <<< 'b'
a
尽管 PCRE 似乎支持它(至少根据 regex101)。
我做错了什么?我是否在 perl 命令中遗漏了一些标志?
这在 javascript 中“有效”,所以如果我能在命令行中为此想出一个简单的单行程序,我会使用节点......但我仍然想知道为什么perl 命令无效。
对于上下文:
我正在尝试使用 /[àâáãä]/a/g
、/[òôóõö]/o/g
等替换来将字典文件 asciify(即删除单词列表的重音符号等),因此我可以用它来使拼写检查不区分重音(例如在 IntelliJ Idea 中)。
基本上这些是制作“asciified”额外字典的步骤:
- 下载该语言的 .dic 文件(所有单词的列表)
- 使用 grep 过滤包含非 ascii/可替换字符的词
- 连续使用正则表达式替换使单词不区分重音
- 导入IDE中的asciified.dic文件(标准语言词典除外)
所有这些的一个实用方法是使用 Text::Unidecode
perl -C -MText::Unidecode -pe'unidecode($_)' <<< 'à'
打印 a
。该模块将 Unicode 文本音译为纯 ASCII。
另一种方法:使用 Unicode::Normalize, so that the character and its diacritical marks (combining accents) are separated into their own code points, while they still form a valid grapheme 分解字符(“规范化”),然后使用简单的正则表达式删除变音符号(\p{NonspacingMark}
或 \p{Mn}
)。
这两种方式都会有例外和边缘情况,但我认为它可能正好满足您的需要。
对于包含特定(文字)字符的代码,需要通过utf8 pragma和use utf8;
或命令行标志告诉Perl程序源是UTF-8 -Mutf8
perl -C -Mutf8 -pe's/[à]/a/g' <<< 'à'
您需要添加 -Mutf8
以告诉 Perl 该程序是使用 UTF-8 而不是 ASCII 编码的。
$ perl -pC -Mutf8 -e's/[à]/a/gu' <<< 'à'
a
下面是我如何实施第 2 步和第 3 步。
这可以在 these 词典中使用(虽然我没有在每种语言上测试它)。
asciify-dic
#!/usr/bin/env bash
#License: "Zero-Clause BSD" <https://opensource.org/licenses/0BSD>
if [[ "" == "--help" ]]; then
echo "Usage: $(basename "[=10=]") INPUT_FILE > OUTPUT_FILE"
echo "Asciify a .dic file (list of dictionary words)."
echo ""
echo "Generates a file with ASCII-only versions of the words that have non-ASCII chars."
echo "These additional words can be used to make spell-checking accent-insensitive."
echo "Comment lines beginning with % are left unchanged."
exit
fi
# Filter words containing non-ascii characters, except in comments
grep -P '^\%|[^\x00-\x7F]' |
# Make words accent-insensitive, except in comments
perl -C -MText::Unidecode -pe'next if /^\s*%/;unidecode($_)' |
# Remove duplicate lines, except in comments
awk '/^\s*%/||!seen[[=10=]]++'
用法示例:
asciify-dic $DIC_NAME.dic > $DIC_NAME-asciified.dic
简短的回答是将 -Mutf8
添加到您的命令行。
如果您不确定 Perl 如何解释您在命令行上编写的内容,您可以使用核心 B::perlstring()
函数让它返回给您,或者使用 [=23= 解析整个脚本].那会很快说明你的问题。 (将 'à' 字符括在括号中在这里没有任何作用。)
$ perl -MO=Deparse -pC -e 's/à/a/gu' <<< 'à'
LINE: while (defined($_ = <ARGV>)) {
s/30/a/gu;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
看看你的替换是如何巧妙地包含 2 个字符的?
然后您可以立即看到 use utf8
如何解决您的问题。
$ perl -MO=Deparse -Mutf8 -pC -e 's/à/a/gu' <<< 'à'
use utf8;
LINE: while (defined($_ = <ARGV>)) {
s/0/a/gu;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
您可以使用 perlstring()
来确保 Perl 正在接收您认为的输入。
$ perl -p -MB -E 'say B::perlstring($_)' <<< 'à'
"30\n"
à
$ perl -pC -MB -E 'say B::perlstring($_)' <<< 'à'
"\x{e0}\n"
à
你可以看到没有 -C
Perl 正在接收 2 个分解的字符。
根据情况,Perl 将字符转储为八进制代码 (0
) 或十六进制代码 (\xE0
)。请注意,您始终可以将命令行中的原始 unicode 字符替换为转义码版本。这是一个很好的方式来明确什么否则会模棱两可。
$ perl -pC -e 's/[\xE0]/a/gu' <<< 'à'
a
如果您不想记住 UTF8 模式,可以将这些选项添加到 PERL5OPT
环境变量中或创建一个 shell 别名。谨防将其设为全球!
$ export PERL5OPT='-C -Mutf8'
$ perl -MO=Deparse -p -e 's/à/a/gu' <<< 'à'
use utf8;
LINE: while (defined($_ = <ARGV>)) {
s/0/a/gu;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
$ perl -MB -p -E 'say B::perlstring($_)' <<< 'à'
"\x{e0}\n"
à
或作为 shell 别名。
alias uperl='perl -C -Mutf8'
有关如何在命令行中使用 Swiss Army Chainsaw 的详细信息,请参阅 perlrun。
另见 B::Deparse。
这在使用 unicode 字符时不起作用(在 Ubuntu bash 中):
$ perl -pC -e's/[à]/a/gu' <<< 'à'
à
$ perl -pC -e's/[b]/a/gu' <<< 'b'
a
尽管 PCRE 似乎支持它(至少根据 regex101)。
我做错了什么?我是否在 perl 命令中遗漏了一些标志?
这在 javascript 中“有效”,所以如果我能在命令行中为此想出一个简单的单行程序,我会使用节点......但我仍然想知道为什么perl 命令无效。
对于上下文:
我正在尝试使用 /[àâáãä]/a/g
、/[òôóõö]/o/g
等替换来将字典文件 asciify(即删除单词列表的重音符号等),因此我可以用它来使拼写检查不区分重音(例如在 IntelliJ Idea 中)。
基本上这些是制作“asciified”额外字典的步骤:
- 下载该语言的 .dic 文件(所有单词的列表)
- 使用 grep 过滤包含非 ascii/可替换字符的词
- 连续使用正则表达式替换使单词不区分重音
- 导入IDE中的asciified.dic文件(标准语言词典除外)
所有这些的一个实用方法是使用 Text::Unidecode
perl -C -MText::Unidecode -pe'unidecode($_)' <<< 'à'
打印 a
。该模块将 Unicode 文本音译为纯 ASCII。
另一种方法:使用 Unicode::Normalize, so that the character and its diacritical marks (combining accents) are separated into their own code points, while they still form a valid grapheme 分解字符(“规范化”),然后使用简单的正则表达式删除变音符号(\p{NonspacingMark}
或 \p{Mn}
)。
这两种方式都会有例外和边缘情况,但我认为它可能正好满足您的需要。
对于包含特定(文字)字符的代码,需要通过utf8 pragma和use utf8;
或命令行标志告诉Perl程序源是UTF-8 -Mutf8
perl -C -Mutf8 -pe's/[à]/a/g' <<< 'à'
您需要添加 -Mutf8
以告诉 Perl 该程序是使用 UTF-8 而不是 ASCII 编码的。
$ perl -pC -Mutf8 -e's/[à]/a/gu' <<< 'à'
a
下面是我如何实施第 2 步和第 3 步。
这可以在 these 词典中使用(虽然我没有在每种语言上测试它)。
asciify-dic
#!/usr/bin/env bash
#License: "Zero-Clause BSD" <https://opensource.org/licenses/0BSD>
if [[ "" == "--help" ]]; then
echo "Usage: $(basename "[=10=]") INPUT_FILE > OUTPUT_FILE"
echo "Asciify a .dic file (list of dictionary words)."
echo ""
echo "Generates a file with ASCII-only versions of the words that have non-ASCII chars."
echo "These additional words can be used to make spell-checking accent-insensitive."
echo "Comment lines beginning with % are left unchanged."
exit
fi
# Filter words containing non-ascii characters, except in comments
grep -P '^\%|[^\x00-\x7F]' |
# Make words accent-insensitive, except in comments
perl -C -MText::Unidecode -pe'next if /^\s*%/;unidecode($_)' |
# Remove duplicate lines, except in comments
awk '/^\s*%/||!seen[[=10=]]++'
用法示例:
asciify-dic $DIC_NAME.dic > $DIC_NAME-asciified.dic
简短的回答是将 -Mutf8
添加到您的命令行。
如果您不确定 Perl 如何解释您在命令行上编写的内容,您可以使用核心 B::perlstring()
函数让它返回给您,或者使用 [=23= 解析整个脚本].那会很快说明你的问题。 (将 'à' 字符括在括号中在这里没有任何作用。)
$ perl -MO=Deparse -pC -e 's/à/a/gu' <<< 'à'
LINE: while (defined($_ = <ARGV>)) {
s/30/a/gu;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
看看你的替换是如何巧妙地包含 2 个字符的?
然后您可以立即看到 use utf8
如何解决您的问题。
$ perl -MO=Deparse -Mutf8 -pC -e 's/à/a/gu' <<< 'à'
use utf8;
LINE: while (defined($_ = <ARGV>)) {
s/0/a/gu;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
您可以使用 perlstring()
来确保 Perl 正在接收您认为的输入。
$ perl -p -MB -E 'say B::perlstring($_)' <<< 'à'
"30\n"
à
$ perl -pC -MB -E 'say B::perlstring($_)' <<< 'à'
"\x{e0}\n"
à
你可以看到没有 -C
Perl 正在接收 2 个分解的字符。
根据情况,Perl 将字符转储为八进制代码 (0
) 或十六进制代码 (\xE0
)。请注意,您始终可以将命令行中的原始 unicode 字符替换为转义码版本。这是一个很好的方式来明确什么否则会模棱两可。
$ perl -pC -e 's/[\xE0]/a/gu' <<< 'à'
a
如果您不想记住 UTF8 模式,可以将这些选项添加到 PERL5OPT
环境变量中或创建一个 shell 别名。谨防将其设为全球!
$ export PERL5OPT='-C -Mutf8'
$ perl -MO=Deparse -p -e 's/à/a/gu' <<< 'à'
use utf8;
LINE: while (defined($_ = <ARGV>)) {
s/0/a/gu;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
$ perl -MB -p -E 'say B::perlstring($_)' <<< 'à'
"\x{e0}\n"
à
或作为 shell 别名。
alias uperl='perl -C -Mutf8'
有关如何在命令行中使用 Swiss Army Chainsaw 的详细信息,请参阅 perlrun。
另见 B::Deparse。