[英]regex with lookbehind weird behavior
过去两天我一直试图解决这个问题......
请帮助我理解为什么会这样。 我的目的是只要选择<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>
谁能帮帮我吗?
正则表达式引擎将始终为您提供字符串中最左侧的匹配(即使您使用非贪婪的量词)。 这正是您获得的。
因此,解决方案是禁止在.*?
描述的部分中存在另一个<HDR>
.*?
这太宽容了。
你有两个技术要做,你可以替换.*?
有:
(?>[^<]+|<(?!/HDR))*
或者:
(?:(?!</HDR).)*+
大多数情况下,第一种技术性能更高,但如果你的字符串包含高密度的<
,第二种方法也可以产生很好的效果。
占有量词或原子组的使用可以减少获得结果的步骤数,特别是当子模式失败时。
例:
第一种方式:
(?<=<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>
Casimir et Hippolyte已经为您提供了几个很好的解决方案。 我想详细说明一些事情。
首先,为什么你的正则表达式无法做你想做的事情: (?<=<HDR>).*?
告诉它匹配以<HDR>
开头的第一个字符开头的任意数量的字符,直到遇到非贪婪量词( <DTL1...
)之后的<DTL1...
。 嗯,这是由后面的第一个字符<HDR>
是第a
,所以它匹配一切从这里开始,直到固定字符串<DTL1\\sval="3"
遇到。
Casimir et Hippolyte的解决方案适用于广义情况,其中<HDR>标签的内容可以是嵌套<HDR>以外的任何内容。 您也可以通过积极的预测来做到这一点:
(?<=<HDR>)(.(?!</HDR>))*<DTL1\sval="3".*?</HDR>
但是,如果字符串保证在显示的结构中,其中<HDR>标记只包含一个或多个<DTL1 val =“ ## ”>标记,那么您知道其中不会有任何结束标记,可以通过替换第一个来更有效地做到这一点.*?
用[^/]*
:
(?<=<HDR>)[^/]*<DTL1\sval="3".*?</HDR>
否定字符类比零宽度断言更有效, 如果使用否定字符类,则贪婪量词变得比惰性量词更有效。
另请注意,通过使用lookbehind匹配开头<HDR>,您将其从匹配中排除,但您要包括结束</ HDR>。 你确定那是你想要的吗? 你匹配这个......
<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>)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.