将“m”修饰符添加到作为参数传递的 qr 模式。

8
我希望能够向一个函数传递的正则表达式添加 "m" 修饰符。
以下测试脚本演示了我所要做的事情。
#!/usr/bin/env perl

use strict;
use warnings;
use v5.16.3;

use Test::More tests => 3;

my $no_m_modifier_re   = qr{^line1\n^line2};
my $with_m_modifier_re = qr{^line1\n^line2}m;

my $text = <<'EoM';
line1
line2
line3
EoM

unlike( $text, $no_m_modifier_re, 'Text will not match ^ that is equivalent to \A' );
like( $text, $with_m_modifier_re, 'Text will match ^ with m modifier' );

# This fails to add the m modifier to the subexpression
my $add_m_modifier_re = qr{(?m)$no_m_modifier_re};
#my $add_m_modifier_re = qr{(?m:$no_m_modifier_re)};    # Experimented other syntax, with same result
#my $add_m_modifier_re = qr{$no_m_modifier_re}m;
#my $add_m_modifier_re = qr{(?^m:$no_m_modifier_re)};    # suggested by mob, didn't work.

like( $text, $add_m_modifier_re, 'Want this to match, but it fails to add m modifier to subexpression' );

结果如下:

$ prove -v m_modifier.pl
m_modifier.pl ..
1..3
ok 1 - Text will not match ^ that is equivalent to \A
ok 2 - Text will match ^ with m modifier
not ok 3 - Want this to match, but it fails to add m modifier to subexpression

#   Failed test 'Want this to match, but it fails to add m modifier to subexpression'
#   at m_modifier.pl line 25.
#                   'line1
# line2
# line3
# '
#     doesn't match '(?^:(?m)(?^:^line1\n^line2))'
# Looks like you failed 1 test of 3.
Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/3 subtests

Test Summary Report
-------------------
m_modifier.t (Wstat: 256 Tests: 3 Failed: 1)
  Failed test:  3
  Non-zero exit status: 1
Files=1, Tests=3,  1 wallclock secs ( 0.04 usr  0.01 sys +  0.14 cusr  0.05 csys =  0.24 CPU)
Result: FAIL

正如你所看到的,我尝试了不同的语法来添加m修饰符,但似乎没有一个适用于原始模式。

有什么想法吗?

这是在Perl 5.16.3下进行的。 我还没有尝试过更现代的版本。


@zdim,我真的只是在寻找任何一种方法来强制传递的qr正则表达式使用/m修饰符。如果必须以某种方式进行黑客攻击,那就这样做。或者,检查RE是否具有/m修饰符的方法也可能很有帮助,因为我可以将方法的负担放在用户身上,要求他们始终包含它,否则将抛出异常。 - Miller
4个回答

4
问题在于您需要更改一个被qr包装起来的表达式,它是一个正则表达式对象,与包含相同字符的字符串有所不同: ref(qr/x/) 返回 "Regexp";但是,对它进行取消引用的操作并没有被很好地定义。
我找不到一种方法来更改它或向其添加标志(除了编辑其字符串表示形式)。
但是,如果您可以更改方法,以先定义(非qr)变量,然后根据需要稍后将它们转换为qr,那么它就会按预期工作。
use strict;
use warnings;
use v5.16.3;

use Test::More tests => 3;

my $no_m_modifier_re   = q{^line1\n^line2};  # not qr{} but q{}; just a string

my $text = <<'EoM';
line1
line2
line3
EoM

unlike( $text, qr{$no_m_modifier_re}, 'Text does not match ^ equivalent to \A' );

like(   $text, qr{$no_m_modifier_re}m, 'Text matches with the modifier' );

like(   $text, qr{(?m)$no_m_modifier_re}, 'Text matches with the modifier' );

除了字符串外,为了方便,用户还可以设置qr变量,但主要思想是用户通过修改符号来形成正则表达式模式。


如果两者都可能被传递,它们可以通过ref区分。


3
我尝试了像你建议的那样使用qr{(?^m:$no_m_modifier_re)},但它仍然失败了。测试报告显示doesn't match '(?^u:(?^m:(?^u:^line1\n^line2)))'
您正在尝试修改一个已编译模式。为此,您需要使用以下内容:
use re qw( is_regexp regexp_pattern );

