【问题标题】:Python - Parse strings with variable repeating substringPython - 使用可变重复子字符串解析字符串
【发布时间】:2015-02-26 22:09:20
【问题描述】:

我正在尝试做一些我认为很简单(并且可能是)的事情,但是我碰壁了。我有一个包含文档编号的字符串。在大多数情况下,格式为 ######-#-###,但在某些情况下,应为单个数字,但有多个单个数字分隔用逗号(即######-#,#,#-###)。由逗号分隔的个位数是可变的。下面是一个例子:

对于下面的字符串:

('030421-1,2-001 & 030421-1-002,030421-1,2,3-002, 030421-1-003')

我需要返回:

['030421-1-001', '030421-2-001' '030421-1-002', '030421-1-002', '030421-2-002', '030421-3-002' '030421-1-003']

我只返回了与 ######-#-### 模式匹配的字符串:

import re
p = re.compile('\d{6}-\d{1}-\d{3}')
m = p.findall('030421-1,2-001 & 030421-1-002,030421-1,2,3-002, 030421-1-003')
print m

提前感谢您的帮助!

马特

【问题讨论】:

  • 我不知道findall func 会如何修改你的代码。

标签: python regex string parsing substring


【解决方案1】:

大概是这样的:

>>> import re
>>> s = '030421-1,2-001 & 030421-1-002,030421-1,2,3-002, 030421-1-003'
>>> it = re.finditer(r'(\b\d{6}-)(\d(?:,\d)*)(-\d{3})\b', s)
>>> for m in it:
    a, b, c = m.groups()
    for x in b.split(','):
        print a + x + c
...         
030421-1-001
030421-2-001
030421-1-002
030421-1-002
030421-2-002
030421-3-002
030421-1-003

或者使用列表推导

>>> [a+x+c for a, b, c in (m.groups() for m in it) for x in b.split(',')]
['030421-1-001', '030421-2-001', '030421-1-002', '030421-1-002', '030421-2-002', '030421-3-002', '030421-1-003']

【讨论】:

  • 太棒了!那个顶级的例子成功了。我在大约 400 条记录的测试用例上运行它,它完全符合我的需要。非常感谢!
  • 令人印象深刻。非常好。
【解决方案2】:

使用'\d{6}-\d(,\d)*-\d{3}'

* 表示“任意数量(包括 0)”。 它应用于前一个元素,这里是'(,\d)'

【讨论】:

  • 您好,感谢您的快速回复。我试过了,但它没有返回我所期望的。它返回以下内容:[',2', '', ',3', '']
  • 是的,它返回不同的匹配项,即不同括号内的匹配项。如果你在整个正则表达式周围添加括号:('\d{6}-\d(,\d)*-\d{3}'),[x[0] for x in m] 会给你你想要的。
  • 感谢serialk,看起来我们越来越近了,但还不是很远。添加括号结果为: [('030421-1,2-001', ',2'), ('030421-1-002', ''), ('030421-1,2,3-002', ',3'), ('030421-1-003', '')]
  • 你试过 [x[0] for x in m] 吗?这将为您提供每个元组的第一个元素。
【解决方案3】:

我不会使用单个正则表达式来尝试解析它。由于它本质上是一个字符串列表,您可能会发现在字符串中用逗号全局替换“&”更容易,然后使用 split() 将元素放入列表中。

执行列表循环将允许您编写一个函数来解析和修复字符串,然后您可以将其推送到新列表并显示您的字符串。

replace(string, '&', ',')
initialList = string.split(',')
for item in initialList:
    newItem = myfunction(item)
    newList.append(newItem)

newstring = newlist(join(','))

【讨论】:

  • 不是真正的问题,格式很容易与正则表达式匹配。我没有投反对票,因为你的提议很有意义,但我认为这不是最好的建议。 OP 没有指定格式,所以一般情况可能比你想象的要复杂。
  • 感谢您的回复。 Serialk 是正确的,一般情况可能要复杂得多。唯一给出的是该字符串将包含格式为######-#-### 或###-#(,# 任意次数)-### 的子字符串。整个字符串中可能包含所有其他类型的字符和文本(例如 '0122.03、0652.2 和 0652.5,用于电力线用途的 EASEMENT 的分配和承担,(到期日期为 2085 年 7 月 17 日,记录为 INST NO.167831 ON 85 年 7 月 30 日,另见 030421-2-010) 见 030421-2-020 和 030421-1-XXX'
【解决方案4】:

(\d{6}-)((?:\d,?)+)(-\d{3})

我们采用 3 个捕获组。我们以简单的方式匹配第一部分和最后一部分。中心部分可选地重复并且可选地包含','。然而,正则表达式只会匹配最后一个,所以?: 根本不会存储它。剩下的结果如下:

>>> p = re.compile('(\d{6}-)((?:\d,?)+)(-\d{3})')
>>> m = p.findall('030421-1,2-001 & 030421-1-002,030421-1,2,3-002, 030421-1-003')
>>> m
[('030421-', '1,2', '-001'), ('030421-', '1', '-002'), ('030421-', '1,2,3', '-002'),  ('030421-', '1', '-003')]

您必须手动处理第二个术语以将它们拆分并加入它们,但列表理解应该能够做到这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-26
    • 2011-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-03
    • 1970-01-01
    • 2021-12-11
    相关资源
    最近更新 更多