Python re.findall匹配正则所有模式返回空元组问题排查
问题排查与解决
问题复现
你编写的用于匹配给药途径的正则可正常通过语法校验,代码如下:
route = r"""[\s+|\(][iI](\.)?[vV](\.)?(\W|\s|$)? |\s intravenously|\s intravenous |[\s|\(][pP](\.)?[oO](\.)?(\W|\s|$) |\s perorally|\s?(per)?oral(ly)?|\s intraduodenally |[\s|\(]i(\.)?p(\.)?(\W|\s|$)? |\s intraperitoneal(ly)? |[\s|\(]i(\.)?c(\.)?v(\.)?(\W|\s|$)? |\s intracerebroventricular(ly)? |[\s|\(][iI](\.)?[gG](\.)?(\W|\s|$)? |\s intragastric(ly)? |[\s|\(]s(\.)?c(\.)?(\W|\s|$)? |subcutaneous(ly)?(\s+injection)? |[\s|\(][iI](\.)?[mM](\.)?(\W|\s|$)? |\sintramuscular """
使用re.search可正常匹配到首个符合要求的片段:
s = 'Pharmacokinetics parameters evaluated after single IV or IM' m = re.search(re.compile(route, re.X), s) print(m.group(0)) # 输出: ' IV '
预期使用re.findall获取全部匹配项时返回['IV', 'IM'],实际返回大量包含空字符串的元组组成的列表,与预期不符。
核心成因
问题来自两处写法错误:
- 不了解
re.findall的返回规则
只要正则里存在普通捕获分组(即未加?:前缀的(xxx)格式分组),re.findall就不会返回每次完整匹配的字符串,而是按顺序返回所有分组捕获的内容;分组未匹配到内容时就返回空字符串。你的正则里有近30个捕获分组(比如用来匹配可选点号的(\.)?、匹配后缀边界的(\W|\s|$)?等),绝大多数分组在单次匹配中不会命中,最终就会返回大量空字符串组成的元组。 - 正则本身存在逻辑错误
- 字符集用法错误:
[\s+|\(]这类写法完全不符合预期,字符集内的|、+会被当作普通字符(分别匹配竖线、加号本身),不具备“或”的逻辑能力,无法正确匹配“空白或左括号”的边界。 - 边界字符被纳入匹配范围:你用来做前后边界的空格、标点都被写进了匹配规则里,就算findall正常返回完整匹配,结果也会携带前后多余的空白,无法得到干净的
IV、IM结果。
- 字符集用法错误:
修复方法
按以下步骤调整正则即可得到预期结果:
- 把所有不需要提取内容的捕获分组,全部改成非捕获分组,也就是把
(xxx)替换为(?:xxx),避免findall抓取分组内的空值。 - 修正错误的字符集写法,推荐用零宽断言做单词边界,不会把边界字符算进匹配结果里。
- 可以在正则开头加
(?i)标记全局忽略大小写,省去[iI][vV]这类冗余写法,简化正则长度。
修复后的可运行代码示例:
import re # 修正后的正则,适配re.X模式 route = r""" (?i) # 全局忽略大小写 (?<![A-Za-z]) # 左边界:前面不能是字母,避免匹配到单词内部的片段 (?: i\.?v\.? |intravenously|intravenous |p\.?o\.? |perorally|(?:per)?oral(?:ly)?|intraduodenally |i\.?p\.? |intraperitoneal(?:ly)? |i\.?c\.?v\.? |intracerebroventricular(?:ly)? |i\.?g\.? |intragastric(?:ly)? |s\.?c\.? |subcutaneous(?:ly)?(?:\s+injection)? |i\.?m\.? |intramuscular ) (?![A-Za-z]) # 右边界:后面不能是字母 """ s = 'Pharmacokinetics parameters evaluated after single IV or IM' result = re.findall(route, s, re.X) print(result) # 输出: ['IV', 'IM'],完全符合预期
内容的提问来源于stack exchange,提问作者Despe1990
相关产品推荐
相关产品推荐

