re.findall仅匹配半数模式:为何仅提取字符串部分日期?
嘿,我来帮你捋捋这个问题!用re.findall解析年份月份却只拿到一半结果,大概率是正则写法或者匹配逻辑踩了坑,我给你列几个最常见的原因和解决办法:
贪婪匹配在搞鬼
很多新手容易栽在贪婪量词.*上——它会尽可能多的匹配字符,直接把后面的日期也“吞掉”。比如你的字符串是"2023-05 项目启动 2024-03 项目验收",如果正则写的是r'(\d{4}-\d{2}).*',findall只会返回第一个日期,因为.*把后面所有内容都匹配走了。
解决办法:要么用非贪婪量词.*?限制匹配范围,要么直接简化正则,比如只用r'\d{4}-\d{2}'来精准匹配日期格式,这样就能抓到所有符合的结果。正则匹配范围太局限
如果你的字符串里日期格式不统一(比如有的是2023/05,有的是2023-05,甚至是2023.05),但你的正则只写了\d{4}-\d{2},那格式不符的日期自然匹配不到。这时候要把所有可能的分隔符都考虑进去,比如改成r'\d{4}[-/.]\d{2}'。分组导致的返回内容偏差
re.findall有个特性:如果正则里有捕获分组(),它会优先返回分组内的内容,而不是整个匹配项。比如你写的正则是r'(\d{4})-(\d{2})',findall会返回一个包含元组的列表(每个元组是年份和月份),但如果你的字符串里有部分内容不符合这个分组结构,或者你误以为会返回完整日期,就会觉得“漏了内容”。
解决办法:如果想要完整的日期匹配结果,要么去掉分组,要么用非捕获分组(?:...),比如r'(?:\d{4}-\d{2})'。换行符没被匹配到
默认情况下,正则里的.是不匹配换行符的。如果你的字符串里有换行,而目标日期在换行后的内容里,re.findall就会漏掉这部分。这时候可以加上re.DOTALL标志,比如re.findall(pattern, your_string, re.DOTALL),让.能匹配包括换行在内的所有字符。
举个实际例子:
假设你的原始字符串是:
"季度报告1:2022-01,季度报告2:2022-06,季度报告3:2023-02"
如果用错误的正则r'(\d{4}-\d{2}).*',findall只会返回['2022-01'];改成正确的r'\d{4}-\d{2}'后,就能得到所有三个日期:['2022-01', '2022-06', '2023-02']。
内容的提问来源于stack exchange,提问作者kazooie_tooie

