我使用正则表达式来查找文本中出现的字符串模式。一旦我发现字符串模式出现,我想在字符串之前和之后得到x个单词(x可以小到4,但如果仍然有效的话,最好是~10)。
我目前使用正则表达式来查找所有示例,但偶尔会挂起。有没有更有效的方法来解决这个问题?
这是我目前的解决方案:
sub = r'(\w*)\W*(\w*)\W*(\w*)\W*(\w*)\W*(%s)\W*(\w*)\W*(\w*)\W*(\w*)\W*(\w*)' % result_string #refind string and get surrounding += 4 words
surrounding_text = re.findall(sub, text)
for found_text in surrounding_text:
result_found.append(" ".join(map(str,found_text)))
4条答案
按热度按时间oaxa6hgo1#
我不确定这是不是你要找的:
基本上,str.partition()将字符串拆分为一个3元素元组。在这个例子中,第一个元素是特定的“分隔符”之前的所有单词,第二个元素是分隔符,第三个元素是分隔符之后的所有单词。
yrdbyhpb2#
你的模式的主要问题是,它以可选的东西开始,这会导致对字符串中每个位置进行多次尝试,直到找到匹配。尝试的次数随着文本大小和n值(前后的单词数)的增加而增加。这就是为什么只有几行文本就足以使代码崩溃的原因。
一种方法是从目标词开始开始模式,并使用lookarounds来捕捉前后的文本(或单词):
用搜索到的单词(一个文字字符串)开始一个模式,这使得它非常快(因为一个快速算法被用来在字符串中找到这个文字字符串的位置,然后只在这些位置测试模式),然后从字符串中的这个位置快速找到周围的单词。不幸的是,re模块有一些限制,不允许可变长度的lookbehind(和其他正则表达式一样)。
新的regex module支持可变长度的lookbehinds和其他有用的功能,例如存储重复捕获组的匹配的能力(方便一次获得分离的单词)。
4bbkushb3#
使用“尽可能多的重复”来创建一个正则表达式(好吧,任何东西,就此而言)是一个非常糟糕的主意。
以下解决方案的底线:对于大数据,第一种解决方案是最有效的解决方案;第二个是最接近你现在的,但规模更差。
str.index
。仅适用于整个单词,re.find
与例如r'\b%s\b'%re.escape(word)
更合适)re.finditer
在一个子串上根据slices to immutable strings by reference and not copy和Best way to loop over a python string backwards进行反向迭代。这只会变得比切片更好,当后者在CPU和/或内存方面是昂贵的-测试一些现实的例子来找出。坏了re
直接使用内存缓冲区。因此不可能在不复制数据的情况下为它反转字符串。(i for i,c in enumerate(reversed(buffer(text,0,substring_index)) if c.isspace())
(timeit
在P3 933 MHz上给出约100 ms,以完全通过100 k字符串)。或者:
第二项措施将消除第二个问题:我们将使重复的次数明确(Python Zen,koan 2),从而高度可见和可管理。
至于第一个问题,如果你 * 真的只需要“最多已知的,相同的N”* 项在每种情况下,你实际上不会做“多余的工作”,找到他们与你的字符串。
\w*\W*
->\w+\W+
。这消除了每个x*
都可以是空白匹配这一事实的主要歧义(参见上面的链接)。(\w+\W+){,10}
或等效的,匹配器将在发现你的字符串不跟随它们之前,每10个单词查找一次,然后尝试9,8等。为了在某种程度上减轻匹配器的负担,模式之前的\b
将使它只在每个单词的开头执行所有这些工作{,10}
。这不会保存单个单词,但对于大文本应该会明显更快(请参阅上面关于匹配如何工作的内容)。一旦我们得到了文本块,我们总是可以更详细地解析检索到的文本块(下一项中有正则表达式)。或(\w+\W+)?
受到与上述相同的歧义。为了明确起见,表达式必须像这样(为了简洁起见,这里是w=(\w+\W+)
):(w(w...(ww?)?...)?)?
(并且所有组都需要是非捕获的)。ygya80vv4#
我个人认为使用text.partition()是最好的选择,因为它消除了混乱的正则表达式,并自动将输出留在易于访问的元组中。