【问题标题】:Lookahead assertions and grouping with python regex使用 python 正则表达式的前瞻断言和分组
【发布时间】:2013-08-19 08:59:46
【问题描述】:

假设我有很多行文本,比如这一行:

row = '   S.G. Primary School\t\t 434,612.50'

我想找到一个看起来像会计师那样格式化的数字,然后我想向后看并拉出该数字前面的一个或多个单词。我有这个号码:

test = re.search(r"""(?=((-?\d{1,3})(,\d{3})*(\.\d\d)?$|^\.\d\d$))""",row)
   S.G. Primary School       434,612.50
test.groups()
('434,612.50', '434', ',612', '.50')

这看起来是正确的。我有完整的号码和它的部分(我想要的所有)。但是我无法弄清楚如何通过前瞻断言在数字之前获取单词(或短语)。

我试过了:

test = re.search(r"""([A-Za-z ].*) (?=((-?\d{1,3})(,\d{3})*(\.\d\d)?$|^\.\d\d$))""",row)
('   S.G. Primary School\t\t', '434,612.50', '434', ',612', '.50')

这周我花了 4 个小时重读 regex 文档,但我仍然不知道自己是否有所收获。示例似乎对我不起作用。我不能使用 \w+ 因为我希望标签只是文本和空格,但我也想从匹配数字的开头开始倒数。这听起来像是一个“积极的前瞻性断言”,一般格式为“\w+(?=\d)”,但这对我不起作用。

另外 - 我对分配 MULTIPLE 前瞻断言的正确方法感到困惑,即在匹配返回之前 ALL 必须为真:

r"""([A-Za-z ]*)(.*?)([\d,.]+)(?=[A-Za-z ]*)(?=[\d,.])"""

任何不同的

r"""([A-Za-z ]*)(?=[A-Za-z ]*)(.*?)([\d,.]+)(?=[\d,.])"""

因为在本例中两者产生相同的结果:

('   S', '.G. Primary School\t\t ', '434,612.5')

更新

以下是我正在努力寻找正则表达式答案的三个示例:

import re
rows = ['   S.G. Primary School\t\t 434,612.50',
       '   S.G. Bad Primary School\t\t 434,612.50',
       '   N.3#=42^2492q\t\t\t 434,612.50']

for row in rows:
    test = re.search(r"""(?!\s)([A-Za-z]{0,25}) ?([a-zA-Z]{6,25}).*?(?=(?:(?:-?\d{1,3})(?:,\d{3})*(?:\.\d\d)?$|^\.\d\d$))((-?\d{1,3})(,\d{3})*(\.\d\d)?$|^\.\d\d$)""",row)
    if test != None:
        print test.groups()
    else:
        print test

这会返回:

('Primary', 'School', '434,612.50', '434', ',612', '.50')
('Bad', 'Primary', '434,612.50', '434', ',612', '.50')
None

我希望结果是:

('Primary', 'School', '434,612.50', '434', ',612', '.50')
('Primary', 'School', '434,612.50', '434', ',612', '.50')
('', '434,612.50', '434', ',612', '.50')

我希望代码可以调整,以便我也可以返回:

('School', '434,612.50', '434', ',612', '.50')
('School', '434,612.50', '434', ',612', '.50')
('', '434,612.50', '434', ',612', '.50')

经过修改。

更新

根据 Casimir 的回答,这会返回更好的数据,但我不明白如何在数字之前获取多个词组:

test = re.search(r'([A-Za-z][A-Za-z_.]*){1,2}\s+((-?\d{1,3})(,\d{3})*(\.\d\d)?$|^\.\d\d$)',row)
('School', '434,612.50', '434', ',612', '.50')
('School', '434,612.50', '434', ',612', '.50')
('q', '434,612.50', '434', ',612', '.50')

我不知道为什么

test = re.search(r'([A-Za-z_.]*){1,2}\s+((-?\d{1,3})(,\d{3})*(\.\d\d)?$|^\.\d\d$)',row)

给出一个错误:没有什么可重复的。我所做的只是改变

[A-Za-z][A-Za-z_.]*){1,2} 

[A-Za-z_.]*){1,2}

在第一组。

也许:

test = re.search(r'([A-Za-z][A-Za-z_.]*){0,}\s+([A-Za-z][A-Za-z_.]*){0,}\s+((-?\d{1,3})(,\d{3})*(\.\d\d)?$|^\.\d\d$)',row)

更好,因为我得到了第一个单词和第二个单词,但不确定如何将它们组合起来并使其成为可选:

('Primary', 'School', '434,612.50', '434', ',612', '.50')
('Primary', 'School', '434,612.50', '434', ',612', '.50')
('q', None, '434,612.50', '434', ',612', '.50')

更新

我已将 Casimir 的答案(稍作修改){0,2} 更改为 {0,1} 并使用 findall 版本对其进行了测试:

import re
rows = ['   S.G. Primary School\t\t 434,612.50 S.G. Primary School\t\t 434,612.50',
       '   S.G. Bad Primary School\t\t 434,612.50 Bad Primary School\t\t 434,612.50',
       '   N.3#=42^2492q\t\t\t 434,612.50  N.3#=42^2492q\t\t\t 434,612.50  N.3#=42^2492q\t\t\t 434,612.50 ']

