提问人:PracticingPython 提问时间:8/23/2021 最后编辑:mkrieger1PracticingPython 更新时间:8/27/2021 访问量:281
构建正则表达式以查找彼此附近的文本
Building a regular expression to find text near each other
问:
我无法使此搜索正常工作:
import re
word1 = 'this'
word2 = 'that'
sentence = 'this and that'
print(re.search('(?:\b(word1)\b(?: +[^ \n]*){0,5} *\b(word2)\b)|(?:\b(word2)\b(?: +[^ \n]*){0,5} *\b(word1)\b)',sentence))
我需要构建一个正则表达式搜索,以查找一个字符串是否在一定数量的其他单词中具有多达 5 个不同的子字符串(因此两个字符串可能相距 3 个单词,三个字符串总共相距 6 个单词,依此类推)。
我发现了很多类似的问题,例如正则表达式让 3 个单词彼此靠近。如何获取他们的上下文?或者如何在Python中检查两个单词是否相邻?,但它们都没有完全做到这一点。
因此,如果搜索词是“this”、“that”、“these”和“those”,并且它们以任何顺序出现在彼此的 9 个单词以内,则脚本将输出 True。
似乎使用各种不同的正则表达式语句编写一个 if/else 块来适应不同的排列会相当麻烦,所以我希望有一种更有效的方法来在 Python 中对此进行编码。
答:
答案已更改,因为我找到了一种仅使用正则表达式即可完成此操作的方法。方法是从前瞻开始,要求所有目标词都出现在接下来的 N 个词中。然后查找目标词的模式(按任何顺序),由 0 个或多个其他词分隔(最多允许的最大中间词)
单词跨度 (N) 是允许所有目标单词处于最大允许距离的最大单词数。
例如,如果我们有 3 个目标词,并且我们最多允许它们之间有 4 个其他词,那么最大词跨度将为 11。所以 3 个目标词加上 2 个中间系列,最多 4 个其他词 3+4+4=11。
搜索模式是通过组合依赖于单词和允许的最大中间单词数的部分而形成的。
模式:\bALL((ANY)(\W+\w+\W*){0,INTER}){COUNT,COUNT}
故障:
\b
从单词边界开始ALL
将被多个前瞻替换,以确保在接下来的 N 个单词中找到每个目标单词。- 每个 lookahead 都将具有形式,其中 WORD 是目标词,SPAN 是可能最长的词序列中的其他词的数量。每个目标词都会有一个这样的前瞻。从而确保 N 个单词的序列包含所有目标单词。
(?=(\w+\W*){0,SPAN}WORD\b)
(\b(ANY)(\W+\w+\W*){0,INTER})
匹配任何目标词,后跟 0 到 maxInter 中间词。其中,将被与任何目标词(即用管道分隔的词)匹配的模式替换。并将替换为允许的中间词数。ANY
INTER
{COUNT,COUNT}
确保上述内容的重复次数与目标词的重复次数一样多。这对应于以下模式:targetWord+intermediates+targetWord+intermediates...+targetWord- 将“展望”放在重复模式之前,我们可以保证在单词序列中拥有所有目标单词,这些单词恰好包含目标单词的数量,中间单词不会超过允许的数量。
...
import re
words = {"this","that","other"}
maxInter = 3 # maximum intermediate words between the target words
wordSpan = len(words)+maxInter*(len(words)-1)
anyWord = "|".join(words)
allWords = "".join(r"(?=(\w+\W*){0,SPAN}WORD\b)".replace("WORD",w)
for w in words)
allWords = allWords.replace("SPAN",str(wordSpan-1))
pattern = r"\bALL(\b(ANY)(\W+\w+\W*){0,INTER}){COUNT,COUNT}"
pattern = pattern.replace("COUNT",str(len(words)))
pattern = pattern.replace("INTER",str(maxInter))
pattern = pattern.replace("ALL",allWords)
pattern = pattern.replace("ANY",anyWord)
textList = [
"looking for this and that and some other thing", # YES
"that rod is longer than this other one", # NO: 4 words apart
"other than this, I have nothing", # NO: missing "that"
"ignore multiple words not before this and that or other", # YES
"this and that or other, followed by a bunch of words", # YES
]
输出:
print(pattern)
\b(?=(\w*\b\W+){0,8}this\b)(?=(\w*\b\W+){0,8}other\b)(?=(\w*\b\W+){0,8}that\b)(\b(other|this|that)\b(\w*\b\W+){0,3}){3,3}
for text in textList:
found = bool(re.search(pattern,text))
print(found,"\t:",text)
True : looking for this and that and some other thing
False : that rod is longer than this other one
False : other than this, I have nothing
True : ignore multiple words not before this and that or other
True : this and that or other, followed by a bunch of words
评论
(\b(other|this|that)\b(\w*\b\W+){0,3}){3,3}
re
import regex
这可以使用支持条件、原子组和捕获组
状态的引擎来完成,如标记、标记或 .其中 null 未定义。EMPTY
NULL
所以这几乎是所有现代引擎。有些是不完整的,比如 JS。
Python 可以使用其替换引擎 import regex
来支持这一点。
基本上,这将支持无序,并且可以限制在总共 4 到 9 个单词的最短
范围内。
底部断言已找到所有必需的项目。
在没有原子组的情况下使用它可能会导致回溯问题,但由于它
在那里,这个正则表达式非常快。(?= \1 \2 \3 \4 )
更新:添加了 LookAhead,因此它开始匹配一个特殊的单词。(?= this | that | these | those )
Python 代码
>>> import regex
>>>
>>> targ = 'this sdgbsesfrgnh these meat ball those nhwsgfr that sfdng sfgnsefn sfgnndfsng'
>>> pat = r'(?=this|that|these|those)(?>\s*(?:(?(1)(?!))\bthis\b()|(?(2)(?!))\bthat\b()|(?(3)(?!))\bthese\b()|(?(4)(?!))\bthose\b()|(?(5)(?!))\b(.+?)\b|(?(6)(?!))\b(.+?)\b|(?(7)(?!))\b(.+?)\b|(?(8)(?!))\b(.+?)\b|(?(9)(?!))\b(.+?)\b)\s*){4,9
}?(?=\1\2\3\4)'
>>>
>>> regex.search(pat, targ).group()
'this sdgbsesfrgnh these meat ball those nhwsgfr that '
General PCRE / Perl et all (same regex)
(?=this|that|these|those)(?>\s*(?:(?(1)(?!))\bthis\b()|(?(2)(?!))\bthat\b()|(?(3)(?!))\bthese\b()|(?(4)(?!))\bthose\b()|(?(5)(?!))\b(.+?)\b|(?(6)(?!))\b(.+?)\b|(?(7)(?!))\b(.+?)\b|(?(8)(?!))\b(.+?)\b|(?(9)(?!))\b(.+?)\b)\s*){4,9}?(?=\1\2\3\4)
https://regex101.com/r/zhSa64/1
(?= this | that | these | those )
(?>
\s*
(?:
(?(1)(?!))
\b this \b ( ) # (1)
|
(?(2)(?!))
\b that \b ( ) # (2)
|
(?(3)(?!))
\b these \b ( ) # (3)
|
(?(4)(?!))
\b those \b ( ) # (4)
|
(?(5)(?!))
\b ( .+? ) \b # (5)
|
(?(6)(?!))
\b ( .+? ) \b # (6)
|
(?(7)(?!))
\b ( .+? ) \b # (7)
|
(?(8)(?!))
\b ( .+? ) \b # (8)
|
(?(9)(?!))
\b ( .+? ) \b # (9)
)
\s*
){4,9}?
(?= \1 \2 \3 \4 )
评论