12

我有这样的文字:

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,但这将从第一个“*/”开始

4

3 回答 3

20

在这种情况下,一个有用的技巧是在正则表达式前面加上贪婪模式.*,这将在其余模式匹配之前尝试匹配尽可能多的字符。所以:

my ($match) = ($string =~ m!^.*\*/(.*?)////RESULT!s);

让我们把这个模式分解成它的组件:

  • ^.*从字符串的开头开始并匹配尽可能多的字符。(s修饰符.甚至允许匹配换行符。)字符串开头的锚点^并不是绝对必要的,但它确保了正则表达式引擎在匹配失败时不会浪费太多时间回溯。

  • \*/只匹配文字字符串*/

  • (.*?)匹配并捕获任意数量的字符;这?使它变得不贪心,因此它更喜欢匹配尽可能少的字符,以防正则表达式的其余部分可以匹配的位置不止一个。

  • 最后,////RESULT只匹配自己。

由于该模式包含很多斜线,并且因为我想避免倾斜牙签综合症,我决定使用替代正则表达式分隔符。感叹号 ( !) 是一种流行的选择,因为它们不会与任何正常的正则表达式语法发生冲突。


编辑:根据下面与 ikegami 的讨论,我想我应该注意,如果你想在更长的正则表达式中使用这个正则表达式作为子模式,并且如果你想保证匹配的字符串(.*?)永远不会包含////RESULT,那么你应该将正则表达式的这些部分包装在一个独立的(?>)子表达式中,如下所示:

my $regexp = qr!\*/(?>(.*?)////RESULT)!s;
...
my $match = ($string =~ /^.*$regexp$some_other_regexp/s);

(?>)会导致其中的模式失败,而不是接受次优匹配(即超出第一个子字符串匹配的匹配////RESULT),即使这意味着正则表达式的其余部分将无法匹配。

于 2013-01-02T19:00:13.427 回答
5
(?:(?!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;
于 2013-01-02T18:57:52.113 回答
4

这是一个选项:

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 
于 2013-01-02T18:44:05.670 回答