【问题标题】:python complementing a complex regular expressionpython补充一个复杂的正则表达式
【发布时间】:2017-08-15 01:58:40
【问题描述】:

尝试学习正则表达式,尽管这里有一些很棒的帖子和一个 regEx 网站的链接,但我有一个案例我试图摆脱纯粹的固执,无法产生我正在寻找的匹配。要理解它,请考虑以下代码,它允许我们传入一个字符串列表和一个模式,并找出该模式是匹配列表中的所有项目还是一个都不匹配:

import re
def matchNone(pattern, lst):
    return not any([re.search(pattern, i) for i in lst])

def matchAll(pattern, lst):
    return all([re.search(pattern, i) for i in lst])

为了帮助调试,这个简单的代码允许我们将 _test 添加到函数调用中,然后查看传递给 any()all() 函数的内容,最终返回结果:

def matchAll_test(pattern, lst):
    return [re.search(pattern, i) for i in lst]

def matchNone_test(pattern, lst):
    return ([re.search(pattern, i) for i in lst])

此模式和列表从matchAll() 生成True

wordPattern = "^[cfdrp]an$"
matchAll(wordPattern, ['can', 'fan', 'dan', 'ran', 'pan']) # True

表面上的这种模式似乎与matchNone() 一起工作,我们正在努力扭转这种模式:

wordPattern = "^[^cfdrp]an|[cfdrp](^an)$"
matchNone(wordPattern, ['can', 'fan', 'dan', 'ran', 'pan']) # True

它像我们希望的那样返回True。但是,这种模式的真正反转将返回 False 以获得一个值列表,其中没有一个与我们的原始列表 ['can', 'fan', 'dan', 'ran', 'pan'] 等效,而不管我们传递给它的其他内容。 (即“匹配除这 5 个单词之外的任何内容”)

在测试该列表中单词的哪些更改会为我们提供False 时,我们很快发现该模式并不像最初出现的那样成功。如果是这样,matchNone() 在上述列表之外的任何内容上都会失败。

这些排列有助于发现我的模式测试的缺点:

["something unrelated", "p", "xan", "dax", "ccan", "dann", "ra"]

在我对上面的探索中,我尝试了其他排列方式以及原始列表,使用_test 版本的函数并在原始单词上一次更改一个字母,或者修改一个术语或添加一个术语从上面的排列。

如果有人能找到我原始模式的真正反面,我很乐意看到它,以便我可以从中学习。

为了帮助您进行调查:

这种模式也适用于所有单词的matchAll(),但我似乎也无法创建它的逆模式:"^(can|fan|dan|ran|pan)$"

感谢您为此付出的任何时间。我希望在这里找到一位发现错误并能提出正确解决方案的 regEx 大师。

【问题讨论】:

  • inverse/reverse 是什么意思?
  • (^an) 并不意味着“除了an”。在字符类之外,^ 匹配字符串的开头(在 MULTILINE 模式下也是行的开头)。
  • 即使它确实的意思是“除了an”,你仍然不会正确地反转你的正则表达式。
  • 在这里回复所有的cmets。虽然第一个模式成功匹配了列表中的 5 个单词,但我正在寻找的“reverse”或“inverse”(但正在寻找单词)是一个匹配传递给它的所有内容的模式,除了我们样本中的 5 个单词。关于“^”上的 cmets,在各种 regEx 站点和我正在学习的课程中,这个字符有两个上下文:在模式的最开始它意味着模式的开始,但是在 [] 内部,可能在 () 内部或放在图案的中间,意思是“不是这个”。 . .
  • 我的模式测试都到了一半,但是当我通过一些给定的测试用例时,它们都失败了,所以它们不是真正的“反转”或原始的,并且在匹配的目标上失败,除了列表中有什么。如前所述,围绕这个用例可能还有其他方法,但找到我正在寻找的正则表达式等价性将有助于我掌握它的语法以及我在尝试的模式中看到的一些令人困惑的结果。跨度>

标签: python regex


【解决方案1】:

我希望我能理解你的问题。这是我找到的解决方案:

^(?:[^cfdrp].*|[cfdrp][^a].*|[cfdrp]a[^n].*|.{4,}|.{0,2})$
  • [^cfdrp].*: 如果文本不以 c、f、d、r 或 p 开头,则匹配
  • [cfdrp][^a].*:文本以 c、f、d、r 或 p 开头:如果第二个字符不是 a,则匹配
  • [cfdrp]a[^n].*:文本以 [cfdrp]a 开头:如果第三个字符不是 n,则匹配。
  • .{4,}:匹配任何超过 3 个字符的内容
  • .{0,2}:匹配任何 0、1 或 2 个字符

