我有正则表达式:
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]
答案 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>