使用正则表达式在每个块的末尾使用ID的多行选择块

时间:2012-08-03 06:02:51

标签: regex

我有正则表达式:

BEGIN\s+\[([\s\S]*?)END\s+ID=(.*)\]

从下面的文字中选择多行文字和ID。我只想选择前缀为X_的ID,但如果我将ID=(.*)更改为ID=(X_.*),则根据需要从第二对中选择开始,而不是从第三对中选择。有人可以帮助我得到正确的表达吗?

文字示例:

BEGIN [
text a
END ID=X_1]

BEGIN [
text b
text c
END ID=Y_1]

text aaa
text bbb

BEGIN [
text d
text e
END ID=X_2]

text xxx

BEGIN [
text bbb
END ID=X_3]

3 个答案:

答案 0 :(得分:1)

假设某个块内没有任何换行符BEGIN / END语句是其行的第一个非空格,我会编写正则表达式像这样(Perl表示法;如果使用不同的引擎,则更改分隔符并删除注释,空格和/x修饰符)

m{
  \n \s* BEGIN \s+ \[          # match the beginning
     ( (?!\n\s*\n) .)*?        # match anything that isn't an empty line
                               # checking with a negative look-ahead (?!PATTERN)
  \n \s* END \s+ ID=X_[^\]]* \] # the ID may not contain "]"
}sx                            # /x: use extended syntax, /s: "." matches newlines

如果内容可能是,则最好创建所有块的列表,然后通过它们进行grep。这个正则表达式匹配任何块:

m{ (
  BEGIN \s+ \[
  .*?              # non-greedy matching is important here
  END \s+ ID=[^\]]* \] # greedy matching is safe here
) }xs

(如果需要,添加换行符)

然后只保留与此正则表达式匹配的匹配项:

/ID = X_[^\]]* \] $/x  # anchor at end of line

如果我们不这样做,回溯可能会阻止正确匹配[\s\S]*?可以包含END ID=X_)。你的正则表达式会在块中放置任何内容,直到它看到X_.*

所以使用BEGIN\s+\[([/s/S]*?)END\s+ID=(.*?)\] - 注意额外的问号 - 一个匹配将是:

BEGIN [
text b
text c
END ID=Y_1]

text aaa
text bbb

BEGIN [
text d
text e
END ID=X_2]

...而不是在Y_失败。一个贪婪的匹配(你没有改变的正则表达式)应该导致整个文件匹配:你的(.*)吃掉所有字符(直到文件末尾),然后返回直到找到]

编辑:

如果你使用perls regex引擎,我们可以使用(*FAIL)动词:

/BEGIN\s+\[(.*?)END\s+ID=(X_[^\]]*|(*FAIL))\]/s

“要么ID为X_,要么匹配失败”。但是,这并不能解决数据中END ID=X_1]之类的语句问题。

答案 1 :(得分:1)

正如人们一直在说的那样,.*并不是在吞噬一切,而是[\s\S]*?.*END\s+ID=(X_.*)\]无法做到,因为(正如OP所说)点与新行不匹配。

当正则表达式的[\s\S]*?部分无法与第二个块的最后一行匹配时,您希望它放弃该块并从第三个块重新开始。这就是做最短匹配所必须做的事情。

实际上,它会回溯到行的开头,让END\s+ID=(X_.*)\]代替它。并且它会一直消耗,直到找到END可以匹配的位置,这恰好是第三个块的最后一行。

以下正则表达式通过逐行匹配来避免这个问题,检查每个问题以查看它是否以(?m)^BEGIN\s+\[[\r\n]+((?:(?!END).*[\r\n]+)*)END\s+ID=(X_.*)\] 开头。这有效地将匹配限制在一个块中。

^

请注意,我使用(?m)将每个匹配锚定到一行的开头,因此我使用{{1}}打开了多行模式。但我没有 - 你应该不 - 打开单线/ DOTALL模式。

答案 2 :(得分:0)

将您的.*更改为[^\]]*(即匹配非),以便您的匹配不会溢出END块,为您提供BEGIN\s+\[([^\]]*?)END\s+ID=(X_[^\]]*)\] < / p>