开发笔记:使用 Python 正则表达式模块时遇到的一个小问题

本贴最后更新于 1442 天前,其中的信息可能已经时移世易

Preface

今天在使用 Python3 内置的正则表达式模块,即 re 时遇到了一点小问题。因此做个记录分享出来。

正文

今天在处理某个配置文件时,需要对文件中一个不定长数组进行解析,提取部分参数。这个数组由 n 个不定长的小数组组合而来,但它仍是一维的;而小数组的前三项无需关心,后面的奇数项不相同而偶数项相同,而偶数项就是我需要的数据,并统计它们在小数组中的长度。示例如下:

arr1 = [1, 22, 56, 122, 1289, 34, 1289, 45, 1289] # (122, 34, 45), len = 3
arr2 = [2, 20, 0, 0, 1289,] # (0), len = 1
arr_to_handle = arr1 + arr2

其中小数组中的偶数项(n \ge 3)会随着文件不同而不同。

现在要提取我需要的数据,首先就是找到数据在哪。由于最后处理的数组的长度不定,因此不太方便依靠索引处理,于是我打算将其转换成字符串之后,使用正则表达式匹配我需要的区间。

经过观察得知,由各个小数组组成的大数组总是具有这样的规律:

  • 长度不确定,但所有元素都是十进制数字
  • 数组的最后一项总是一个固定不变的数据
  • 存在至少一个含有两个元素的子数组,子数组的偶次项是我需要的数据,奇次项是上一规律中不变的数据

那么将所有的元素,使用一个独特的符号(例如 /)连接起来,得到一个巨大的字符串,就可以使用正则搜索了:

arr_to_handle = [1, 22, 56, 122, 1289, 34, 1289, 45, 1289, 2, 20, 0, 0, 1289]
arr2str = '/1/22/56/122/1289/34/1289/45/1289/2/20//0/0/1289'

根据 arr2str 的内容,可以轻松写出正则表达式 r'/[+-]?\d+/1289/',但是该式只能匹配到某一个子数组,即便是使用 re.findall() 函数,也会丢失原本在小数组中的长度信息。我期望的匹配到的是 /122/1289/34/1289/45/1289/0/1289,这样字符串稍加处理即可还原长度信息。

那么对正则表达式进行改进,需要刚才示例中的 pattern 出现至少一次,那么新的正则表达式可以写作 r'(/[+-]?\d+/1289/)+',现在把新的 pattern 传递给 re.findall() 时,神奇的事情发生了,该函数只返回了 ['/45/1289', '/0/1289'],显然这不是我所期待的结果。

经过查询资料,最后发现这并不是 re.findall() 函数的错误,而我的表达式也基本正确——但是括号内的 group 被过早的捕获了,以至于无法返回正确结果,但是它的原理是正确的。详情参见 issue 42448

因此只需在匹配过程中,禁止提前捕获即可,所以只需将正则表达式修改为 r'(?:/[+-]?\d+/1289/)+' 即可,其中 ?: 表示在当前圆括号的作用域中禁用捕获。

总结

写代码也不能望文生义呀,今天被这个 bug 折腾了一天,最后终于圆满解决,唉!

  • 笔记

    好记性不如烂笔头。

    308 引用 • 793 回帖 • 1 关注
  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    543 引用 • 672 回帖 • 1 关注
  • Regex
    1 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
StephenZhang
我没有生来天赋过人,面对人山人海只剩一些诚恳 杭州