【问题标题】:Why doesn't this RegEx match anything?为什么这个 RegEx 不匹配任何东西?
【发布时间】:2020-05-10 02:25:45
【问题描述】:

我已经尝试了大约两个小时来编写一个匹配单个字符的正则表达式,该字符前后没有相同的字符。

这就是我得到的:(\d)(?<!\1)\1(?!\1);但它似乎不起作用! (测试https://regex101.com/r/whnj5M/6

例如:

1111223 中,我希望最后匹配3,因为它的前面或后面都没有另一个 3。

1151223 中,出于与上述相同的原因,我希望匹配中间的 5 和末尾的 3

这样做的最终目标是能够在字符串中找到成对(并且只有成对)字符(例如,在 112223 中找到 11 或在 123544 中找到 44),我打算尝试并匹配单个孤立字符,然后在其中添加 {2} 以查找对,但我什至似乎无法匹配孤立字符!

任何帮助将不胜感激,我认为我非常了解 RegEx!

附:我正在 regex101.com 上的 JS 中进行测试,因为它不允许我在 Python 中使用可变长度回溯,并且我正在使用 regex 库在我的实际实现中允许这样做。

【问题讨论】:

  • “没有先于还是先于”?您的意思是“没有先于或后于”吗?
  • 糟糕,对不起!我会解决这个问题,谢谢,我一开始写的是“没有先于或成功”,但这听起来很奇怪:P
  • 你需要使用正则表达式吗?这似乎是一个简单的for 循环更容易解决的问题。
  • @BallpointBen 我确实用普通 Python 解决了实际问题,但老实说,我认为它可能可以在正则表达式中完成,而且我有一段时间没有使用它,所以我想我会尝试它。这个问题并不是要弄清楚我的问题,而是我想知道为什么正则表达式似乎与我认为的不匹配:P
  • @LKloosterman 很公平,只要确保你没有错过明显的东西

标签: python-3.x regex


【解决方案1】:

您的正则表达式很接近,但是通过简单地使用 (\d) 您正在消耗字符,这会阻止其他匹配的发生。相反,您可以使用正向前瞻来设置捕获组,然后测试捕获的数字是否被自身的副本包围:

(?=.*?(.))(?<!\1)\1(?!\1)

通过使用前瞻,您可以避免使用任何字符,因此正则表达式可以匹配字符串中的任何位置。

请注意,在1151223 中,这将返回513,因为第三个1 不与任何其他1s 相邻。

Demo on regex101(需要支持可变宽度lookbehinds的JS)

【讨论】:

  • ...但是,但是,这是一个 Python 问题,标准的 re regex 模块不支持可变长度的lookbehinds。 Hold the phone!。 Python 的替代正则表达式引擎不支持可变长度的lookbehinds 吗?
  • @CarySwoveland 是的,regex 确实支持可变长度的lookbehinds。
【解决方案2】:

您尝试的模式不匹配,因为这部分(\d)(?&lt;!\1) 无法匹配。

它读作:

在第 1 组中捕获一个数字。然后,在捕获后的位置 数字,断言捕获的内容不应该在左边。

您可以通过在反向引用 (?&lt;!\1.) 之后添加例如一个点来使模式起作用,以断言您刚刚匹配的值之前的值与第 1 组不同

模式

 (\d)(?<!\1.)\1(?!\1)

Regex demo | Python demo

注意您已在 regex101 上选择了 ECMAscript。

Python re 不支持variable width lookbehind

要在 Python 中完成这项工作,您需要 PyPi regex module

示例代码

import regex

pattern = r"(\d)(?<!\1.)\1(?!\1)"

test_str = ("1111223\n"
    "1151223\n\n"
    "112223\n"
    "123544")

matches = regex.finditer(pattern, test_str)

for matchNum, match in enumerate(matches, start=1):
    print(match.group())

输出

22
11
22
11
44

【讨论】:

    【解决方案3】:

    @Theforthbird 很好地解释了为什么您的常规解释与感兴趣的字符不匹配。

    与下面正则表达式匹配的每个字符的前后都不是同一个字符(包括字符串开头和结尾的字符)。

    r'^.$|^(.)(?!\1)|(?<=(.))(?!\2)(.)(?!\3)'
    

    Demo

    Python 的 re 正则表达式引擎执行以下操作。

    ^.$          match the first char if it is the only char in the line
    |            or 
    ^            match beginning of line
    (.)          match a char in capture group 1...
    (?!\1)       ...that is not followed by the same character 
    |            or
    (?<=(.))     save the previous char in capture group 2... 
    (?!\2)       ...that is not equal to the next char
    (.)          match a character and save to capture group 3...
    (?!\3)       ...that is not equal to the following char
    

    假设字符串是"cat"

    • 内部字符串指针最初位于行首。
    • "c" 不在行尾,因此交替的第一部分失败并考虑第二部分。
    • "c" 匹配并保存到捕获组 1。
    • 断言"c" 后面没有捕获组1 的内容的否定前瞻成功,因此匹配"c",并且内部字符串指针前进到"c""a" 之间的位置。李>
    • "a" 未通过断言的前两部分,因此考虑了第三部分。
    • (?&lt;=(.)) 的正向后视将前面的字符 ("c") 保存在捕获组 2 中。
    • 断言下一个字符 ("a") 不等于捕获组 2 的内容的否定前瞻 (?!\2) 成功。字符串指针保留在 "a" 之前。
    • 下一个字符 ("a") 匹配并保存在捕获组 3 中。
    • 否定前瞻(?!\3),断言后面的字符("t")不等于捕获组3的内容,成功,所以"a"匹配,字符串指针前进到"t"之前.
    • 评估"t" 时执行的步骤与评估"a" 时执行的步骤相同。然而,这里最后一个令牌 ((?!\3)) 成功了,因为 "t" 后面没有字符。

    【讨论】:

    • 它在开头错过了字符
    • @PhuNgo,在我的“演示”链接中遗漏了哪些字符?
    • 1 来自1222
    • @Phu,你把我带到了那里!我会修复它。 (谢谢!)
    • @Phu,我认为现在可以了。我还对其进行了更改,以便匹配感兴趣的字符。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-07
    • 1970-01-01
    • 2017-07-12
    • 1970-01-01
    • 2021-10-24
    • 2016-06-21
    • 2017-12-20
    相关资源
    最近更新 更多