python regex
从工程实现的角度理解正则表达式正则表达式在Python里说白了就是一套字符串匹配的规则系统。本质上它定义了一种模式语言,让你能用简短的符号描述复杂的字符串结构。比如你要从一堆文本里抓出所有邮箱地址,手写循环判断字符是不是、点号、字母数字的组合,代码量会相当可观。但用正则表达式,一行模式就能搞定。这个工具最直接的价值在于处理非结构化文本。我们日常遇到的数据,真正规整成JSON或者CSV的其实不多,很多原始数据就是杂乱的字符串——日志文件、用户输入、爬虫抓回来的网页内容。正则表达式在这些场景下特别管用。比如检测用户输入的手机号格式是否正确,或者从服务器日志里提取所有IP地址和状态码,都属于它擅长的事。Python里使用正则主要是通过re模块。这个模块提供了几个核心函数。re.search()在字符串里搜索第一个匹配的位置,re.match()从字符串开头开始匹配,re.findall()返回所有匹配的结果列表,re.sub()做替换操作。模式字符串前面通常加个r,这样可以避免Python字符串转义和正则转义打架的问题。比如r\d表示匹配连续的数字,如果没有那个r,得写\\d才行。实际写正则的时候,很容易踩几个坑。一个是过度使用贪婪匹配。比如你想提取HTML标签里的内容,用.*会匹配到最后一个符号,而不是最近的。这时候需要加上?变成非贪婪:.*?。另一个常见问题是忘记考虑空值或者特殊情况。比如匹配电话号码,有人可能写\d{11},但用户输入的可能带括号或横线,比如(010)1234-5678,这就匹配不上了。所以实际写模式的时候,最好把常见变异都考虑进去,用管道符|组合多个可选模式。和同类技术相比,Python的正则实现和Perl、JavaScript、Java这些语言大体上相似,但细节上有差异。比如Python不支持Perl里某些高级特性,像递归匹配(?R)就没有。不过Python的re模块提供了re.VERBOSE模式,可以在正则里加注释和换行,这对复杂模式的可读性帮助很大。相比之下,Shell里的grep和sed虽然也支持正则,但它们支持的是更古老的POSIX标准,特性和Python不太一样,比如不支持\d这种方便的缩写。写正则的时候有几点经验值得留意。第一是性能问题。很多新手把正则写得特别长,试图在一个模式里描述所有可能性。但实际上正则引擎是回溯匹配的,模式越长、分支越多,性能就越差。碰到需要匹配复杂结构的时候,拆成几步来处理往往更好。比如从网页里提取数据,先用BeautifulSoup这种专门的HTML解析器,再用正则处理解析后的文本,比单用正则硬刚要优雅得多。第二是测试。正则这种东西,自己看半天觉得没问题,实际一跑经常出幺蛾子。平时可以准备一份测试数据,把各种边界情况都覆盖到,比如空字符串、极长的字符串、含有特殊字符的字符串。Python的re模块本身不提供专门的测试工具,但配合unittest写几个测试用例也不费什么事。另外建议养成写注释的习惯。re.VERBOSE模式下可以在正则里加#注释,这个功能平时用的人不多,但其实很实用。比如一个用来匹配中国身份证号的正则,如果不加注释,过段时间自己看都费劲。加上注释写明每一位的含义,对维护代码帮助很大。