字符串中的模式及其索引的出现

时间:2014-01-03 20:03:22

标签: python string python-3.x string-matching

如果我有一个模式'哈哈'和一个文本'哈哈哈',我希望模式的索引在[0,2,4]这样的列表中返回。 但是,如果我这样做的话

def find_pat(text, pattern)
    import re
        return[x.start() for x in re.finditer(pattern,text)]

它返回[0,4],并且无法在索引2处看到模式的重复。 如何以最pythonic和最有效的方式实现所需的输出?

4 个答案:

答案 0 :(得分:3)

使用积极的先行断言:

>>> import re
>>> def find_pat(text, pattern):
...     return [x.start() for x in re.finditer("(?={})".format(pattern), text)]
...
>>> find_pat('hahahaha', 'haha')
[0, 2, 4]
>>>

这是reference

答案 1 :(得分:3)

pattern = "haha"
text = "hahahaha"
[i for i in range(len(text)-len(pattern)+1) if text[i:].startswith(pattern)]

iter会消耗它看到的令牌,所以它会在第一场比赛中消耗前四个字母......

实际上使用前瞻的解决方案可能是正确的(基于问题而不是用例),但这也可以...

答案 2 :(得分:1)

正如Joran Beasley的回答所示,使用普通字符串函数比使用正则表达式进行静态字符串查找要快得多。

但如果N非常大并且匹配不常见,那么测试startswith N次本身可能是一个巨大的减速。此外,由于您使用的是finditer而不是findall,我怀疑您可能会担心这种情况。

您可以使用str.find充分利用这两个方面。当然,最终这与在每个点使用startswith做同样的工作 - 但是它在C中完成了这项工作,在没有匹配的情况下进行长时间的拉伸,速度提高了20倍。

另一方面,没有办法将这个重复的find包装在一个简单的循环表达式中。 (除非你在一个闭包周围使用iter构建一个复杂的包装器,但我怀疑它实际上会有所帮助。)因此,代码看起来比Joran的listcomp更复杂。当匹配非常常见时,它可能较慢(因为在这种情况下,你将大部分时间花在循环中,而显式循环语句比理解慢)。

从好的方面来说,额外的冗长意味着如何定制它更加明显。例如,如果您决定要跳过重叠匹配,只需执行i += len(pattern)而不是i += 1

def finditer(text, pattern):
    i = 0
    while True:
        i = text.find(pattern, i)
        if i == -1: return
        yield i
        i += 1

快速测试(在64位Apple CPython 2.7.5下):

In [931]: pattern = 'ha'
In [932]: text = 'hahahaha'
In [933]: %timeit [i for i in range(len(text)-len(pattern)+1) if text[i:].startswith(pattern)]
100000 loops, best of 3: 2.69 µs per loop
In [934]: %timeit list(finditer(text, pattern))
100000 loops, best of 3: 3.56 µs per loop
In [935]: text = ('hahahaha' + string.ascii_lowercase + 'ha')*100
pattern = 'ha'
In [936]: %timeit [i for i in range(len(text)-len(pattern)+1) if text[i:].startswith(pattern)]
100000 loops, best of 3: 1.74 ms per loop
In [937]: %timeit list(finditer(text, pattern))
100000 loops, best of 3: 180 µs per loop

所以,它几乎和Joran的代码一样快,即使是50%匹配的非常短的字符串也是如此;对于11%匹配的更长的字符串,它已经快了9.6倍。如果比赛更不常见,或者如果我们实际上不需要列表,显然它会赢得更大。

答案 3 :(得分:0)

我确定有更好的方法可以做到这一点。这是我第一次接受它:

编辑:我误解了这个问题 - 他试图匹配[哈哈]哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈我只会匹配[哈哈]哈哈和哈哈[哈哈],因为我认为他正在寻找独特的比赛。编程时阅读理解是一个加分。

def find_text(text,pattern):
    indexes = list()
    index = 0
    patlen = len(pattern)
    while index<=len(text)-patlen:
        if text[index:].startswith(pattern):
            indexes.append(index)
            index+=patlen
        else:
            index+=1
    return indexes