【问题标题】:Long RegEx pattern isn't working as planned长正则表达式模式未按计划工作
【发布时间】:2021-05-14 15:58:02
【问题描述】:

我的正则表达式模式似乎不适用于 Python。此列以逗号与电子表格分隔,在逗号之间还有分隔事物的 Pipes(|)。但是,我并不担心管道。我需要使用re.split() 方法用逗号分割字符串,但是,您会在示例中注意到用户在第一个| 之前的第一项中将逗号输入到字符串中——这就是我的原因用于使用 Regex 建立要查找的模式。但是,它不能正常工作,并且可以作为初学者使用另一组眼睛。我已经通过Regex101 构建并运行正则表达式来帮助我,并且解释似乎正确,但它仍然没有返回我期望的匹配数。

我的正则表达式模式

".+\s\|\s\d\d\s\|\s\d\d\d\d\s\|\s\d\d\d\d\s\|\s.{2}\d\d\d\d\s\|\s\d+?\.\d+?,"gm

我的示例测试字符串

ICS: Basic Maintenance | 30 | 5877 | 0000 | IT0000 | 12000.0,ICS: E-Rate discount (85%) | 30 | 5877 | 0000 | IT0000 | -10200.0,ICS: Basic Maintenance | 40 | 5877 | 0000 | IT0000 | 9000.0,ICMS: E-Rate discount (85%) | 40 | 5877 | 0000 | IT0000 | -7650.0,ICS: Basic Maintenance | 20 | 5877 | 0000 | IT0000 | 13500.0,ICS: E-Rate discount (85%) | 20 | 5877 | 0000 | IT0000 | -11475.0,ICCMS: Basic Maintenance | 70 | 5877 | 0000 | IT0000 | 12000.0,ICCMS: E-Rate discount (85%) | 70 | 5877 | 0000 | IT0000 | -10200.0,ITSM: Laptops, Desktops, Computers | 30 | 4400 | IT0000 | 720400.0

我期望的匹配数:9 匹配

我得到的匹配数:1 匹配 - (0-443):我从 Regex101 导出的匹配

