书接上文,在下篇中主要介绍如何在 python 中使用正则表达式实现文本的匹配和替换工作。
匹配字符串
python 的 re 模块支持正则表达式,基本步骤是使用 compile 编译正则表达式为 pattern 实例,之后使用实例匹配目标文本(一般使用方法 match 和 search)。先来看一个简单的例子,
|
|
代码第 4 行,使用正则表达式r"\d+"
compile 编译一个 pattern 的实例。
为什么要在正则表达式之前加个
r
呢?
此处的r
表示 raw,即原始输入。因为\
在 python 的字符串中也是转义字符,此处的功能与正则表达式中的功能有冲突。compile
中的表达式从输入到真正的表达式的意思执行需要经过两次解读,python 本身解读之后将字符串表达的意思传递给正则表达式,正则引擎再解读之后执行,问题就出在两次解读上。
以最终匹配\section
为例,如果不加r
使用 python 第 5 行的参数就该是\\\\section
,即pattern = re.compile("\\\\section", re.M)
。第一次解读,python 将其理解为\\section
传递给正则引擎,正则理解为要匹配\section
,如下表所示
Characters | Stage |
---|---|
\section |
希望匹配的字符串 |
\\section |
正则引擎添加反斜线 re.compile() |
"\\\\section" |
python 字符串为表示\ 字符再添加反斜线 |
所以为了匹配 1 个\
最后需要写 4 个\
,实在麻烦,**为了便利,因此使用r在字符串之前,表示去掉 Python 的解读,仅保留正则引擎的解读。**比如下表中的参数写法就能省掉很多不必要的\
,十分方便。
Regular String | Raw string |
---|---|
"ab*" |
r"ab*" |
"\\section" |
r"\section" |
"\\w+\\s+\\1" |
r"\w+\s+\1" |
re.M
是个什么东东?
上面的代码中,compile 还包含了re.M
作为一个 flag,它是MULTILINE
的缩写,表示匹配多行模式,除了多行模式之外(具体含义见下表),python 还有其他的 flag 影响表达式匹配的规则。
Flag | Meaning |
---|---|
ASCII , A |
使得 \w , \b , \s 和 \d 仅仅匹配 ASCII 文本 |
DOTALL , S |
使得 . 匹配换行符在内的所有字符 |
IGNORECASE , I |
忽略大小写 |
LOCALE , L |
使用 locale 匹配 |
MULTILINE , M |
多行匹配,影响 ^ 和 $ |
VERBOSE , X (for ‘extended’) |
忽略表达式中的空格,允许插入注释 |
需要说明的是X
模式,使用方法如下
|
|
表达式内部加入了注释,而且可以多行显示,可以使用它表示复杂的表达式,非常方便。
匹配正则表达式的方法
pattern 的对象建立之后,就是可以使用 re 的方法匹配字符串了。最重要的方法有下表中的四个。
方法或者属性 | 目的 |
---|---|
match() |
表达式是否匹配字符串的开头 |
search() |
浏览整个字符串,匹配其中任意位置 |
findall() |
匹配所有的字符子串,返回列表 |
finditer() |
匹配所有的字符子串,返回 iterator. |
运行上面的代码如下所示
|
|
sear 匹配成功,返回匹配的对象。mat 匹配失败,fin 匹配成功并且返回了字符列表。
匹配成功之后对象有如下方法
方法 | 目的 |
---|---|
group() |
返回匹配的字符串 |
start() |
返回匹配字符串的开始位置 |
end() |
返回匹配字符串的结束位置 |
span() |
返回匹配字符串开始位置和结束位置的元组 (start, end) |
group()
在上篇中讲过,它在字符捕获时候非常有用,字符的分组从 0 开始,其中第 0 个代表所有匹配的内容,1 代表匹配的第一组,以此类推。比如下面的代码
|
|
可以将group(0)
理解成没有分组符号匹配的所有字符,而之后的编号代表分组依次匹配的子串。
修改字符串
模块级别的字符串修改方法如下表所示。
方法和属性 | 目的 |
---|---|
split() |
使用 pattern 将目标字符串分割,并返回列表 |
sub() |
查找所有匹配的子串,并替换之,返回替换之后的字符串 |
subn() |
查找所有匹配的子串,并替换之,返回替换之后的字符串和次数 |
.split
(string[, maxsplit=0]) 将字符串分割,其中有参数最大分块,默认将分割成最多的块。
|
|
替换字符串
.sub
(replacement, string[, count=0]) 将 string 中所有符合 pattern 模式替换成 replacement,count 表示替换的次数。看如下的例子
|
|
方法subn()
与sub()
类似,但是它返回一个元组,其中包含替换之后的字符串,以及替换的次数,下面是另一个例子
|
|
特别地,替换可以使用分组,这在交换字符串的位置时非常有用。举例如下
|
|
我们还可以使用之前的名字分组,除了使用\number
指代第几个分组之外,还可以使用\g<number>
指代已经命名的分组。一个典型的例子如下所示
|
|
使用正则表达式
考虑使用正则表达式之前,先想想是否可以使用字符串方法就能解决问题。如果仅仅匹配和替换固定字符串,python 自带的字符串方法速度更快,效率更高,不是每个字符串的替换都需要使用正则表达式。
未尽事宜
相信以上介绍的内容能够解决大部分的字符处理问题,但是对于专业大数据处理可能还需要了解正则表达式更多的内容,比如不同语言的正则表达式语法,以及复杂正则表达式的匹配效率。这些比较深入的知识可以参考 Jeffrey E.F. Friedl 的《精通正则表达式》,里面有非常专业的解释,相信它可以帮你解决正则表达式的绝大部分疑问。