为什么这个正则表达式匹配两个连续的单词不起作用?

时间:2018-03-04 21:40:04

标签: python regex

这里有一个类似的问题:Regular Expression For Consecutive Duplicate Words。这解决了如何解决这个问题的一般问题,而我正在寻找有关我的解决方案无效的具体建议。

我正在使用python正则表达式,我正在尝试匹配所有连续重复的单词,例如粗体:

  

我正在努力使这个工作

我试过了:

text = "But then of course African swallows are nonmigratory"

def lenumerate(txt):
    s = text.split(' ')
    return list(zip(s, map(len, s)))

# [('But', 3), ('then', 4), ('of', 2), ('course', 6), ('African', 7),
#  ('swallows', 8), ('are', 3), ('nonmigratory', 12)]

这是正则表达式选择背后的逻辑:[A-Za-z0-9]* {2} 应匹配任何长度的任何单词,'[A-Za-z0-9]*'使其考虑单词末尾的空格。因此'[A-Za-z0-9]* '应标记前一个单词的重复,并在末尾添加一个空格。换句话说,它说“对于任何一个词,找到在空格后立即重复的情况”。

我的逻辑在这里有什么缺陷?为什么这个正则表达式不起作用?

5 个答案:

答案 0 :(得分:5)

[A-Za-z0-9]* {2}

正则表达式中的量词将始终仅适用于它们前面的元素。因此,\d+会查找一个或多个数字,但x\d+会查找单个 x,后跟一个或多个数字。

如果您希望量词不仅适用于单一事物,则需要先对其进行分组,例如: (x\d)+。这是一个捕获组,因此它实际上会在结果中捕获它。如果您只想将事物分组以应用通用量词,这有时是不受欢迎的。在这种情况下,您可以为群组添加?:前缀,使其成为非捕获群组:(?:x\d)+

所以,回到你的正则表达式,你必须这样做:

([A-Za-z0-9]* ){2}

然而,这实际上并没有检查第二个匹配的单词是否与第一个相同。如果你想匹配,你需要使用反向引用。反向引用允许您在表达式中引用先前捕获的组,再次查找它。在您的情况下,这将是这样的:

([A-Za-z0-9]*) \1

\1将引用第一个捕获组,即([A-Za-z0-9]*)。所以小组将匹配第一个单词。然后,有一个空格,然后再次对第一个单词进行反向引用。因此,这将寻找由空格分隔的相同单词的重复。

正如博评泡泡在评论中指出的那样,仍有很多人可以做些改进正则表达式。虽然我主要关心的是解释各种概念而不过多关注你的特定例子,但我想我仍然欠你一个更健壮的正则表达式,用于匹配字符串中由空格分隔的两个连续单词。这是我对此的看法:

\b(\w+)\s\1\b

有一些与前一种方法不同的东西:首先,我正在寻找整个表达式的单词边界。 \b基本上在单词开始或结束时匹配。这将阻止表达式在其他词语中匹配,例如, <{1}}和foo fooo都不匹配。

然后,正则表达式需要至少一个字符。所以空话不会匹配。我也在这里使用foo oo这是一种更灵活的方式来包含字母数字字符。最后,我不是寻找实际的空间,而是接受单词之间的任何空格,所以这甚至可以匹配制表符或换行符。在那里添加量词也是有意义的,即\w允许多个空白字符。

当然,这对你来说效果更好,取决于你的实际要求,我们无法从你的一个例子中说出来。但是,这应该会给你一些关于如何至少继续的想法。

答案 1 :(得分:3)

您可以将先前的捕获组与第一组\1匹配,第二组匹配\2等等。

import re
s = "I am struggling to to make this this work"
matches = re.findall(r'([A-Za-z0-9]+) \1', s)
print(matches)

>>> ['to', 'this']

如果您想要两次出现,请在\1附近添加一个捕获组:

matches = re.findall(r'([A-Za-z0-9]+) (\1)', s)
print(matches)

>>> [('to', 'to'), ('this', 'this')]

答案 2 :(得分:2)

一眼就看出这会匹配任何两个单词,而不是重复单词。如果我没记错,星号(*)会匹配零次或多次,所以也许你应该使用加号(+)表示一个或多个。然后,您需要提供捕获并重新使用捕获的结果。此外,为了清楚起见,\w可用于字母数字字符。此外,\b可用于匹配单词边界处的空字符串。

以下示例中的某些内容将帮助您完成部分工作。

>>> import re
>>> p = re.compile(r'\b(\w+) \1\b')
>>> p.findall('fa fs bau saa saa fa bau eek mu muu bau')
['saa']

这些页面可能会提供一些指导:

答案 3 :(得分:1)

这应该有效:\b([A-Za-z0-9]+)\s+\1\b

\b匹配字边界,\s匹配空格,\1指定第一个捕获组。

>>> s = 'I am struggling to to make this this work'
>>> re.findall(r'\b([A-Za-z0-9]+)\s+\1\b', s)
['to', 'this']

答案 4 :(得分:1)

这是一个不使用RegEx的简单解决方案。

sentence = 'I am struggling to to make this this work'

def find_duplicates_in_string(words):
    """ Takes in a string and returns any duplicate words
        i.e. "this this"
    """
    duplicates = []
    words = words.split()

    for i in range(len(words) - 1):
        prev_word = words[i]
        word = words[i + 1]
        if word == prev_word:
            duplicates.append(word)
    return duplicates

print(find_duplicates_in_string(sentence))