"
.\s\|\s\d\d\s\|\s\d\d\d\d\s\|\s\d\d\d\d\s\|\s.\d\d\d\d\s\|\s\d\.\d,
"
gm
. matches any character (except for line terminators)
+ matches the previous token between one and unlimited times, as many times as possible, giving back as needed (greedy)
\s matches any whitespace character (equivalent to [\r\n\t\f\v  ])
\| matches the character | literally (case sensitive)
\s matches any whitespace character (equivalent to [\r\n\t\f\v  ])
\d matches a digit (equivalent to [0-9])
\d matches a digit (equivalent to [0-9])
\s matches any whitespace character (equivalent to [\r\n\t\f\v  ])
\| matches the character | literally (case sensitive)
\s matches any whitespace character (equivalent to [\r\n\t\f\v  ])
\d matches a digit (equivalent to [0-9])
\d matches a digit (equivalent to [0-9])
\d matches a digit (equivalent to [0-9])
\d matches a digit (equivalent to [0-9])
\s matches any whitespace character (equivalent to [\r\n\t\f\v  ])
\| matches the character | literally (case sensitive)
\s matches any whitespace character (equivalent to [\r\n\t\f\v  ])
\d matches a digit (equivalent to [0-9])
\d matches a digit (equivalent to [0-9])
\d matches a digit (equivalent to [0-9])
\d matches a digit (equivalent to [0-9])
\s matches any whitespace character (equivalent to [\r\n\t\f\v  ])
\| matches the character | literally (case sensitive)
\s matches any whitespace character (equivalent to [\r\n\t\f\v  ])
. matches any character (except for line terminators)
\d matches a digit (equivalent to [0-9])
\d matches a digit (equivalent to [0-9])
\d matches a digit (equivalent to [0-9])
\d matches a digit (equivalent to [0-9])
\s matches any whitespace character (equivalent to [\r\n\t\f\v  ])
\| matches the character | literally (case sensitive)
\s matches any whitespace character (equivalent to [\r\n\t\f\v  ])
\d matches a digit (equivalent to [0-9])
+? matches the previous token between one and unlimited times, as few times as possible, expanding as needed (lazy)
\. matches the character . literally (case sensitive)
\d matches a digit (equivalent to [0-9])
, matches the character , literally (case sensitive)
Global pattern flags
g modifier: global. All matches (don't return after first match)
m modifier: multi line. Causes ^ and $ to match the begin/end of each line (not only begin/end of string)
0-443   ICS: Basic Maintenance | 30 | 5877 | 0000 | IT0000 | 12000.0,ICS: E-Rate discount (85%) | 30 | 5877 ...
Search reference
space
,
.+\s\|\s\d\d\s\|\s\d\d\d\d\s\|\s\d\d\d\d\s\|\s.{2}\d\d\d\d\s\|\s\d+?\.\d+?,
ICS: Basic Maintenance | 30 | 5877 | 0000 | IT0000 | 12000.0,ICS: E-Rate discount (85%) | 30 | 5877 | 0000 | IT0000 | -10200.0,ICS: Basic Maintenance | 40 | 5877 | 0000 | IT0000 | 9000.0,ICMS: E-Rate discount (85%) | 40 | 5877 | 0000 | IT0000 | -7650.0,ICS: Basic Maintenance | 20 | 5877 | 0000 | IT0000 | 13500.0,ICS: E-Rate discount (85%) | 20 | 5877 | 0000 | IT0000 | -11475.0,ICCMS: Basic Maintenance | 70 | 5877 | 0000 | IT0000 | 12000.0,ICCMS: E-Rate discount (85%) | 70 | 5877 | 0000 | IT0000 | -10200.0,ITSM: Laptops, Desktops, Computers | 30 | 4400 | IT0000 | 720400.0
ICS: Basic Maintenance | 30 | 5877 | 0000 | IT0000 | 12000.0,ICS: E-Rate discount (85%) | 30 | 5877 | 0000 | IT0000 | -10200.0,ICS: Basic Maintenance | 40 | 5877 | 0000 | IT0000 | 9000.0,ICMS: E-Rate discount (85%) | 40 | 5877 | 0000 | IT0000 | -7650.0,ICS: Basic Maintenance | 20 | 5877 | 0000 | IT0000 | 13500.0,ICS: E-Rate discount (85%) | 20 | 5877 | 0000 | IT0000 | -11475.0,ICCMS: Basic Maintenance | 70 | 5877 | 0000 | IT0000 | 12000.0,ICCMS: E-Rate discount (85%) | 70 | 5877 | 0000 | IT0000 | -10200.0,ITSM: Laptops, Desktops, Computers | 30 | 4400 | IT0000 | 720400.0
ICS: Basic Maintenance | 30 | 5877 | 0000 | IT0000 | 12000.0,ICS: E-Rate discount (85%) | 30 | 5877 | 0000 | IT0000 | -10200.0,ICS: Basic Maintenance | 40 | 5877 | 0000 | IT0000 | 9000.0,ICMS: E-Rate discount (85%) | 40 | 5877 | 0000 | IT0000 | -7650.0,ICS: Basic Maintenance | 20 | 5877 | 0000 | IT0000 | 13500.0,ICS: E-Rate discount (85%) | 20 | 5877 | 0000 | IT0000 | -11475.0,ICCMS: Basic Maintenance | 70 | 5877 | 0000 | IT0000 | 12000.0,```

【问题讨论】:

    标签: python-3.x regex split


    【解决方案1】:

    查看数据,如果您不担心管道,如果您想要 9 个匹配项,您可以使用 re.findall 匹配所有值,而不是拆分并稍微缩短模式:

    \w+:.*?\b\d+(?:\.\d+)(?=,|$)
    
    • \w+: 匹配 1+ 个单词字符和 :
    • .*? 匹配尽可能少的字符
    • \b\d+(?:\.\d+) 单词边界,匹配 1+ 位数字和可选的小数部分
    • (?=,|$) 在右边断言一个逗号或字符串的结尾

    Regex demo | Python demo

    import re
    from pprint import pprint
    
    pattern = r"\w+:.*?\b\d+(?:\.\d+)(?=,|$)"
    s = "ICS: Basic Maintenance | 30 | 5877 | 0000 | IT0000 | 12000.0,ICS: E-Rate discount (85%) | 30 | 5877 | 0000 | IT0000 | -10200.0,ICS: Basic Maintenance | 40 | 5877 | 0000 | IT0000 | 9000.0,ICMS: E-Rate discount (85%) | 40 | 5877 | 0000 | IT0000 | -7650.0,ICS: Basic Maintenance | 20 | 5877 | 0000 | IT0000 | 13500.0,ICS: E-Rate discount (85%) | 20 | 5877 | 0000 | IT0000 | -11475.0,ICCMS: Basic Maintenance | 70 | 5877 | 0000 | IT0000 | 12000.0,ICCMS: E-Rate discount (85%) | 70 | 5877 | 0000 | IT0000 | -10200.0,ITSM: Laptops, Desktops, Computers | 30 | 4400 | IT0000 | 720400.0"
    
    pprint(re.findall(pattern, s))
    

    输出

    ['ICS: Basic Maintenance | 30 | 5877 | 0000 | IT0000 | 12000.0',
     'ICS: E-Rate discount (85%) | 30 | 5877 | 0000 | IT0000 | -10200.0',
     'ICS: Basic Maintenance | 40 | 5877 | 0000 | IT0000 | 9000.0',
     'ICMS: E-Rate discount (85%) | 40 | 5877 | 0000 | IT0000 | -7650.0',
     'ICS: Basic Maintenance | 20 | 5877 | 0000 | IT0000 | 13500.0',
     'ICS: E-Rate discount (85%) | 20 | 5877 | 0000 | IT0000 | -11475.0',
     'ICCMS: Basic Maintenance | 70 | 5877 | 0000 | IT0000 | 12000.0',
     'ICCMS: E-Rate discount (85%) | 70 | 5877 | 0000 | IT0000 | -10200.0',
     'ITSM: Laptops, Desktops, Computers | 30 | 4400 | IT0000 | 720400.0']
    

    如果必须使用re.split,可以使用捕获组来保留拆分值并以逗号分隔。

    匹配中带有管道的完整模式:

    import re
    from pprint import pprint
    
    pattern = r"(\w+:[^|]+\|\s\d\d\s\|(?:\s\d{4}\s\|){2}\s.{2}\d{4}\s\|\s-?\d+(?:\.\d+)?),"
    s = "ICS: Basic Maintenance | 30 | 5877 | 0000 | IT0000 | 12000.0,ICS: E-Rate discount (85%) | 30 | 5877 | 0000 | IT0000 | -10200.0,ICS: Basic Maintenance | 40 | 5877 | 0000 | IT0000 | 9000.0,ICMS: E-Rate discount (85%) | 40 | 5877 | 0000 | IT0000 | -7650.0,ICS: Basic Maintenance | 20 | 5877 | 0000 | IT0000 | 13500.0,ICS: E-Rate discount (85%) | 20 | 5877 | 0000 | IT0000 | -11475.0,ICCMS: Basic Maintenance | 70 | 5877 | 0000 | IT0000 | 12000.0,ICCMS: E-Rate discount (85%) | 70 | 5877 | 0000 | IT0000 | -10200.0,ITSM: Laptops, Desktops, Computers | 30 | 4400 | IT0000 | 720400.0"
    
    pprint(list(filter(None, re.split(pattern, s))))
    

    输出

    ['ICS: Basic Maintenance | 30 | 5877 | 0000 | IT0000 | 12000.0',
     'ICS: E-Rate discount (85%) | 30 | 5877 | 0000 | IT0000 | -10200.0',
     'ICS: Basic Maintenance | 40 | 5877 | 0000 | IT0000 | 9000.0',
     'ICMS: E-Rate discount (85%) | 40 | 5877 | 0000 | IT0000 | -7650.0',
     'ICS: Basic Maintenance | 20 | 5877 | 0000 | IT0000 | 13500.0',
     'ICS: E-Rate discount (85%) | 20 | 5877 | 0000 | IT0000 | -11475.0',
     'ICCMS: Basic Maintenance | 70 | 5877 | 0000 | IT0000 | 12000.0',
     'ICCMS: E-Rate discount (85%) | 70 | 5877 | 0000 | IT0000 | -10200.0',
     'ITSM: Laptops, Desktops, Computers | 30 | 4400 | IT0000 | 720400.0']
    

    Python demo

    【讨论】:

    • 此模式在此 sample @Thefourthbird 上失败
    • @mraguda 但这是一个不同的数据样本。试试[^,].*?\b\d+(?:\.\d+)(?=,|$)regex101.com/r/LgNKeN/1
    • 你是对的——从模式中删除了冒号:,它起作用了。泰!
    【解决方案2】:

    不要在开始时使用贪婪匹配.+\s 匹配所有内容,而是使用惰性匹配.*?\s

    我还使用数量说明符清理了所有重复的 \d,例如{4}

    .*?\s\|\s\d{2}\s\|\s\d{4}\s\|\s\d{4}\s\|\s.{2}\d{4}\s\|\s-?\d+?\.\d+?Demo,

    其中有八个匹配项:

    0-60    ICS: Basic Maintenance | 30 | 5877 | 0000 | IT0000 | 12000.0
    60-126  ,ICS: E-Rate discount (85%) | 30 | 5877 | 0000 | IT0000 | -10200.0
    126-186 ,ICS: Basic Maintenance | 40 | 5877 | 0000 | IT0000 | 9000.0
    186-252 ,ICMS: E-Rate discount (85%) | 40 | 5877 | 0000 | IT0000 | -7650.0
    252-313 ,ICS: Basic Maintenance | 20 | 5877 | 0000 | IT0000 | 13500.0
    313-379 ,ICS: E-Rate discount (85%) | 20 | 5877 | 0000 | IT0000 | -11475.0
    379-442 ,ICCMS: Basic Maintenance | 70 | 5877 | 0000 | IT0000 | 12000.0
    442-510 ,ICCMS: E-Rate discount (85%) | 70 | 5877 | 0000 | IT0000 | -10200.0
    

    最后一部分不匹配,因为缺少与 \s\d{4}\s 的匹配项。 (下面粗体的0000)

    ITSM: Laptops, Desktops, Computers | 30 | 4400 <b>| 0000</b> | IT0000 | 720400.0

    【讨论】:

    • 谢谢你,Pranav,效果很好。懒惰的匹配和\d 的重复确实让我的模式过于复杂。替换为 {4} 很有帮助。
    猜你喜欢
    • 1970-01-01
    • 2014-04-28
    • 1970-01-01
    • 2022-01-02
    相关资源
    最近更新 更多