使用python正则表达式进行前瞻断言和分组

使用python正则表达式进行前瞻断言和分组,python,regex,lookahead,Python,Regex,Lookahead,假设我有很多行文字,比如这一行: 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() (

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

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][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 ].*) (?=((-?\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)”,但这对我来说不起作用

另外-我对分配多个前瞻断言的正确方法感到困惑,这些断言在匹配返回之前都必须为true:

有什么不同于

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"""(?=((-?\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][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} 

第一组

也许:

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 
但是第一个测试返回这个(当第二个测试语句被注释掉时):

第二个测试语句返回这个,一个结果列表-我想要什么,sorta:

[('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')]

但是这些语句非常相似,我不知道为什么列表中缺少多个数字/标签。

您根本不需要前瞻:

(?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)(?:^ |)(?我有点困惑。
('S.G.Primary School\t\t','434612.50','434','612','.50')
你在寻找什么?理想的情况下,我想充分理解这一点,抓住('S.G.Primary School','434612.50','434','612','.50')或('S,'434612.50')。这会让我接近你,但不是“学校”本身:test=re.search(r”“)(?!\s)([a-zA-Z]{6,25})。*(?=(?:(?:-?{1,3})(?:,\d{3})*(?:,\d{3})(?:。。\d\d$)((((?\d{1,3})(,\d{3})*(\。\d\d\d\d})$),“行)所以你想将),对吗?这将捕获第一个“短语”原样:
re.search(r”“)(\s+\D+\s+(?=((((-\D{1,3})(,\D{3})*(\。\D\D)$\124; ^\。\ D\D$)))”,row)。groups()
。这就是你要找的吗?@MarcMaxson:为什么是“小学”而不是“s.G.小学”?我们还不知道;-)他没有解释完整的情况。是的,但我能从他的精神中通过网络感觉到他不需要向前看。我倾向于同意这一点;-)但假设是通往地狱的道路。根据你的回答,我发现这一点越来越近了(返回'school',但还没有返回两个字的'Primary school':r'([a-Za-z][a-Za-z.]*){1,2}\这不是我的模式。
(?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.