for row in rows:
    test = re.findall(r"(?i)([a-z][a-z_.]*(?:\s+[a-z][a-z_.]*){0,1})?\s+((-?\d{1,3})(?:,\d{3})*(?:\.\d\d)?$|^\.\d\d$)",row)
    test = re.findall(r"(?i)([a-z][a-z_.]*(?:\s+[a-z][a-z_.]*){0,1})?\s+(-?\d{1,3}(?:,\d{3})*(?:\.\d\d)?)",row)
    print test 

但是第一个测试返回这个(当第二个测试语句被注释掉时):

[('Primary School', '434,612.50', '434')]
[('Primary School', '434,612.50', '434')]
[]

第二个测试语句返回这个结果列表 - 我想要的,排序:

[('Primary School', '434,612.50'), ('Primary School', '434,612.50')]
[('Primary School', '434,612.50'), ('Primary School', '434,612.50')]
[('q', '434,612.50'), ('q', '434,612.50'), ('q', '434,612.50')]

但陈述是如此相似,我不知道为什么一个人缺少列表中的多个数字/标签。

【问题讨论】:

  • 我有点困惑。 (' S.G. Primary School\t\t', '434,612.50', '434', ',612', '.50') 不是你要找的吗?
  • 理想情况下,我想充分理解这一点,以便抓住 ('Primary School', '434,612.50', '434, '612', '.50') 或 ('School', '434,612.50') '、'434、'612'、'.50')。这会让我接近但不是“学校”本身: test = re.search(r"""(?!\s)([a-zA-Z ]{6,25}).*?(?=( ?:(?:-?\d{1,3})(?:,\d{3})*(?:\.\d\d)?$|^\.\d\d$))( (-?\d{1,3})(,\d{3})*(\.\d\d)?$|^\.\d\d$)""",行)
  • 所以你想匹配S.G. Primary School 而不消耗其余的行,但要检查它看起来像特定的东西(会计师的号码),对吗?
  • 这将按原样捕获第一个“短语”:re.search(r"""(\s+\D+\s+(?=((-?\d{1,3})(,\d{3})*(\.\d\d)?$|^\.\d\d$)))""",row).groups()。这就是你所追求的吗?
  • @MarcMaxson:为什么是“小学”而不是“S.G.小学”?

标签: python regex lookahead


【解决方案1】:

您根本不需要前瞻:

(?i)([a-z][a-z_.]*(?:\s+[a-z][a-z_.]*){0,2})?\s+(-?\d{1,3}(?:,\d{3})*(?:\.\d\d)?)

使用{0,...},您可以控制想要的字数。如果您想要所有单词,请将其替换为*。如果要最多一个单词,则必须删除所有非捕获组:

(?i)([a-z][a-z_.]*)?\s+(-?\d{1,3}(?:,\d{3})*(?:\.\d\d)?)

如果你想要三个字:

(?i)([a-z][a-z_.]*(?:\s+[a-z][a-z_.]*){2})\s+(-?\d{1,3}(?:,\d{3})*(?:\.\d\d)?)

如果您想避免“非单词”中的单个字母(例如“q”字母),您可以添加:

(?i)((?:^|(?<=\s))[a-z][a-z_.]*(?:\s+[a-z][a-z_.]*){0,2})?\s+(-?\d{1,3}(?:,\d{3})*(?:\.\d\d)?)

图案细节:

(?i)                      # make the pattern case insensitive
(                         # open the first capturing group
    (?:^|(?<=\s))         # begining of the string or lookbehind with space
    [a-z][a-z_.]*         # a letter and zero or more chars from [a-z_.]
    (?:                   # open a non-capturing group
        \s+               # one or more spaces
        [a-z][a-z_.]*     # a letter and zero or more chars from [a-z_.]
    ){0,2}                # repeat the capturing group zero or two times
)?                        # close the capturing group and make it optional
\s+                       # one or more spaces
(                         # open a capturing group
    -?                    # - sign optional
    \d{1,3}               # between 1 or 3 digits
    (?:,\d{3})*           # a group (zero or more times) with a , and 3 digits
    (?:\.\d\d)?           # an optional group with a . and 2 digits
)                         # close the second capturing group.

【讨论】:

  • 我们还不知道 ;-) 他没有解释完整的案例。
  • 是的,但我可以从他的精神中感受到他不需要向前看。
  • 我倾向于同意这一点 ;-) 但假设是通往地狱的道路。
  • 有了你的回答,我发现这更接近了(返回“学校”但尚未返回 2 字版本“小学”):r'([A-Za-z] [A-Za-z_.]*){1,2}\s+((-?\d{1,3})(,\d{3})*(\.\d\d)?$|^ \.\d\d$)'
  • @MarcMaxson:这不是我的模式。
猜你喜欢
  • 1970-01-01
  • 2015-10-11
  • 1970-01-01
  • 1970-01-01
  • 2020-04-15
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 2011-02-27
相关资源
最近更新 更多