【Python】正则表达式详解札记
一、什么是正则表达式?
正则表达式又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。
很多编程语言都内置了对正则表达式的支持,如Python、Perl、Java、C#、Javascript、PHP等。
常见的正则表达式使用场景有检查邮箱地址、URL有效性等等,当然还有分词(例如Java中的split函数)等。
下图展示了使用正则表达式进行匹配的流程:
正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。
二、正则表达式的使用
下面以Python语法来介绍正则表达式的用法,其他语言大同小异。正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。得益于这一点,在提供了正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同;但不用担心,不被支持的语法通常是不常用的部分。
2.1正则表达式语法介绍
下图列出了Python支持的正则表达式元字符和语法:
2.2 数量词的贪婪模式与非贪婪模式
正则表达式通常用于在文本中查找匹配的字符串。Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;非贪婪的则相反,总是尝试匹配尽可能少的字符。例如:正则表达式"ab*"如果用于查找"abbbc",将找到"abbb"。而如果使用非贪婪的数量词"ab*?",将找到"a"。
2.3 匹配模式
正则表达式提供了一些可用的匹配模式,比如忽略大小写、多行匹配等,这部分内容将在Pattern类的工厂方法re.compile(pattern[, flags])中一起介绍。
2.4 re模块
2.4.1开始使用re
Python通过re模块提供对正则表达式的支持。使用re的一般步骤是先将正则表达式的字符串形式编译为Pattern实例,然后使用Pattern实例处理文本并获得匹配结果(一个Match实例),最后使用Match实例获得信息,进行其他的操作。
# encoding: UTF-8
import re
# 将正则表达式编译成Pattern对象
pattern = re.compile(r'hello')
# 使用Pattern匹配文本,获得匹配结果,无法匹配时将返回None
match = pattern.match('hello world!')
if match:
# 使用Match获得分组信息
print (match.group())
### 输出 ###
# hello
re.compile(strPattern[, flag]):
这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。 第二个参数flag是匹配模式,取值可以使用按位或运算符'|'表示同时生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile('pattern', re.I | re.M)与re.compile('(?im)pattern')是等价的。
可选值有:
-
re.A(re.ASCII): 使\b、\B、\s、\S、\w和\W都假定字符串为ASCII(括号内是完整写法,下同)
-
re.I(re.IGNORECASE): 忽略大小写
-
re.M(MULTILINE): 多行模式,改变'^'和'$'的行为(使^在起始处并在每个换行符后匹配,使$在结尾处但在每个换行符之前匹配)
-
re.S(DOTALL): 点任意匹配模式,改变'.'的行为,使.匹配任意字符(包括换行符)
-
re.X(VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。以下两个正则表达式是等价的:
a = re.compile(r"""\d + # the integral part
\. # the decimal point
\d * # some fractional digits""", re.X)
b = re.compile(r"\d+\.\d*")
re提供了众多模块方法用于完成正则表达式的功能。这些方法可以使用Pattern实例的相应方法替代,唯一的好处是少写一行re.compile()代码,但同时也无法复用编译后的Pattern对象。如上面这个hello world例子可以简写为:
m = re.match(r'hello', 'hello world!')
print (m.group())
2.5 实例
2.5.1点号匹配
2.5.2 \匹配
2.5.3 […]匹配
2.5.4 \d、\D 匹配
2.5.5 \s、\S 匹配
2.5.6 \w、\W 匹配
2.5.7 数量词匹配
2.5.7.1 数量词贪婪模式匹配
*号(匹配前一个字符(或分组)0次或无限次)>=0:
+号(匹配前一个字符(或分组)1次或无限次)>=1:
?号(匹配前一个字符(或分组)0次或1次)0 <= x <= 1:
{m}(匹配前一个字符(或分组)m次):
{m, n}(匹配前一个字符(或分组)m至n次):
2.5.7.2 数量词非贪婪模式匹配
*?、+?、? ?、{m,n}?
2.5.8 边界匹配
^(匹配字符串的开头、多行模式下匹配每一行的开头)、$(匹配字符串的结尾、多行模式下匹配每一行的结尾)、\A(仅匹配字符串开头)、\Z(仅匹配字符串结尾)、\b(匹配任何单词边界)、\B(匹配任何非单词边界)
多行模式匹配开头和结尾
这里匹配到hello后便返回,按照理解多行模式下应该继续向下匹配,但根本不存在组1。是因为search和match方法是匹配到就返回,而不是去匹配所有。
使用findall函数就会发现的确会匹配两次,也就是继续向下匹配了一次。
2.5.9 逻辑、分组匹配
|、(…)、(?P<name>…)、\<number>、(?P=name)
2.5.9 特殊构造(不作为分组)匹配
(?:…)、(?aimsx)、(?#...)、(?=…)、(?!...)、(?<=…)、(?<!...)、(?(name/id)yes-pattern|no-pattern)
(?:…)用到(?:…|…)表示只匹配一次,所以后面的数字引用不再起作用
(?:…)用到(?:…){m}表示只匹配m次,后面的数字引用不再起作用
(?aimsx)用在正则表达式开头和re.compile(strPattern[, flag])中[, flag]作用一样
(?#...)#后的内容作为注释忽略,在正则表达式中添加注释用
(?=…)正前瞻、(?!...)负前瞻、(?<=…)正回顾、(?<!...)负回顾
报错指出正回顾需要跟固定宽度模式,修正一下。
负回顾报错一样的信息
(?(name/id)yes-pattern|no-pattern)
从上面发现no-pattern还是不能匹配,这里没有实验成功,较为疑惑。
2.6 实际使用
说说最近我对正则表达式的使用。我特意制作了两个PY小工具。
由于送出的翻译需要统计每个字符串的长度,累计大几百行字符串,这个时候看到strings.xml自然发现这些字符串非常有规律,实际上是非常规整的xml文件。可以采用两种方式实现对字符串中有用数据的提取,其一为使用标准的解析xml库去实现;其二是自己使用正则表达式去解析这些有规律的字符串。我选择后者去实现,最终直接生成excel。具体参见PY脚本。
还是继续说strings.xml中的字符串,在开发即将结束的时候,由于移植等原因或者忘记删除没用的字符串,会出现大量"垃圾字符串",这些字符串站着地方,但没有实际用途了,由于数量较多就想到使用PY脚本简单处理。首先需要使用Android Lint工具在AS下生成对应的带处理文件,将AS下的输出粘贴到TXT待处理,解析这个TXT文件,删除strings.xml中无用字符串,采用操作xml库实现。