用于匹配没有相同单词的关键字的正则表达式出现在另一个关键字之前

时间:2013-10-04 14:46:20

标签: python regex

我需要找到“test”一词后面跟着“follow”,而中间没有另一个“test”。

示例:

test
word
word
word
test
test
word
word
follow
word
word
test

我只想要这个:

test
word
word
word
test
**test**
**word**
**word**
**follow**
word
word
test

虽然我对正则表达式不熟悉。任何建议都会很棒。

修改 虽然单词test会在那里多次出现,但单词follow只会出现在字符串中一次。

3 个答案:

答案 0 :(得分:2)

你需要你的正则表达式在这里使用lookahead

test(?:\w|\s(?!test))+?follow

(?:)是一个非捕获组。 \w匹配任何字符[a-zA-Z0-9_]\s匹配任何空格(包括新行)。 \s(?!test)仅匹配未跟test的换行符(在正则表达式用法中称为否定前瞻)。 ()+?只会使比赛变得非贪婪。

使用匹配项测试输入:

test
word
**test**
**word**
**follow**
word
test
**test**
**word**
**word**
**follow**
word
word
**test**
**word**
**follow**

<小时/> 以下正则表达式也消除了任何子字符串匹配(如测试中的测试,抗议等)。

(?<!\w)(test)\s(?!\1\s)(?:\w|\s(?!\1\s))*?(?<!\w)follow(?!\w)

答案 1 :(得分:1)

为了简单起见,我个人不会在这里使用正则表达式:

text = (
"""test
word
word
word
test
test
word
word
follow
word
word
test
"""
)

def find_patterns(text):
    curr = []
    for word in text.split('\n'):
        if word == 'test':
            curr = ['word']  # start found sequence (also resets an existing one)
        else:
            if curr:  # if a sequence has been started by 'test'
                curr.append(word)  # otherwise just add to current sequence
                if word == 'follow':  # end of sequence
                    yield curr  # yield one result
                    curr = []  # reset sequence

print list(find_patterns(text))

输出:

 [['test', 'word', 'word', 'follow']]

答案 2 :(得分:0)

在某些情况下,Ravi的正则表达式会产生错误的结果 例如:

import re
s = """test
word 1
word 2
word 3
test 
tutulululalalo
testimony
word A
word B
follow
word X
word Y
test
"""

pat = ('test(?:\w|\s(?!test))+?follow')
print re.findall(pat,s)
#
#result : ['testimony\nword A\nword B\nfollow']

模式必须是:

pat = ('test(?=\s)'  '(?:\w|\s(?!test(?=\s)))+?'  'follow')
print re.findall(pat,s)
#
#result : ['test \ntutulululalalo\ntestimony\nword A\nword B\nfollow']

此外,我没有看到OR表达的兴趣。这有效:

pat = ('(test(?=\s)'  '(?:.(?!test(?=\s)))+?'  'follow)')
print re.findall(pat,s,re.DOTALL)
#
#result : ['test \ntutulululalalo\ntestimony\nword A\nword B\nfollow']

最后,我更喜欢以下模式,因为它只通过一次验证开始'test'和结束'follow'之间没有'test', '(?:\w|\s(?!test(?=\s)))+?''(?:.(?!test(?=\s)))+?'验证每个字符是否跟随'follow':

pat = ('test(?=\s)'
       '(?!.+?test(?=\s).*?follow)'
       '.+?'
       'follow')
print re.findall(pat,s,re.DOTALL)
#
#result : ['test \ntutulululalalo\ntestimony\nword A\nword B\nfollow']

编辑1

正如Ravi Thapliyal指出的那样,我的最后一个正则表达式

pat = ('test(?=\s)'
       '(?!.+?test(?=\s).*?follow)'
       '.+?'
       'follow')  

