Created: Aug 12, 2020 11:28 AM Tags: Python, RegExp, re, regex
re 模块import re编译标志
ASCII (A)
使几个转义如 \w、\b、\s 和 \d 匹配仅与具有相应特征属性的 ASCII 字符匹配。
DOTALL (S)
使 . 匹配任何字符,包括换行符
IGNORECASE (I)
进行大小写不敏感匹配
re.search('AbC', 'aBc', re.I) # <re.Match object; span=(0, 3), match='aBc'>
LOCALE (L)
进行区域设置感知匹配
MULTILINE (M)
多行匹配,影响 ^ 和 $
VERBOSE (X)
启用详细的正则,可以更清晰,更容易理解。
转义字符反斜杠 \
可以转义已有的通配符,保留原本的字符匹配含义
re.compile("\\section").search("aaa\section") # None re.compile(r"\\d").search("aaa\dbbb") # <re.Match object; span=(3, 5), match='\\d'>通用匹配符
.
- 匹配单个字符
re.search('.', 'abc') # <re.Match object; span=(0, 1), match='a'>
+
匹配前一个符号 1 至无穷次
re.search('a+', 'aaab') # <re.Match object; span=(0, 3), match='aaa'>
*
匹配前一个符号 0 至无穷次
re.search('ca*b', 'cb') <re.Match object; span=(0, 2), match='cb'>
?
匹配前一个符号 0 次或 1次
re.search('ca?b', 'caab') # None
{m,n}
匹配前一个符号 m 至 n 次
re.search('c2{4}b', 'c2222b') # <re.Match object; span=(0, 6), match='c2222b'>
[ ]
匹配字符集
re.search('c[A-Z]b', 'cZb') # <re.Match object; span=(0, 3), match='cZb'>
^
匹配行首
re.search('^cb', 'acb') # None
否定
re.search('[^ab]', 'acb') # <re.Match object; span=(1, 2), match='c'>
$
匹配行尾
re.search('c$', 'abc') # <re.Match object; span=(2, 3), match='c'>
|
分割不同的匹配正则
re.search('d$|^a', 'abc') # <re.Match object; span=(0, 1), match='a'>
\d
匹配十进制数字,等价于 [0-9]
re.search('\d+', 'ac12345v') # <re.Match object; span=(2, 7), match='12345'>
补集 **\D**,等价于 [^0-9]
\w
匹配字母以下划线及数字,等价于 [a-zA-Z0-9_]
re.search('\w+', 'ac1v_c%3') # <re.Match object; span=(0, 6), match='ac1v_c'>
补集 **\W**,等价于 [^a-zA-Z0-9_]
\s
匹配空格,等价于 [ \t\n\r\f\v]
re.search('\s+', 's \t p ace') # <re.Match object; span=(1, 4), match=' \t '>
补集 **\S**,等价于 [^ \t\n\r\f\v]
贪婪/非贪婪正则表达式默认会尽可能多的匹配(贪婪)
re.findall('\d+', 'a1234455b') # ['1234455']
更少的匹配需要在贪婪算符后增加 ?
+? *? ?? {m,n}?
re.findall('\d+?', 'a1234455b') # ['1', '2', '3', '4', '4', '5', '5']
回溯灾难
如果在正则内连用两个贪婪匹配算符,可能会导致回溯灾难(Catastrophic Backtracking)
# !!!!!!!!!!!!!!! 谨慎运行 !!!!!!!!!!!!!!!!!! re.search("(a+)+b", "aaaaaaaaaaaaaaaaaaaaa") # 会循环尝试所有的组合 (a+)+ 的组合,导致卡死,大概有 O(2^N) 组合 from functools import partial import timeit def a(i): return i, re.search("(a+)+b", "a"*i) for i in range(15, 25): print(timeit.timeit(partial(a, i), number=10)) # 0.0691330000000221 # 0.11274059999999508 # 0.16568009999997457 # 0.3310396999999625 # 0.6679392000000348 # 1.4523080000000164 # 2.7366319000000203 # 6.06315950000004 # 10.824507399999959 # 23.64085460000001捕获匹配组
捕获分组 (regex)
re.findall("(\d+)", "abc123def234") # ['123', '234'] re.search("(\d+)", "abc123def234") # <re.Match object; span=(3, 6), match='123'>
分组不捕获 (?:regex)
re.search("(?:\D+)-(\d+)", "abc-234").groups() # ('234',)
分组配置 (?[aiLmsux]:regex)
re.findall("(?i:[a-z]+)", "abc123DEF234") # ['abc', 'DEF']
前项肯定 (?<=regex)
re.search(r"(?<=abc)\d+", "abc123deb").group()
后项肯定 (?=regex)
re.search(r"(\d+(?=abc))", "xbc123abc").group()
前项否定 (?<!regex)
re.search(r"(?<!xbc)\d+", "abc123abc").group()
后项否定 (?!regex)
re.search(r"\d+(?!xbc)", "123abc").group()模块常用方法
re.compile
预编译正则的方法,可以加速程序运行的时间
number_regex = re.compile('[0-9]+')
re.escape
把已有的字符串做转义
re.escape('\d') # '\\\\d' re.search(re.escape('\d[0-9]'), '121\d[0-9]abc') # <re.Match object; span=(3, 10), match='\\d[0-9]'>
re.findall
查找所有能匹配上的子字符串
re.findall('a+', 'aaaa') # ['aaaa'] re.findall('a+?', 'aaaa') # ['a', 'a', 'a', 'a']
re.match
匹配后的第一个对象
matched = re.match('a+?', 'aaaa') # <re.Match object; span=(0, 1), match='a'> matched.span() # (0, 1) matched.group() # 'a'
re.search
搜索并返回匹配对象
searched= re.search('(a)(b)', 'aaaab') # <re.Match object; span=(3, 5), match='ab'> searched.groups() # ('a', 'b')
re.split
按照 regex pattern 拆分字符
re.split('\s', 'This is a test string') # ['This', 'is', 'a', 'test', 'string']
re.sub
替换正则匹配的文字
可以用 \1 \2 在替换的文本内表示正则的匹配内容,依次为所有捕获的匹配组
re.sub('_', ' ', 'This_is_a_test_string') # 'This is a test string' re.sub('([a-z]+)(\d+)', r'\2\1', 'abc123xyz987') '123abc987xyz'