【问题标题】:Python regex A|B|C matches C even though B should matchPython 正则表达式 A|B|C 匹配 C,即使 B 应该匹配
【发布时间】:2021-06-15 06:07:33
【问题描述】:

我已经在这个问题上坐了几个小时了,我真的不知道了...... 本质上,我有一个 A|B|C - 类型分隔的正则表达式,无论出于何种原因,C 都与 B 匹配,即使单个正则表达式应该从左到右进行测试并以非贪婪的方式停止(即一次匹配找到,其他正则表达式'不再测试)。

这是我的代码:

text = 'Patients with end stage heart failure fall into stage D of the ABCD classification of the American College of Cardiology (ACC)/American Heart Association (AHA), and class III–IV of the New York Heart Association (NYHA) functional classification; they are characterised by advanced structural heart disease and pronounced symptoms of heart failure at rest or upon minimal physical exertion, despite maximal medical treatment according to current guidelines.'
expansion = "American Heart Association"
re_exp = re.compile(expansion + "|" + r"(?<=\W)" + expansion + "|"\
                    + expansion.split()[0] + r"[-\s].*?\s*?" + expansion.split()[-1])

m = re_exp.search(text)
print(m.group(0))

我希望正则表达式找到“扩展”字符串。在我的数据集中,有时文本对扩展字符串进行了略微编辑,例如在主要名词之间有冠词或介词,如“for”或“the”。这就是为什么我首先尝试按原样匹配字符串,然后尝试匹配它是否在任何非单词字符之后(即括号,或者像上面的示例一样,因为空格被省略了很多东西)最后,我只是通过使用通配符搜索字符串的开头和结尾来查找字符串。

不管怎样,对于上面的例子,我希望得到以下输出:

American Heart Association

但我得到的是

American College of Cardiology (ACC)/American Heart Association

这是最终正则表达式的匹配项。

如果我删除最终的正则表达式或只是调用re.findall(r"(?&lt;=\W)"+ expansion, text),我会得到我想要的输出,这意味着正则表达式实际上匹配正确。

什么给了?

【问题讨论】:

  • 这是你的模式American Heart Association|(?&lt;=\W)American Heart Association|American[-\s].*?\s*?Association 见regex101.com/r/vzN62o/1。正则表达式从左到右,替代方案从左到右尝试。当遇到单词 American 的第一次出现时,前 2 个选项不匹配,而第 3 个选项匹配,这就是你得到匹配项的原因
  • 而不是使用.*?,例如,您可以不允许匹配\bAmerican\b[^/,.]*\bAssociation\bregex101.com/r/Iz83Xb/1之间的指定字符或像\bAmerican\b(?:(?!American|Association).)*\bHeart Associationregex101.com/r/kiR7xx/1这样的缓和贪婪令牌

标签: python regex nlp re


【解决方案1】:

所以re.findall(r"(?&lt;=\W)"+ expansion, text) 有效,因为在匹配之前是一个非单词字符(表示为\w),“/”。您的正则表达式将匹配“美国 [这里的任何随机内容] 心脏协会”。这意味着在匹配内部字符串“American Heart Association”之前,先匹配“American College of Cardiology (ACC)/American Heart Association”。例如。如果您删除了字符串中的第一个“American”,您将使用正则表达式获得您正在寻找的匹配项。

您需要更加严格地使用正则表达式来排除此类情况。

【讨论】:

    【解决方案2】:

    正则表达式如下所示:

    American Heart Association|(?<=\W)American Heart Association|American[-\s].*?\s*?Association
    

    前 2 个备选方案匹配相同的文本,只有第二个备选方案前面带有肯定的后视。

    您可以省略第二个替代方案,因为没有任何断言的第一个替代方案已经匹配它,或者如果第一个不匹配它,第二部分也将不匹配它。

    由于模式从左到右匹配并遇到American 的第一个匹配项,因此第一个和第二个替代方案无法匹配American College of Cardiology。

    然后第三个交替可以匹配它,并且由于.*?它可以延伸到第一次出现Association。


    例如,您可以使用negated character class 排除可能匹配的字符:

    \bAmerican\b[^/,.]*\bAssociation\b
    

    Regex demo

    或者您可以使用tempered greedy token 方法来不允许在第一部分和最后一部分之间使用特定单词:

    \bAmerican\b(?:(?!American\b|Association\b).)*\bHeart Association\b
    

    Regex demo

    【讨论】:

    • 感谢您的精彩解释和建议!我虽然正则表达式随后会通过整个序列测试每个A|B|C。猜我错了。虽然我不太清楚适用于集合中所有句子的正则表达式(必须快速获取数据),但我只是简单地使用for 循环遍历数据中的每个句子,逐渐放松正则表达式条件(基本上是在做我虽然A|B 所做的事情)。这使我能够涵盖我需要的集合中的所有示例,而不会像上面显示的那样给我无效的匹配项。
    猜你喜欢
    • 2015-02-20
    • 2021-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-20
    • 1970-01-01
    • 2011-11-18
    相关资源
    最近更新 更多