正则表达式
最简单的正则表达式
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)
|
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)
|
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_蛤') Out: <_sre.SRE_Match object; span=(0, 4), match='a1_蛤'>
In: re.search('\s\s\s', ' \r\t') Out: <_sre.SRE_Match object; span=(0, 3), match=' \r\t'>
In: re.search('\d\d\d', '123') 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') Out: <_sre.SRE_Match object; span=(0, 1), match='a'>
In: re.search('[12b]', 'abcd') Out: <_sre.SRE_Match object; span=(1, 2), match='b'>
In: re.search('[a-z]', 'qwe') Out: <_sre.SRE_Match object; span=(0, 1), match='q'>
In: re.search('[d-z]', 'abc') 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') 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'
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)
|
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'))
|
命名分组
命名分组是给具有默认分组编号的组另外取一个别名
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() 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'
|