Perl多行正则表达式匹配如何与Unicode字符属性交互?

3

我正在处理一个带有Unix(\n)换行符的多行字符串。

其中一些行的格式为“A,a”(即大写字母、逗号、空格、小写字母),我想从字符串中删除这些行。

我可以用正则表达式替换来完成这个任务,但有一个谜团让我不明白:

使用“[A-Z]”和“[a-z]”的正则表达式在普通模式和多行模式下都有效。

使用“\p{Lu}”和“\p{Ll}”的正则表达式也是有效的,但仅在普通模式下有效,而在多行模式下无效。

每个正则表达式都成功:

$all =~ s/\n\K *[A-Z], [a-z]\n//g;    # 1

$all =~ s/^ *[A-Z], [a-z]\n//mg;      # 2

$all =~ s/\n\K *\p{Lu}, \p{Ll}\n//g;  # 3

但是这种方法失败了:
$all =~ s/^ *\p{Lu}, \p{Ll}\n//mg;    # 4

我原以为 /m 开关会改变正则表达式中 "^" 的含义,但不会改变其他任何内容。所以,我期望语句 4 能够像语句 1、2 和 3 一样工作。语句 2 看起来表明多行语法是可以的,而语句 3 则表明 Unicode 字符属性与预期相匹配,因此,当我将它们结合起来时,我期望语句 4 能够正常工作。
我已经查看了 Tom Christensen 在为什么现代 Perl 默认避免使用 UTF-8?中的回答,但我没有找到关于多行正则表达式匹配的内容,也没有在其他地方找到答案。

1
请提供一个#2和#4结果不同的示例。我的测试 $all = "foo\n A, x\nmeow";,对于两者都有相同的结果。 - ikegami
1个回答

3
我无法复制您的问题。
$ perl -wle'
   $all = "foo\n  A, x\nmeow";
   $all =~ s/^ *[A-Z], [a-z]\n//mg;
   print $all;
'
foo
meow

$ perl -wle'
   $all = "foo\n  A, x\nmeow";
   $all =~ s/^ *\p{Lu}, \p{Ll}\n//mg;
   print $all;
'
foo
meow

测试使用5.8.8、5.10.1、5.12.4(线程)和5.16.0在Linux上进行。

最佳猜测:pos($all)不为零。也许你做了一些傻事,比如 if ($all =~ /.../g)


一开始我也无法去除空格来重现此问题。

$ perl -wle'
   $all = "foo\nA, x\nmeow";
   $all =~ s/^ *[A-Z], [a-z]\n//mg;
   print $all;
'
foo
meow

$ perl -wle'
   $all = "foo\n  A, x\nmeow";
   $all =~ s/^ *\p{Lu}, \p{Ll}\n//mg;
   print $all;
'
foo
meow

在cygwin上测试通过5.10.1(线程化)。

>perl -wle"$all = qq{foo\nA, x\nmeow}; $all =~ s/^ *[A-Z], [a-z]\n//mg; print $all;"
foo
meow

>perl -wle"$all = qq{foo\nA, x\nmeow}; $all =~ s/^ *\p{Lu}, \p{Ll}\n//mg; print $all;"
foo
meow

在Windows操作系统中(ActivePerl),使用5.14.0(多线程)和5.14.2(多线程)进行测试。

但是,惊奇发现!!!

>perl -wle"$all = qq{foo\nA, x\nmeow}; $all =~ s/^ *[A-Z], [a-z]\n//mg; print $all;"
foo
meow

>perl -wle"$all = qq{foo\nA, x\nmeow}; $all =~ s/^ *\p{Lu}, \p{Ll}\n//mg; print $all;"
foo
A, x
meow

在Windows (ActivePerl)上测试了5.10.1(线程),5.12.1(线程)和5.12.4(线程)。

旧版本的Perl似乎存在一个错误。它似乎已经在5.14中得到修复。该错误似乎位于优化器中(使用-Mre=debug查看),因此可以通过“禁用”优化器来绕过该错误。

>perl -wle"$all = qq{foo\nA, x\nmeow}; $all =~ s/^ *\p{Lu}, \p{Ll}\n//mg; print $all;"
foo
A, x
meow

>perl -wle"$all = qq{foo\nA, x\nmeow}; $all =~ s/^ *\p{Lu}{1}, \p{Ll}\n//mg; print $all;"
foo
meow

感谢您的快速评论。我已经复制了您的结果(使用5.12.4)。但是,如果我在两个脚本中删除“A,x”左侧的2个空格,则它们的行为不同:第一个成功,第二个失败。 - Jonathan Pool
@user1572601:我已经在“foo\nA,x\nmeow”上尝试了所有4个正则表达式,并且都给出了相同的(预期)结果。(Perl l5.14.2) - MRAB
@MRAB,请查看更新。其中包括一个解决此错误的解决方法,显然在较新版本的Perl中已经修复了此问题。 - ikegami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接