my $re = qr/^line1\n^line2/;

my ($pat, $mods) =
   is_regexp($re)
      ? regexp_pattern($re)
      : ( $re, "" );

$mods .= 'm' if $mods !~ /m/;

$re = eval("qr/\$pat/$mods")
   or die($@);  # Should never happen.

它还可以使用未编译的模式,生成尽可能少的(?:)嵌套的编译模式。

The result for   "abc"       is   qr/abc/m    which stringifies as   (?^um:abc)
The result for   qr/abc/     is   qr/abc/m    which stringifies as   (?^um:abc)
The result for   qr/abc/m    is   qr/abc/m    which stringifies as   (?^um:abc)
The result for   qr/abc/s    is   qr/abc/sm   which stringifies as   (?^ums:abc)
The result for   qr/abc/sm   is   qr/abc/sm   which stringifies as   (?^ums:abc)

@Miller 一致性。我使用低优先级的 orand 控制流程。 - ikegami
1
不用犹豫,甚至不需要检查是否已定义。qr//总是返回真值。已修复。 - ikegami

3

您已经接近成功。在 Perl 5.16.3 中,它是这样的:

qr/(?^m:pattern)/   # equiv to   /pattern/m

在早期版本中,它会像这样:
qr/(?m-xis:pattern)/   # equiv to  /pattern/m
qr/(?ix-ms:patterm)/   # equiv to  /pattern/ix

这种方法不适用于所有正则表达式修饰符,特别是无法通过此方式模拟/g修饰符。

示例:

$ perl -E 'say 0 + ("CAT" =~ /cat/)'
0
$ perl -E 'say 0 + ("CAT" =~ /(?^i:cat)/)'
1

更新:在这里找到了文档(点击跳转)。篇幅过长,无法摘录,但文档提供了比我的回答更广泛、更深入的"嵌入式模式匹配修饰符"的理解。


我按照你的建议尝试了qr{(?^m:$no_m_modifier_re)},但仍然失败了。测试报告显示不匹配 '(?^u:(?^m:(?^u:^line1\n^line2)))' - Miller
我认为问题在于他们具有需要更改的“qr”模式,而在我的测试中,这仍然无法实现...像perl -wE'$_=q(aAa); $re = qr{a}; say for /(?^i)($re)/g'打印 aa(没有 A)。使用 $re = q{a}(不是qr)可以实现并打印出 aAa - zdim
关于“这不适用于所有正则表达式修饰符”的问题,它适用于影响正则表达式的所有修饰符(我称之为正则表达式修饰符),但不适用于操作符修饰符。g 用于 m//s///s///rs///e。如果一个修饰符不影响正则表达式,那么在正则表达式中就不能使用它,这并不奇怪。 - ikegami
$add_m_modifier_re 是一个 qr 构造的 qr 构造?我认为这就是问题所在。一个更简单的 $add_m_modifier_re = qr{(?m)^line1\n^line2} 应该可以解决问题。 - mob
is a qr construction of a qr construction?" -- 是的,这就是问题所在,正如评论中所示。 - zdim
有一个re::函数可以将编译后的正则表达式拆分为模式和标志,这可能有助于嵌套。 - ikegami

1
你的方法自5.14版本(含)以来已经失效。
替换为:
# This fails to add the m modifier to the subexpression
my $add_m_modifier_re = qr{(?m)$no_m_modifier_re};

(?^u:(?m)(?^u:^line1\n^line2))

使用:

my $add_m_modifier = $no_m_modifier_re;
$add_m_modifier =~ s/:/m:/;
my $add_m_modifier_re = qr{$add_m_modifier};

(?^u:(?^um:^line1\n^line2))

我以前没有使用过Perl,所以不要责怪我!;D

https://perldoc.perl.org/perl5140delta#(?%5E...)-construct-signifies-default-modifiers


1
这只是个技巧,但是以下代码可以运行:my $add_m_modifier_re = ( map qr/$_/m, $no_m_modifier_re =~ s/:/m:/r )[0]; - Miller
1
这是我找到的唯一更改qr模式并编辑其字符串的方法。 - zdim

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