Python中使用递增数字序列的正则表达式

10

假设我有一个字符串:

teststring =  "1.3 Hello how are you 1.4 I am fine, thanks 1.2 Hi There 1.5 Great!" 

That I would like as:

testlist = ["1.3 Hello how are you", "1.4 I am fine, thanks 1.2 Hi There", "1.5 Great!"]

基本上,只在递增的数字上拆分,差值为0.1(即1.2到1.3)。

有没有一种方法可以使用正则表达式拆分,但仅捕获递增的连续数字?我使用Python编写了代码,使用自定义re.compile()依次迭代每个数字,它还可以,但非常难以处理。

类似于这样(其中parts1_temp是字符串中给定的x.x.数字列表):

parts1_temp = ['1.3','1.4','1.2','1.5']
parts_num =  range(int(parts1_temp.split('.')[1]), int(parts1_temp.split('.')[1])+30)
parts_search = ['.'.join([parts1_temp.split('.')[0], str(parts_num_el)]) for parts_num_el in parts_num]
#parts_search should be ['1.3','1.4','1.5',...,'1.32']

for k in range(len(parts_search)-1):
    rxtemp = re.compile(r"(?:"+str(parts_search[k])+")([\s\S]*?)(?=(?:"+str(parts_search[k+1])+"))", re.MULTILINE)
    parts_fin = [match.group(0) for match in rxtemp.finditer(teststring)]

但这太丑陋了。有没有更直接的正则表达式方法来做到这一点?我想这是某个人在某个时候使用正则表达式时想要的功能,但我找不到任何解决方法(也许纯正则表达式不可能)。


2
但是它真的很丑。是的...使用单个正则表达式它会更丑陋! - ctwheels
哈哈,也许吧。我离正则表达式专家非常非常远,所以我不知道。 - sfortney
"1.3 ..... 1.4 ..... 1.2 ...... 1.3....." 这种情况怎么处理?你会匹配第二个1.3吗? - Preston Martin
2
我建议两个步骤:(1)使用正则表达式进行过度生成,(2)后处理以修复错误。即,在每个x.x出现之前拆分(用包含\d\.\d的正则表达式),然后检查相邻部分的成对内容以重新连接被错误地拆分的内容。 - lenz
之后你可能会发现这个对学习很有用 - revo
显示剩余3条评论
3个回答

3

仅使用正则表达式完成此操作似乎过于复杂。以下是一种处理方式:

import re

teststring =  "1.3 Hello how are you 1.4 I am fine, thanks 1.2 Hi There 1.5 Great!" 
res = []
expected = None
for s in re.findall(r'\d+(?:\.\d+)?|\D+', teststring):
    if s[0].isdigit() and expected is None:
        expected = s
        fmt = '{0:.' + str(max(0, len(s) - (s+'.').find('.') - 1)) + 'f}'
        inc = float(re.sub(r'\d', '0', s)[0:-1] + '1')
    if s == expected:
        res.append(s)
        expected = fmt.format(float(s) + inc)
    elif expected:
        res[-1] = res[-1] + s

print (res)

即使数字有两位或更多的小数,或者没有小数,这也适用。


只是提供信息,这似乎无法从8.9转到8.10。我可能错了,但我认为是正确的。 - sfortney
它会从8.9变成9.0,我认为这是预期的结果(你写道:“差异为.1”)。如果必须不同,则逻辑必须稍作更改。但无论如何,我想您已经得到了答案,因为您已经接受了一个答案;-) - trincot

2
该方法使用finditer查找所有\d+\.\d+的位置,然后测试匹配是否比先前的数字大。如果测试为true,则将索引附加到indices数组中。
最后一行使用列表推导式,如此答案中所示,在给定的这些索引上拆分字符串。

原始方法

该方法确保先前的匹配小于当前匹配。它不是按顺序工作,而是基于数字大小工作。因此,假设字符串具有数字1.1、1.2、1.4,则会在每个出现的位置拆分,因为每个数字都比上一个大。 在此处使用的代码
import re

indices = []
string =  "1.3 Hello how are you 1.4 I am fine, thanks 1.2 Hi There 1.5 Great!"
regex = re.compile(r"\d+\.\d+")
lastFloat = 0

for m in regex.finditer(string):
    x = float(m.group())
    if lastFloat < x:
        lastFloat = x
        indices.append(m.start(0))

print([string[i:j] for i,j in zip(indices, indices[1:]+[None])])

输出:['1.3 你好吗', '1.4 我很好,谢谢 1.2 嗨', '1.5 太棒了!']


编辑

顺序方法

这种方法与原始方法非常相似,但是在1.1、1.2、1.4的情况下,它不会在1.4处分割,因为它不按照给定的.1顺序分隔符进行顺序排列。

以下方法仅在if语句中有所不同,因此这个逻辑可以根据您的需求进行自定义。

在此处查看使用的代码

import re

indices = []
string =  "1.3 Hello how are you 1.4 I am fine, thanks 1.2 Hi There 1.5 Great!"
regex = re.compile(r"\d+\.\d+")
lastFloat = 0

for m in regex.finditer(string):
    x = float(m.group())
    if (lastFloat == 0) or (x == round(lastFloat + .1, 1)):
        lastFloat = x
        indices.append(m.start(0))

print([string[i:j] for i,j in zip(indices, indices[1:]+[None])])

@sfortney,它不是在每个 x.x 上拆分,而是在 x.x 大于最后一个时拆分。 - ctwheels
1
@sfortney 哦,我明白了!给我几分钟来修复它。抱歉,我没有看到你只想要增加0.1。 - ctwheels
1
@sfortney请看我的修改。我已经改成了顺序执行的方式。我保留了原始内容,以防对未来的读者有帮助。 - ctwheels
1
很遗憾,正则表达式不知道什么是顺序的。它是在正则表达式中使序列工作的黑客技巧。请参见我的答案,了解如何在正则表达式中匹配连续字母。 - ctwheels
1
@sfortney你需要将正则表达式更改为Section (\d+\.\d+),并设置x = float(m.group(1)),而不是m.group(),如此处所示(https://tio.run/##TZCxbsMgFEXn8BW3nkCxUNw0S6Subbp06uZ4QMmzTWoDwkRN@vPuo8mQBT3g3sMR4Zp679bzbMfgY0IkIaw72gNNeEXdiClF6zqeUVR6jR0Ng0fvf2Ai4erPqPQLPmBGtNZRidQb9z3x6TN2Fl89cazSG7xHMumpEJE6ujAukj74MdiBZCz2x@Ve81IoMZgpvQ3eJM6shGh9xAjr8N/TbXZLFOVNS23FIsPaXJCj7qI/B6mUWNgW8gHFLAVGyUueOeWOD9dL6KpEpTJu8Shw4f39N7QJgbg16imZmOSKXxGBJZKsbzK13Z4aZGFbnrLyrw3y3i5xH@pq2yzrT@@oUQ0T5vkP)。 - ctwheels
显示剩余6条评论

2
你还可以改变字符串,使得如果数字是递增序列的一部分,则在其旁边放置一个标记。然后,你可以在该标记处进行拆分:
import re
teststring =  "1.3 Hello how are you 1.4 I am fine, thanks 1.2 Hi There 1.5 Great!" 
numbers = re.findall('[\.\d]+', teststring)
final_string = re.sub('[\.\d]+', '{}', teststring).format(*[numbers[0]]+[numbers[i] if numbers[i] < numbers[i-1] else '*'+numbers[i] for i in range(1, len(numbers))]).split(' *')

输出:

['1.3 Hello how are you', '1.4 I am fine, thanks 1.2 Hi There', '1.5 Great!']

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接