正则表达式
最简单的正则表达式
In: import re
In: re.match('test', 'test')
Out: <_sre.SRE_Match object; span=(0, 4), match='test'>
In: re.match('test', 'test123')
Out: <_sre.SRE_Match object; span=(0, 4), match='test'>
In: re.match('test', 'tes')
Out: # 无输出
match(pattern, string, flags=0) # re.match 的函数签名, pattern 是模式, string 是要匹配的文本, flags 是标志位, 用于控制正则表达式的匹配方法(比如是否区分大小写, 多行匹配等等)
search() / match() 的区别
In: re.search('test', 'test123')
Out: <_sre.SRE_Match object; span=(0, 4), match='test'>
In: re.search('test', '1test')
Out: <_sre.SRE_Match object; span=(1, 5), match='test'>
In: re.match('test', '1test')
Out: # 无输出
search(pattern, string, flags=0) # re.search 的函数签名
match() 要求从文本开头就匹配, search() 是搜索整个文本, 有匹配项即可。
match() 函数只检测 string 开始位置, 而 search() 会扫描整个 string 查找匹配。
也就是说 match() 只有在 0 位置匹配成功的话才有返回, 如果不是开始位置匹配成功的话, match 就返回 none。
search() 会扫描整个 string 并返回第一个成功的匹配。
元字符
普通字符包括大小写的字母、数字, 而元字符具有特殊的含义, 正是因为元字符, 才使得正则表达式具有非常强的表达能力。
常用的元字符:
.
: 圆点符号表示匹配除换行符以外的任意字符
\w
: 匹配字母或数字或下划线或汉字
\s
: 匹配任意的空白符, 包括空格制表符换页符等
\d
: 匹配数字
\b
: 匹配单词的开始或结束
^
: 匹配字符串的开始
$
: 匹配字符串的结束
x|y
: 匹配 x 或 y
[xyz]
: 字符集合。匹配所包含的任意一个字符
[a-z]
: 字符范围。匹配指定范围内的任意字符
In: re.search('..', 'hi') # .
Out: <_sre.SRE_Match object; span=(0, 2), match='hi'>
In: re.search('\w\w\w\w', 'a1_蛤') # \w
Out: <_sre.SRE_Match object; span=(0, 4), match='a1_蛤'>
In: re.search('\s\s\s', ' \r\t') # \s
Out: <_sre.SRE_Match object; span=(0, 3), match=' \r\t'>
In: re.search('\d\d\d', '123') # \d
Out: <_sre.SRE_Match object; span=(0, 3), match='123'>
In: re.search('^test', 'test123') # ^
Out: <_sre.SRE_Match object; span=(0, 4), match='test'>
In: re.search('^test', '123test123') # ^
Out: # 无输出
In: re.search('123$', 'test123') # $
Out: <_sre.SRE_Match object; span=(4, 7), match='123'>
In: re.search('123$', 'test123test') # $
Out: # 无输出
In: re.search('a|b', 'abcd') # x|y
Out: <_sre.SRE_Match object; span=(0, 1), match='a'>
In: re.search('[12b]', 'abcd') # [xyz]
Out: <_sre.SRE_Match object; span=(1, 2), match='b'>
In: re.search('[a-z]', 'qwe') # [a-z]
Out: <_sre.SRE_Match object; span=(0, 1), match='q'>
In: re.search('[d-z]', 'abc') # [a-z]
Out: # 无输出
匹配内容
通过匹配对象的 group()
方法, 可以获得正则表达式匹配的内容
In: match = re.search('\d+', 'hellopg12138world')
In: match.group()
Out: '12138'
重复
一个元字符只能对应匹配一个字符, 如果有重复的匹配项, 我们需要使用下面的方法来处理:
?
: 匹配前面的子表达式零次或一次
+
: 匹配前面的子表达式一次或多次
*
: 匹配前面的子表达式零次或多次
{n}
: 重复 n 次
{n, }
: 重复 n 次或更多次
{n, m}
: 重复 n 到 m 次
In: re.search('ca?t', 'ct') # ?
Out: <_sre.SRE_Match object; span=(0, 2), match='ct'>
In: re.search('ca?t', 'cat') # ?
Out: <_sre.SRE_Match object; span=(0, 3), match='cat'>
In: re.search('ca?t', 'caat') # ?
Out: # 无输出
In: re.search('ca+t', 'ct') # +
Out: 无输出
In: re.search('ca+t', 'cat') # +
Out: <_sre.SRE_Match object; span=(0, 3), match='cat'>
In: re.search('ca+t', 'caaaaaat') # +
Out: <_sre.SRE_Match object; span=(0, 8), match='caaaaaat'>
In: re.search('ca*t', 'cat') # *
Out: <_sre.SRE_Match object; span=(0, 3), match='cat'>
In: re.search('ca*t', 'cart') # *
Out: # 无输出
In: re.search('ca*t', 'caaaat') # *
Out: <_sre.SRE_Match object; span=(0, 6), match='caaaat'>
In: re.search('\d{5}', '123456') # {n}
Out: <_sre.SRE_Match object; span=(0, 5), match='12345'>
反义
[^x]
: 匹配除了 x 以外的任意字符
[^abc]
: 匹配除了 abc 这几个字符以外的任意字符
\W
: 匹配任意不是数字, 字母, 下划线, 汉字的字符, 等价于 [^A-Za-z0-9_]
\S
: 匹配任意不是空白符的字符, 等价于 [^ \f\n\r\t\v]
\D
: 匹配任意非数字的字符 等价于 [^0-9]
\B
: 匹配不是单词开头或结束的位置
贪婪与懒惰
当正则表达式中包含能接受重复的限定符时, 通常的行为是匹配尽可能多的字符, 这被称为贪婪匹配。
有时我们需求是匹配尽可能少的匹配项, 这时需要懒惰匹配模式。
In: re.search('a.*b', 'aabacdabebb').group() # 贪婪匹配
Out: 'aabacdabebb' # 匹配最长的以 a 开始, 以 b 结束的字符串
In: re.search('a.?b', 'aabacdabebb').group() # 懒惰匹配
Out: 'aab' #
懒惰限定符:
*?
: 重复任意次, 但尽可能少重复
+?
: 重复 1 次或更多次, 但尽可能少重复
??
: 重复 0 次或 1 次, 但尽可能少重复
{n, m}?
: 重复 n 到 m 次, 但尽可能少重复
{n, }?
: 重复 n 次以上, 但尽可能少重复
编译标志
编译标志可以修改正则表达式的一些运行方式
DOTALL, S
: 使 . 匹配包括行在内的所有字符
IGNORECASE, I
: 使匹配对大小写不敏感
LOCALE, L
: 做本地化识别匹配
MULTILINE, M
: 多行匹配, 影响 ^ 和 $
VERBOSE, X
: 详细装填
DEBUG
: 调试模式
In: re.search('.', '\n')
Out: # 无输出
In: re.search('.', '\n', re.S)
Out: <_sre.SRE_Match object; span=(0, 1), match='\n'>
In: re.search('a.', 'A\n', re.S|re.I)
Out: <_sre.SRE_Match object; span=(0, 2), match='A\n'>
编译正则表达式
search() 和 match() 方法在背后做了两件事情:
1.编译正则表达式, 若正则表达式的字符串本身不合法, 则报错。
2.用编译后的正则表达式去匹配字符串。对于程序频繁使用的表达式要做预编译, 在预编译之后, 重复使用它时就不再需要编译。
In: regex = re.compile(r'^\d{1,3}$')
In: regex.match('12')
Out: <_sre.SRE_Match object; span=(0, 2), match='12'>
In: regex.match('1234')
Out: # 无输出
检索和替换
re.sub(pattern, repl, string, count=0, flags=0) # re.sub 签名
# 第一个参数是模式
# 第二个参数是要替换成的内容
# 第三个是待匹配的文本
# 如果有多项匹配的内容, 可以通过第四个参数 count 指定匹配的数量。
# flags 是编译的标志
In: re.sub('\d+', '', 'test123')
Out: 'test'
In: re.sub('\d', '', 'test123')
Out: 'test'
In: re.sub('\d', '', 'test123', count=2)
Out: 'test3'
findall()/finditer()
findall() 和 finditer() 是用来搜索文本, 返回全部能匹配的字符串或者对象的方法。
findall() 是一次性地把结果放到列表中返回。
finditer() 返回一个可以顺序访问每一个匹配对象的迭代器。
In: re.findall('\d', '1a2b3c4d')
Out: ['1', '2', '3', '4']
In: for i in re.finditer('\d', '1a2b3c4d'):
... print(i)
...
Out:
<_sre.SRE_Match object; span=(0, 1), match='1'>
<_sre.SRE_Match object; span=(2, 3), match='2'>
<_sre.SRE_Match object; span=(4, 5), match='3'>
<_sre.SRE_Match object; span=(6, 7), match='4'>
分组
很多需求里面, 一个表达式会同时匹配多个模式, 我们需要把他们分组, 分组是通过 () 来标识的。
() 也是元字符, () 内的表达式成为一组。
通过给匹配对象的 group() 传入分组位置, 就可以获得对应分组的匹配内容。
位置是从 1 开始的, group() 可以一次性传入多个分组位置, 返回的是 tuple()。
使用 groups() 方法可以得到匹配对象的所有分组。
In: m = re.match('(a)b', 'ab')
In: m.group(1)
Out: 'a'
In: m = re.match('([a-c]+).*(\w)', 'abcbde')
In: m.groups()
Out: ('abcb', 'e')
In: m.group(1), m.group(2), m.group(1, 2)
Out: ('abcb', 'e', ('abcb', 'e'))
命名分组
命名分组是给具有默认分组编号的组另外取一个别名
(?P<name>regex) # 命名分组格式
In: pattern = '(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
In: m = re.match(pattern, '1998-09-23')
In: m.groupdict() # 通过 groupdict() 可以得到命名分组的名字和结果
Out: {'year': '1998', 'month': '09', 'day': '23'}
In: m.group('year')
Out: '1998'
In: m.group('month')
Out: '09'
In: m.group('day')
Out: '23'