等于:

^(?:[^cfdrp].*|.[^a].*|..[^n].*|.{4,}|.{0,2})$

【讨论】:

  • 你能解释一下“?:” ... 在这部分语法上甚至无法获得帮助,但我想你可能已经明白了。对您的解释进行一些测试和一些逆向工程,以更好地理解和验证它,如果它按我的预期工作,很快就会接受答案。
  • 您的回答已被接受。如果您还没有这样做,如果您可以为这个问题投票,我们将不胜感激。我的问题有答案和 cmets 但没有投票,上周我几乎无法发布新问题,因为这个网站上有一个奇怪的算法。
  • @TMWP: 如果没有?:,您的用例将是相同的。它生成一个非捕获组而不是捕获组。重要的是 ^$ 之间的所有内容都在一个组中。否则类似于:(^[^cfdrp].*)|(.[^a].*)|(..[^n].*)|(.{4,})|(.{0,2}$)
  • 谢谢。我一直在学习我希望从这次交流中学到的东西。分析你提交的模式,我现在觉得我对 regEx 有了更好的理解,所以我会写更多的模式,将来可以工作。最美好的祝愿,周末愉快。
【解决方案2】:

您要做的是找到补码。对任何正则表达式执行此操作都是一个难题。没有用于补充正则表达式的内置函数。

PPCG 上有一个开放的challenge 可以执行此操作。一位comment 解释了困难:

这是可能的,但非常乏味。您需要将正则表达式解析为 NFA(例如 Thompson 算法),将 NFA 转换为 DFA(幂集构造),完成 DFA,找到补码,然后将 DFA 转换为 RE(例如 Brzozowski 的方法)。即比写一个完整的 RE 引擎稍微难一点!

有一些 Python 库可以从正则表达式转换(原始规范指的是“正则语言”,它只有文字、“或”和“星号”——比正则表达式的类型简单得多您正在考虑将 [more info here]) 转换为 NFA,转换为 DFA,对其进行补充,然后将其转换回来。这很复杂。

这是一个相关的 SO 问题:Finding the complement of a DFA?

总而言之,找到原始正则表达式的结果要简单得多,然后使用布尔求反。

【讨论】:

  • 这很有帮助,并解释了为什么随着模式变大,如果可以找到其他方法,则不应尝试答案。我认为有 5 个术语应该是可能的,并且在我输入此内容时正在评估另一个解决方案。感谢有用的 cmets。重点是更好地理解 RegEx 语法,这就是为什么要寻找与这个有限示例的实际模式匹配的原因。最良好的祝愿。
  • 这是一个implementation in Python。我在代码底部添加了注释,解释了允许哪些操作。它不是您正在寻找的补充,因为它只包括输入中的字母。此外,它并不总是完全简化的。在此,+ 表示 OR。 * 是一样的。所以a+b 的补码是(a+b)(a+b)(a+b)*+()(一个ab,后跟另一个,然后是零个或多个,或者是空字符串。)
  • 您是否在上面的“Python 实现”链接中创建了代码?如果是这样 - 那是一段令人惊叹的代码,你一定要花很长时间才能完成。我才看到这个评论。奇怪的是,我接受的人类解决方案虽然不是那么健壮(对于任何场景),但它是一个更清晰的答案,它通过了我设计的所有测试。但是理论上你的代码可以解决任何问题,即使是人类无法解决的问题,这真的令人印象深刻!
  • @TMWP 我告诉过你,它输出的不是 Python 正则表达式。你需要更彻底地阅读我所说的。正则表达式的formal definition 没有字符类、否定、非贪婪修饰符或捕获组。它只有+(类似于Python 的|)、*(与Python 的* 相同)和连接。
  • 所以如果你想要Python的a+的补码,你必须使用aa*。如果你想要 Python 的a|b,你必须使用a+b。等等。然后在收到结果后,您必须将其 back 转换为 Python 的语法。 aa*的补码是aaa*+(),意思是“2个或多个a的或空字符串”,在Python中转换回aa+|,如果字母表中唯一的字母是@,则为补码987654345@。该代码无法使用比正则表达式更大的字母表。
猜你喜欢
  • 2016-08-11
  • 2015-02-21
  • 1970-01-01
  • 1970-01-01
  • 2015-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-16
相关资源
最近更新 更多