正则表达式和 re 模块

正则表达式

正则表达式是用来匹配字符串或者子串的一种模式,匹配的字符串可以很具体,也可以很一般化。

Python 标准库提供了 re 模块。

In [1]:
import re

re.match & re.search

re 模块中, re.matchre.search 是常用的两个方法:

re.match(pattern, string[, flags])
re.search(pattern, string[, flags])

两者都寻找第一个匹配成功的部分,成功则返回一个 match 对象,不成功则返回 None,不同之处在于 re.match 只匹配字符串的开头部分,而 re.search 匹配的则是整个字符串中的子串。

re.findall & re.finditer

re.findall(pattern, string) 返回所有匹配的对象, re.finditer 则返回一个迭代器。

re.split

re.split(pattern, string[, maxsplit]) 按照 pattern 指定的内容对字符串进行分割。

re.sub

re.sub(pattern, repl, string[, count])pattern 匹配的内容进行替换。

re.compile

re.compile(pattern) 生成一个 pattern 对象,这个对象有匹配,替换,分割字符串的方法。

正则表达式规则

正则表达式由一些普通字符和一些元字符(metacharacters)组成。普通字符包括大小写的字母和数字,而元字符则具有特殊的含义:

子表达式 匹配内容
. 匹配除了换行符之外的内容
\w 匹配所有字母和数字字符
\d 匹配所有数字,相当于 [0-9]
\s 匹配空白,相当于 [\t\n\t\f\v]
\W,\D,\S 匹配对应小写字母形式的补
[...] 表示可以匹配的集合,支持范围表示如 a-z, 0-9
(...) 表示作为一个整体进行匹配
¦ 表示逻辑或
^ 表示匹配后面的子表达式的补
* 表示匹配前面的子表达式 0 次或更多次
+ 表示匹配前面的子表达式 1 次或更多次
? 表示匹配前面的子表达式 0 次或 1 次
{m} 表示匹配前面的子表达式 m 次
{m,} 表示匹配前面的子表达式至少 m 次
{m,n} 表示匹配前面的子表达式至少 m 次,至多 n 次

例如:

  • ca*t 匹配: ct, cat, caaaat, ...
  • ab\d|ac\d 匹配: ab1, ac9, ...
  • ([^a-q]bd) 匹配: rbd, 5bd, ...

例子

假设我们要匹配这样的字符串:

In [2]:
string = 'hello world'
pattern = 'hello (\w+)'

match = re.match(pattern, string)
print match
<_sre.SRE_Match object at 0x0000000003A5DA80>

一旦找到了符合条件的部分,我们便可以使用 group 方法查看匹配的部分:

In [3]:
if match is not None:
    print match.group(0)
hello world
In [4]:
if match is not None:
    print match.group(1)
world

我们可以改变 string 的内容:

In [5]:
string = 'hello there'
pattern = 'hello (\w+)'

match = re.match(pattern, string)
if match is not None:
    print match.group(0)
    print match.group(1)
hello there
there

通常,match.group(0) 匹配整个返回的内容,之后的 1,2,3,... 返回规则中每个括号(按照括号的位置排序)匹配的部分。

如果某个 pattern 需要反复使用,那么我们可以将它预先编译:

In [6]:
pattern1 = re.compile('hello (\w+)')

match = pattern1.match(string)
if match is not None:
    print match.group(1)
there

由于元字符的存在,所以对于一些特殊字符,我们需要使用 '\' 进行逃逸字符的处理,使用表达式 '\\' 来匹配 '\'

但事实上,Python 本身对逃逸字符也是这样处理的:

In [7]:
pattern = '\\'
print pattern
\

因为逃逸字符的问题,我们需要使用四个 '\\\\' 来匹配一个单独的 '\'

In [8]:
pattern = '\\\\'
path = "C:\\foo\\bar\\baz.txt"
print re.split(pattern, path)
['C:', 'foo', 'bar', 'baz.txt']

这样看起来十分麻烦,好在 Python 提供了 raw string 来忽略对逃逸字符串的处理,从而可以这样进行匹配:

In [9]:
pattern = r'\\'
path = r"C:\foo\bar\baz.txt"
print re.split(pattern, path)
['C:', 'foo', 'bar', 'baz.txt']

如果规则太多复杂,正则表达式不一定是个好选择。

Numpy 的 fromregex()

In [10]:
%%file test.dat 
1312 foo
1534    bar
444  qux
Writing test.dat
fromregex(file, pattern, dtype)

dtype 中的内容与 pattern 的括号一一对应:

In [11]:
pattern = "(\d+)\s+(...)"
dt = [('num', 'int64'), ('key', 'S3')]

from numpy import fromregex
output = fromregex('test.dat', pattern, dt)
print output
[(1312L, 'foo') (1534L, 'bar') (444L, 'qux')]

显示 num 项:

In [12]:
print output['num']
[1312 1534  444]
In [13]:
import os
os.remove('test.dat')