匹配前面或后面没有数字的特定数量的数字
Match a specific number of digits not preceded or followed by digits
我有一个字符串:
string = u'11a2ee22b333c44d5e66e777e8888'
我想找到所有 k
个连续的数字块,其中 n <= k <= m
。
仅使用正则表达式:
比如说 n=2
和 m=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'
的匹配实际消耗了:
e
,(?:\D|^)
– 未捕获(但仍被消耗)
22
与 (\d{2,3})
– 捕获
b
和 (?:\D|$)
– 未捕获(但仍被消耗)
... 这样 b
在 333
.
之前就不再可以匹配了
你可以通过后视和前视语法得到你想要的结果:
>>> 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']
我有一个字符串:
string = u'11a2ee22b333c44d5e66e777e8888'
我想找到所有 k
个连续的数字块,其中 n <= k <= m
。
仅使用正则表达式:
比如说 n=2
和 m=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'
的匹配实际消耗了:
e
,(?:\D|^)
– 未捕获(但仍被消耗)22
与(\d{2,3})
– 捕获b
和(?:\D|$)
– 未捕获(但仍被消耗)
... 这样 b
在 333
.
你可以通过后视和前视语法得到你想要的结果:
>>> 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']