从正则表达式字符串中找到字符位置
假设我有一个字符串 'ABBACDA',我想找到每个字母 A 的位置。
如果我在 Python 中使用正则表达式,像这样:match = re.findall('A', 'ABBACDA')
,这只会返回一个列表。
有没有什么办法可以调整一下这个方法?或者我应该换个思路?我想避免使用 for x in enumerate(str)
,因为我还想检查一下,比如 'BB' 在哪里,它的起始位置是什么。
6 个回答
你可以试着测一下下面这个方法的速度,看看它是否比正则表达式的方法更快:
def multifind(needle, haystack, overlap=False):
delta = 1 if overlap else max(1, len(needle))
pos = 0
find = haystack.find
while 1:
pos = find(needle, pos)
if pos < 0: return
yield pos
pos += delta
>>> data = 'ABBACDABBmmrteyBBgfrewBBBBBioBBytA BBB ggdAdbBB BBbGtBBABGtbBbGT'
>>> list(multifind('A', data))
[0, 3, 6, 33, 42, 55]
>>> list(multifind('BB', data))
[1, 7, 15, 22, 24, 29, 35, 45, 48, 53]
>>> list(multifind('BB', data, overlap=True))
[1, 7, 15, 22, 23, 24, 25, 29, 35, 36, 45, 48, 53]
>>> list(multifind('', 'qwerty'))
[0, 1, 2, 3, 4, 5, 6]
>>>
给约翰·马钦
« (1) 在你的“find”代码中,tof是“A”,在你的正则表达式代码中,tof是“BB” »
不对。 有一段“find”代码,tof是“A”, 还有一段代码是比较“find”方法和“regex”方法,tof是“BB”。并没有单独的“regex”代码,tof是“BB”。
这段“find”代码是单独展示这个解决方案的,目的是为了让它更清晰,因为在第二段代码中可读性被模糊了。
我把pat = re.compile(tof)的定义放在循环外面,是为了不把它创建的时间算在测量的时间里。因为它在第二段代码的开头,所以你以为这是完全的“regex”代码。其实不是。这是一段比较代码。
你应该更仔细地阅读。
« (2) ch[prec:].find(tof) 而不是 ch.find(tof, prec) »
没错。
其实我早就知道索引应该放在find()里,而不是用在字符串中。我发现了这个问题,并且在之前的代码中已经用过这种方式,只是当时脑子一片空白。
顺便说一下,这样代码就简单多了。其次,正如你指出的,修正后的“find”解决方案现在运行得更快。它在T秒内完成,而正则表达式的解决方案在0.77 * T(修正前是0.65 * T)内完成:
ch = 'jggBBjgjBBBjhgBBBBjjgBBBBBjjggBBBBBBjjjgjBBBBBBB'
tof = 'BB'
L = len(tof)
X,Y = [],[]
pat = re.compile(tof)
for essay in xrange(5):
te = clock()
for i in xrange(1000):
li = []
x = ch.find(tof)
while x+1:
li.append(x)
x = ch.find(tof,x+L)
X.append( clock()-te )
te = clock()
for i in xrange(1000):
ly = [ m.start() for m in pat.finditer(ch) ]
Y.append( clock()-te )
print li,'\n',ly,'\n'
print min(X),'\n',min(Y)
不过,这并不是一个戏剧性的加速:快了18%。
« (3) 你的find代码考虑了重叠匹配,而正则表达式代码没有 »
我不这么认为。我特意用包含“BBBBB”的字符串‘ABBACDABBmmrteyBBgfrewBBBBBioBByt BBB ggddbBB BBbGtBBBGtbBbGT’进行了测试,以验证“find”解决方案和“regex”解决方案给出的结果是一样的,因为我知道正则表达式不会返回重叠匹配。我使用L = len(tof)正是为了避免检测到重叠的切片。
上述代码的结果:
[3, 8, 14, 16, 21, 23, 30, 32, 34, 41, 43, 45]
[3, 8, 14, 16, 21, 23, 30, 32, 34, 41, 43, 45]
那么“你的find代码考虑了重叠匹配”是什么意思呢?
« (4) 等等 »
这不公平。如果有等等,请说明是哪一些。如果没有……好吧,我不知道……
我觉得一个正确的评论和一个错误的、可疑的、不公平的评论的比例是1:3,这样的情况不足以让我给出差评。
而且,在Stack Overflow上,我发现那些在问题发布后1或2小时内写出的不完美答案更容易被差评,而那些类似的好答案则更少被点赞,因为提问者早就找到了自己的解决方案。
使用 re.finditer() 方法
例子 1:
match = re.finditer('A', 'ABBACDA')
for m in match:
print m.start(), m.end(), m.group(0)
输出结果:
0 1 A
3 4 A
6 7 A
例子 2:
match = re.finditer('BB', 'ABBACDA')
for m in match:
print m.start(), m.end(), m.group(0)
输出结果:
1 3 BB