我有这样的文字:
hello world /* select a from table_b
*/ some other text with new line cha
racter and there are some blocks of
/* any string */ select this part on
ly
////RESULT rest string
文本是多行的,我需要从最后一次出现的“* /”中提取,直到“//// RESULT”。在这种情况下,结果应为:
select this part on
ly
如何在perl中实现这一目标?
我尝试\\\*/(.|\n)*////RESULT
,但这将从第一个“* /”
答案 0 :(得分:18)
在这种情况下,一个有用的技巧是在regexp前加上贪婪模式.*
,它会在模式的其余部分匹配之前尝试匹配尽可能多的字符。所以:
my ($match) = ($string =~ m!^.*\*/(.*?)////RESULT!s);
让我们将这种模式分解为其组成部分:
^.*
从字符串的开头开始,尽可能多地匹配字符。 (s
修饰符允许.
匹配甚至换行符。)字符串开头锚^
不是绝对必要的,但它确保正则表达式引擎不会浪费太多如果比赛失败,则回溯时间。
\*/
只匹配文字字符串*/
。
(.*?)
匹配并捕获任意数量的字符; ?
使它不合适,所以它更喜欢匹配尽可能少的字符,以防正则表达式的其余部分可以匹配多个位置。
最后,////RESULT
只匹配自己。
由于模式包含很多斜杠,并且因为我想避免使用leaning toothpick syndrome,所以我决定使用替代的regexp分隔符。感叹号(!
)是一种流行的选择,因为它们不会与任何正常的正则表达式语法冲突。
编辑:根据以下与ikegami的讨论,我想我应该注意,如果你想在更长的正则表达式中使用这个正则表达式作为子模式,并且如果你想保证由(.*?)
匹配的字符串将从不包含////RESULT
,然后您应将正则表达式的这些部分包装在independent (?>)
subexpression中,如下所示:
my $regexp = qr!\*/(?>(.*?)////RESULT)!s;
...
my $match = ($string =~ /^.*$regexp$some_other_regexp/s);
(?>)
导致其中的模式失败而不是接受次优匹配(即超出匹配////RESULT
的第一个子串的匹配),即使这意味着正则表达式的其余部分将失败匹配。
答案 1 :(得分:4)
(?:(?!STRING).)*
匹配任意数量的不包含STRING
的字符。它就像[^a]
,但是对于字符串而不是字符。
如果您知道不会遇到某些输入(如Kenosis和Ilmari Karonen所做的那样),您可以使用快捷方式,但这与您指定的内容相符:
my ($segment) = $string =~ m{
\*/
( (?: (?! \*/ ). )* )
////RESULT
(?: (?! \*/ ). )*
\z
}xs;
如果您不关心*/
后////RESULT
是否出现,则以下是最安全的:
my ($segment) = $string =~ m{
\*/
( (?: (?! \*/ ). )* )
////RESULT
}xs;
如果在////RESULT
之后有两个*/
,则您没有指定会发生什么。以上匹配直到最后一个。如果你想匹配到第一个,你可以使用
my ($segment) = $string =~ m{
\*/
( (?: (?! \*/ | ////RESULT ). )* )
////RESULT
}xs;
答案 2 :(得分:2)
这是一个选项:
use strict;
use warnings;
my $string = <<'END';
hello world /* select a from table_b
*/ some other text with new line cha
racter and there are some blocks of
/* any string */ select this part on
ly
////RESULT
END
my ($segment) = $string =~ m!\*/([^/]+)////RESULT$!s;
print $segment;
输出:
select this part on
ly