关于正则表达式:了解Perl正则表达式修饰符/ m和/ s

Understanding Perl regular expression modifers /m and /s

本问题已经有最佳答案,请猛点这里访问。

我一直在阅读带有修饰符s m和g的perl正则表达式。我理解//g是一个全局匹配,在这里它将是一个贪婪的搜索。

但是我对修饰符s和m感到困惑。有人能用代码示例解释s和m之间的区别吗,以展示它是如何不同的?我试过在线搜索,它只给出链接http://perldoc.perl.org/perlre.html modifiers中的解释。在StackOverflow中,我甚至看到人们一起使用S和M。S不是M的对立面吗?

1
2
3
//s
//m
//g

我无法使用m匹配多行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use warnings;
use strict;
use 5.012;

my $file;
{
 local $/ = undef;
 $file = <DATA>;
};
my @strings = $file =~ /".*"/mg; #returns all except the last string across multiple lines
#/"String"/mg; tried with this as well and returns nothing except String
say for @strings;

__DATA__
"This is string"
"1!=2"
"This is "string""
"string1"."string2"
"String"
"S
t
r
i
n
g"

在我看来,你链接到自己的文档是非常清楚的。如果你能解释你在理解它时遇到了什么问题,以及你是如何认为/s/m是对立的,那会有所帮助。

简单地说,/s改变了点元字符.的行为,使其完全匹配任何字符。通常,它匹配除换行"
"
以外的任何内容,因此,即使字符串包含换行,它也将把字符串视为一行。

/m修改插入符号^和dollar $元字符,使它们在字符串中的换行处匹配,将其视为多行字符串。通常,它们只在字符串的开始和结束处匹配。

你不应该把/g修饰语搞得"贪婪"。它用于全局匹配,可以在字符串中找到模式的所有匹配项。贪婪一词通常是模式中量词行为的使用者。例如,据说.*贪婪,因为它将匹配尽可能多的字符,而不是.*?,后者将匹配尽可能少的字符。

更新

在修改后的问题中,您使用的是/".*"/mg,其中/m与此无关,因为如上所述,修改器只改变$^元字符的行为,而您的模式中没有。

把它改为/".*"/sg稍微改善了一些,因为.现在可以匹配每行末尾的换行符,这样模式可以匹配多行字符串。(注意,这里被认为是"单行"的对象字符串,也就是说,对于.而言,匹配的行为就像没有换行符一样。)然而,这里是贪婪的传统含义,因为模式现在匹配从第一行的第一个双引号到最后一个双引号的所有内容。最后一行的结尾。我想那不是你想要的。

有几种方法可以解决这个问题。我建议更改您的模式,使您想要的字符串是双引号,后跟除双引号之外的任何字符序列,后跟另一个双引号。这里写的是/"[^"]*"/g(请注意,/s修饰符不再是必需的,因为模式中现在没有点),并且几乎可以按您的需要执行,除了转义双引号被视为结束模式。

请看一下这个程序及其输出,注意我在每一场比赛开始时都放了一个雪佛龙>>,以便区分它们

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;

my $file = do {
  local $/;
  <DATA>;
};

my @strings = $file =~ /"[^"]*"/g;

print">> $_

"
, for @strings;

__DATA__
"This is string"
"1!=2"
"This is "string""
"string1"."string2"
"String"
"S
t
r
i
n
g"

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>"This is string"

>>"1!=2"

>>"This is "

>>""

>>"string1"

>>"string2"

>>"String"

>>"S
t
r
i
n
g"

如你所见,除了在"This is \"string\""中发现了两个匹配项,"This is \"""外,现在一切都井然有序。修复可能比你想去的复杂,但这是完全可能的。如果你也需要修理的话,请这样说。

更新

我还是把这件事做完吧。要忽略转义双引号并将其视为字符串的一部分,我们需要接受\"或除双引号之外的任何字符。这是使用regex交替运算符|完成的,并且必须分组到非捕获括号(?: ... )中。最终的结果是/"(?:\\"|[^"])*"/g(反斜杠本身必须转义,所以它是翻倍的),当放入上述程序时,它产生这个输出,我假设这是您想要的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>"This is string"

>>"1!=2"

>>"This is "string""

>>"string1"

>>"string2"

>>"String"

>>"S
t
r
i
n
g"


/m/s都影响match运算符处理多行字符串的方式。

使用/m修饰符,^$匹配字符串中任何行的开始和结束。如果没有/m修饰符,^$只匹配字符串的开始和结束。

例子:

1
2
3
4
5
6
$_ ="foo
bar
"
;

/foo$/,  /^bar/       do not match
/foo$/m, /^bar/m      match

使用/s修饰符,特殊字符.匹配所有字符,包括换行符。没有/s修饰符,.匹配除换行以外的所有字符。

1
2
3
4
5
6
$_ ="cat
dog
goldfish"
;

/cat.*fish/           does not match
/cat.*fish/s          matches

可以同时使用/sm修饰符。

1
2
3
4
5
6
7
8
9
10
11
12
$_ ="100
101
102
103
104
105
"
;

/^102.*104$/          does not match
/^102.*104$/s         does not match
/^102.*104$/m         does not match
/^102.*104$/sm        matches


/".*"/mg你的比赛

  • "开始
  • 然后,.*"尽可能匹配每个字符(
    除外),直到"为止。
  • 由于您使用/g和match stopped at second ",regex将尝试重复前两个步骤。
  • 这里,/m没有区别,因为你没有使用^$锚。
  • 由于您在示例中转义了引号,所以regex不是执行所需操作的最佳工具。如果不是这样的话,你想要两个报价之间的所有东西,那么/".*?"/gs就可以做到。


    博罗丁的regex将为这个实验室任务的例子工作。

    但是,反斜杠也可以自行退出。当一个字符串中包含Windows路径时,就会出现这种情况,因此以下regex将捕获这种情况:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    use warnings;
    use strict;
    use 5.012;

    my $file = do { local $/; <DATA>};

    my @strings = $file =~ /"(?:(?>[^"\\]+)|\\.)*"/g;

    say"<$_>" for @strings;

    __DATA__
    "This is string"
    "1!=2"
    "This is "string""
    "string1"."string2"
    "String"
    "S
    t
    r
    i
    n
    g"

    "C:\\windows\\style\\path\"
    "
    another string"

    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <"This is string">
    <"1!=2">
    <"This is "string"">
    <"string1">
    <"string2">
    <"String">
    <"S
    t
    r
    i
    n
    g"
    >
    <"C:\\windows\\style\\path\">
    <"
    another string">

    要快速解释模式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    my @strings = $file =~ m{
       "
            (?:
                (?>            # Independent subexpression (reduces backtracking)
                    [^"
    \\]+    # Gobble all non double quotes and backslashes
                )
            |
                \\.            # Backslash followed by any character
            )*
       "
        }xg;                   # /x modifier allows whitespace and comments.