匹配前面或后面没有数字的特定数量的数字

Match a specific number of digits not preceded or followed by digits

我有一个字符串:

string = u'11a2ee22b333c44d5e66e777e8888'

我想找到所有 k 个连续的数字块,其中 n <= k <= m

仅使用正则表达式: 比如说 n=2m=3 使用 (?:\D|^)(\d{2,3})(?:\D|$)

re.findall(u'(?:\D|^)(\d{2,3})(?:\D|$)',u'11a2ee22b333c44d5e66e777e8888')

给出此输出:

['11', '333', '66']

期望的输出:

['11', '22', '333', '44', '66', '777']

我知道有替代解决方案,例如:

filter(lambda x: re.match('^\d{2,3}$', x), re.split(u'\D',r'11a2ee22b333c44d5e66e777e8888'))

这给出了所需的输出,但我想知道第一种方法有什么问题?

好像re.findall是按顺序走的,匹配的时候跳过了前面的部分,怎么办?

注意:你在问题中显示的结果不是我得到的结果:

>>> import re
>>> re.findall(u'(?:\D|^)(\d{2,3})(?:\D|$)',u'11a2ee22b333c44d5e66e777e8888')
[u'11', u'22', u'44', u'66']

它仍然缺少一些您想要的匹配项,但不是相同的匹配项。

问题是即使 (?:\D|^)(?:\D|$) 等非捕获组不 捕获 它们匹配的内容,它们仍然 消费它。

这意味着产生 '22' 的匹配实际消耗了:

  1. e(?:\D|^) – 未捕获(但仍被消耗)
  2. 22(\d{2,3}) – 捕获
  3. b(?:\D|$) – 未捕获(但仍被消耗)

... 这样 b333.

之前就不再可以匹配了

你可以通过后视和前视语法得到你想要的结果:

>>> re.findall(u'(?<!\d)\d{2,3}(?!\d)',u'11a2ee22b333c44d5e66e777e8888')
[u'11', u'22', u'333', u'44', u'66', u'777']

这里,(?<!\d)是负向后视,检查匹配前面没有数字,(?!\d)是负向后视,检查匹配后面没有数字。至关重要的是,这些结构不会消耗任何字符串。

各种前瞻和后视结构在 Regular Expression Syntax Python 的 re 文档部分。

lookaround regex,\d{2,3} 表示 2 或 3 位数字,(?=[a-z]) 表示数字后的字母。

In [136]: re.findall(r'(\d{2,3})(?=[a-z])',string)
Out[136]: ['11', '22', '333', '44', '66', '777']

你甚至可以用函数概括它:

import re

string = "11a2ee22b333c44d5e66e777e8888"

def numbers(n,m):
    rx = re.compile(r'(?<!\d)(\d{' + '{},{}'.format(n,m) + '})(?!\d)')
    return rx.findall(string)

print(numbers(2,3))
# ['11', '22', '333', '44', '66', '777']