具有怪异怪异行为的正则表达式

时间:2014-11-30 16:32:26

标签: java javascript python regex regex-greedy

过去两天我一直试图解决这个问题......

请帮助我理解为什么会这样。我的目的是只选择<HDR>

<DTL1 val="92">.....</HDR>

这是我的正则表达式

(?<=<HDR>).*?<DTL1\sval="3".*?</HDR>

输入字符串是:

<HDR>abc<DTL1 val="1"><DTL2 val="2"></HDR><HDR><DTL1 val="92"><DTL2 val="55"></HDR><HDR><DTL1 val="3"><DTL2 val="4"></HDR>

但是这个正则表达式选择了

abc<DTL1 val="1"><DTL2 val="2"></HDR><HDR><DTL1 val="92"><DTL2 val="55"></HDR>

有人可以帮助我吗?

2 个答案:

答案 0 :(得分:2)

正则表达式引擎将始终为您提供字符串中最左侧的匹配(即使您使用非贪婪的量词)。这正是您获得的。

因此,一个解决方案是禁止在<HDR>描述的部分中存在另一个.*?太过宽松。

您有两种技术要做,您可以将.*?替换为:

(?>[^<]+|<(?!/HDR))*

或与:

(?:(?!</HDR).)*+

大多数情况下,第一种技术性能更高,但如果你的字符串包含高密度的<,第二种方法也可以产生良好的效果。

使用possessive quantifieratomic group可以减少获取结果的步骤数,特别是在子模式失败时。

示例:

第一种方式:

(?<=<HDR>)(?>[^<]+|<(?!/HDR))*<DTL1\sval="3"(?>[^<]+|<(?!/HDR))*</HDR>

或此变体:

(?<=<HDR>)(?:[^<]+|<(?!/HDR|DTL1))*+<DTL1\sval="3"(?:[^<]+|<(?!/HDR))*+</HDR>

第二种方式:

(?<=<HDR>)(?:(?!</HDR).)*<DTL1\sval="3"(?:(?!</HDR).)*+</HDR>

或此变体:

(?<=<HDR>)(?:(?!</HDR|DTL1).)*+<DTL1\sval="3"(?:(?!</HDR).)*+</HDR>

答案 1 :(得分:1)

Casimir et Hippolyte已经为您提供了几个很好的解决方案。我想详细说明一些事情。

首先,为什么你的正则表达式无法做你想做的事情:(?<=<HDR>).*?告诉它匹配以<HDR>开头的第一个字符开头的任意数量的字符,直到遇到非贪婪之后的字符量词(<DTL1...)。好吧,<HDR>前面的第一个字符是第一个a,所以它匹配从那里开始的所有字符,直到遇到固定字符串<DTL1\sval="3"

Casimir et Hippolyte的解决方案是针对一般情况的,其中&lt; HDR&gt;的内容是&lt; HDR&gt;。标签可以是嵌套&lt; HDR&gt;以外的任何其他内容。你也可以积极地展望:

(?<=<HDR>)(.(?!</HDR>))*<DTL1\sval="3".*?</HDR>

但是,如果保证字符串在所示结构中,则&lt; HDR&gt;标签只包含一个或多个&lt; DTL1 val =&#34; ## &#34;&gt;标签,因此您知道其中没有任何结束标记,您可以通过将.*?替换为[^/]*来更有效地执行此操作:

(?<=<HDR>)[^/]*<DTL1\sval="3".*?</HDR>

否定字符类比零宽度断言更有效,而如果你使用否定字符类,贪婪量词变得比懒惰更有效。

另请注意,通过使用lookbehind匹配开头&lt; HDR&gt;,您可以将其从匹配中排除,但是您还要包括结束&lt; / HDR&gt;。你确定这是你想要的吗?你匹配这个...

<DTL1 val="3"><DTL2 val="4"></HDR>

......大概你想要这个......

<HDR><DTL1 val="3"><DTL2 val="4"></HDR>

......或者......

<DTL1 val="3"><DTL2 val="4">

所以,在第一种情况下,不要使用lookbehind作为开头标记:

<HDR>(.(?!</HDR>))*<DTL1\sval="3".*?</HDR>
<HDR>[^/]*<DTL1\sval="3".*?</HDR>

在第二种情况下,使用结束标记的前瞻:

(?<=<HDR>)(.(?!</HDR>))*<DTL1\sval="3".*?(?=</HDR>)
(?<=<HDR>)[^/]*<DTL1\sval="3".*?(?=</HDR>)