使用前导/ lookbehind拆分字符串,空字符串

时间:2017-06-23 21:05:59

标签: python regex lookahead

我试图在每个3.14159265358979323846264338327950288419710组之后立即拆分任何数字字符串,例如0。但是,我想在每组之后保持0。

例如,字符串10203040506070809011应拆分为

['10', '20', '30', '40', '50', '60', '70', '80', '90', '11']

并且字符串3.1415926535897932384626433832795028841971应该分成

['3.14159265358979323846264338327950', '28841971']

我尝试将字符串拆分为正后观和空字符串:

import re
p = '(?<=0+)'

re.search(p, '102030405')
><_sre.SRE_Match object; span=(2, 2), match=''>

'102030405'.split(p)
>['102030405']

但这根本不会拆分字符串,即使模式匹配。

我还尝试根据0拆分字符串,并在前几个字符串后添加0,但它似乎错综复杂且效率低下。

l = '102030405'.split('0')
[e+'0' for e in l[:-1]] + [l[-1]]
>['10', '20', '30', '40', '5']

有没有办法根据空字符串的前瞻或后看来分割字符串?我询问一般情况,而不仅仅是数字。例如,如果我想将3:18am5:19pm10:28am拆分为不同时间而不会丢失ampm,并获得数组['3:18am', '5:19pm', '10:28am'],我将如何进行此操作?

4 个答案:

答案 0 :(得分:1)

使用re.findall

l = re.findall(r'(?<![^0])[1-9.]+0*', s)

关键是要使用双重否定:不在之前,不是零(匹配前面的零或字符串的开头)

答案 1 :(得分:1)

Python split需要非零宽度匹配。

您可以使用findall与此正则表达式来获取匹配项:

>>> print re.findall(r'([\d.]+?(?:0+|$))', '10203040506070809011')
['10', '20', '30', '40', '50', '60', '70', '80', '90', '11']

>>> print re.findall(r'([\d.]+?(?:0+|$))', '3.1415926535897932384626433832795028841971')
['3.14159265358979323846264338327950', '28841971']

([\d.]+?(?:0|$))匹配以0或行尾结尾的数字或点。

<强>更新

但是,我从您编辑过的问题和评论中注意到,您正在寻找通用解决方案,以便使用零宽度正则表达式进行拆分操作。

我建议你在python中安装非常有用的regex module。此模块的第1版提供了大多数PCRE功能,远远超过了默认的re模块。

安装非常简单。只需从上面的链接下载tar gzip文件,然后运行:

sudo python setup.py install

从解压缩tar文件后获取的目录内部。 (在安装过程中忽略一些警告)。

安装regex后,只需使用以下代码:

>>> import regex

>>> regex.DEFAULT_VERSION = regex.VERSION1

>>> regex.split(r'(?<=[ap]m)(?=.)', '3:18am5:19pm10:28am')
['3:18am', '5:19pm', '10:28am']

>>> print regex.split(r'(?<=0)(?=[1-9])', '10203040506070809011')
['10', '20', '30', '40', '50', '60', '70', '80', '90', '11']

>>> print regex.split(r'(?<=0)(?=[1-9])', '3.1415926535897932384626433832795028841971')
['3.14159265358979323846264338327950', '28841971']

>>> print regex.split(r'(?<=0)(?=[1-9])', '10020')
['100', '20']

答案 2 :(得分:1)

re.findall中的这个简单正则表达式就足够了:

l = re.findall(r'[.1-9]+(?:0+|$)', s)

注意:

  • findall会在字符串中返回所有非重叠匹配作为字符串

  • 对于每个匹配,我们希望最长的数字串(或点)以至少一个零结尾,或者字符串的结尾

  • 最后的零不应该被捕获为另一个匹配(因此(?:...

同样适合你的第二个例子:

>>> re.findall(r'[\d:]+(?:am|pm|$)', '3:18am5:19pm10:28am')
['3:18am', '5:19pm', '10:28am']

无需前瞻/后视魔法或非贪婪匹配。

答案 3 :(得分:0)

阿努巴瓦的回答是对的。但是需要安装regex模块,不需要。

import re
pattern = r"(?<=0)(?=[1-9])"
s = "3.1415926535897932384626433832795028841971"
s2 = "10203040506070809011"
re.split(pattern, s)
# ['3.14159265358979323846264338327950', '28841971']
re.split(pattern, s2)
# ['10', '20', '30', '40', '50', '60', '70', '80', '90', '11']

您应该查看 re 模块页面以获取有关前瞻和后瞻的更多详细信息。 如果我要解释的话。我会说

(?<=...) 表示分隔符之前的内容。

(?=...) 表示分隔符之后的内容。

因此,(?<=0)(?=[1-9]) 表示在分隔符之前,这是空的,应该有一个零,而在它之后应该有一个 1 到 9。

regex 和 re 的速度比较。

<头>
表达 时间
re.split(r"(?<=0)(?=[1-9])", s) 5.78 纳秒 ± 0.103 纳秒
regex.split(r"(?<=0)(?=[1-9])", s) 6.04 纳秒 ± 0.364 纳秒
re.split(r"(?<=0)(?=[1-9])", s2) 5.83 纳秒 ± 0.061 纳秒
regex.split(r"(?<=0)(?=[1-9])", s2) 6.34 纳秒 ± 1.16 纳秒