也不完美。
我尝试过这种模式,因为我从不喜欢模式(?!.(?=something))+
我的上一个正则表达式模式应该取代这个不受欢迎的模式 嗯,它不起作用,我努力使它工作没有成功,虽然在我看来,曾经有一段时间我使用这种模式与微妙的附加部分,使其工作。
唉,我没有成功,我想我会放弃它可能会在某一天工作的想法 因此,我决心放弃我的古老想法,并且明确地认为我从未喜欢的模式是最明显,可理解和依旧的模式。

现在我有第二个错误承认:我发现Ravi Thapliyal的正则表达式在某些情况下不起作用,但我没有想到所有可能的失败案例。
但是它很容易纠正;我不应该只用一个先行断言来编写test(?=\s),而应该使用lookbehind和lookahead断言编写(?<=\s)test(?=\s)

Ravi Thapliyal选择写(?<!\w)(test)\s(?!\1\s),但这篇文章有一些缺点:
- 必须(?!\\1\s)而不是(?!\1\s)
- 它要求在捕获组中定义(test),然后整个匹配不能简单地列在re.findall()列表中或使用re.finditer()生成器生成

他还写了(?:\w|\s(?!\\1\s))*?。我没有看到将模式(?!.(?=something))+与OR表达式复杂化的兴趣,而使用点可以做同样的工作。

另外,对我来说,最默认的一点是Ravi的正则表达式模式在包含其他字符的字符串中不能与用\w

表示的字符串匹配

由于所有这些原因,我提出以下纠正的解决方案:

import re

s1 = """test
word 1
word 2
test
word 3
tutulululalalo
protest
testimony
word
unfollow
word B
follow
word X
test
word Y
follow
"""

s2 = """test
word 1
word 2
test
word 3
tutulululalalo
protest
testimony
word ???????
unfollow
word B
follow
word X
test
word Y
follow
"""

# eyquem's pattern
fu = '(?<=\s)%s(?=\s)'
a = fu % 'test'
z = fu % 'follow'
pat = ('%s'
       '(?:(?!%s).)+?'
       '%s'
       % (a,a,z))

# Ravi's pattern
patRT = ('(?<!\w)(test)\s'
         '(?:\w|\s(?!\\1\s))*?(?<!\w)follow(?!\w)')


for x in (s1,s2):
    print x
    print re.findall(pat,x,re.DOTALL)
    print
    print [m.group() for m in re.finditer(patRT,x)]
    print

结果

test
word 1
word 2
test
word 3
tutulululalalo
protest
testimony
word
unfollow
word B
follow
word X
test
word Y
follow

['test\nword 3\ntutulululalalo\nprotest\ntestimony\nword\nunfollow\nword B\nfollow', 'test\nword Y\nfollow']

['test\nword 3\ntutulululalalo\nprotest\ntestimony\nword\nunfollow\nword B\nfollow', 'test\nword Y\nfollow']

test
word 1
word 2
test
word 3
tutulululalalo
protest
testimony
word ???????
unfollow
word B
follow
word X
test
word Y
follow

['test\nword 3\ntutulululalalo\nprotest\ntestimony\nword ???????\nunfollow\nword B\nfollow', 'test\nword Y\nfollow']

['test\nword Y\nfollow'

编辑2

准确回答问题:

s = """test
word 1
word 2
test
word 3
tutulululalalo
protest
testimony
word ???????
unfollow
word B
follow
word X
test
word Y
follow
"""
fu = '(?<=\s)%s(?=\s)'
a,z = fu % 'test' ,  fu % 'follow'
pat = ('%s'
       '(?:(?!%s).)+?'
       '%s'
       % (a,a,z))

def ripl(m):
    return re.sub('(?m)^(.*)$','**\\1**',m.group())

print re.sub(pat,ripl,s,flags=re.DOTALL)

ripl()是用于执行替换的函数,它以RegexMatch对象的形式接收每个匹配,并返回转换后的部分,然后re.sub()使用该部分进行替换< / p>