正则表达式概念 
python正则表达式 深入理解正则表达式环视的概念与用法 资源 | 正则表达式的功法大全 在线正则表达式验证网站  
概念定义
 
使用单个字符串来描述匹配某个句法规则的字符串,是对字符串操作的一种逻辑公式
应用场景
 
处理文本和数据,提高复杂文本分析的效率
正则表达式过程
 
依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;否则匹配失败
1 2 3 4 5 6 7 8 9 10 11 12 import  refrom  IPython.core.interactiveshell import  InteractiveShellInteractiveShell.ast_node_interactivity = "all"    pattern_s = r'imooc'    pattern_r = re.compile (pattern_s)   str1 = 'imooc book'    match_r = pattern_r.match (str1) print (f'match_r.group(): {match_r.group()} ' )match_r.group(): imooc 
正则元字符 内容匹配 
正则表达式 代表的匹配字符  
 
. 
匹配任意字符(不包括换行符) 
 
[0-9] 
0-9的数字 
 
[a-z] 
小写字母 
 
[A-Z] 
大写字母 
 
\d 
匹配数字,等同于[0-9] 
 
\D 
匹配非数字,等同于[\^0-9] 
 
\w 
匹配大小写字母、数字和下划线,等同于[a-z0-9A-Z_] 
 
\W 
匹配非大小写字母、数字和下划线,等同于[\^a-z0-9A-Z_] 
 
\s 
匹配空白 
 
\S 
匹配非空白 
 
\u4e00-\u9fff 
匹配中文字符 
 
 
 
ps: 第(?P<law_idx>(?:(?!第|条).)*?)条可以匹配出公平竞争审查第三方评估实施指南(2023)第十一条 中的第十一条,而不是第三方评估实施指南(2023)第十一条
个数匹配 
正则表达式 代表的匹配字符  
 
* 
匹配前面的字符或者子表达式0次或多次 
 
+ 
匹配前一个字符或子表达式一次或多次 
 
? 
匹配前一个字符或子表达式0次或1次重复 
 
{n} 
匹配前一个字符或子表达式n次 
 
{m,n} 
匹配前一个字符或子表达式m至n次 
 
{n,} 
匹配前一个字符或者子表达式至少n次 
 
*? / +? / ?? 
惰性匹配上一个 
 
 
 
位置匹配 
正则表达式 代表的匹配字符  
 
^ 
匹配字符串开头, 多行模式下匹配每一行的开始 
 
$ 
匹配字符串结尾, 多行模式下匹配每一行的结束 
 
\A / \Z 
指定字符串必须出现在开头/结尾 
 
\b 
匹配位于单词开始或结束位置的空字符串 
 
\B 
匹配不位于单词开始或结束位置的空字符串 
 
 
 
分组匹配 
正则表达式 代表的匹配字符  
 
| 
匹配左右任意一个表达式 
 
(ab) 
括号里的表达式作为一个分组 
 
\<number> 
引用编号为num的分组匹配到的字符串 
 
(?P<name>) 
分组起别名 
 
(?P=name) 
引用别名为name的分组匹配字符串 
 
[ ] 
可匹配其中任意一个字符 
 
 
 
转义匹配 
正则表达式 代表的匹配字符  
 
\ 
转义字符,如\.只能匹配.,不能再匹配任意字符 
 
 
 
基本使用 预编译 compile 函数用于编译正则表达式,生成一个正则表达式(Pattern)对象,供 match() 和 search() 这两个函数使用
如果重复多次地使用正则表达式,最好是使用compile函数把正则表达式编译成对象re.Pattern,这样会大大地提高搜索的效率
re.compile(pattern, flags=0)
 
属性说明 
flags :编译时指定的模式groupindex :以正则表达式中有别名的组的别名为键、以该组对应的编号为值的字典,没有别名的组不包含在内。groups :正则表达式中分组的数量pattern :编译时用的正则表达式 
方法说明 
findall、finditer、match、search、split、sub、subn 等函数 
 
例子
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import  res = 'Hello, Mr.Gumby : 2016/10/26'  pat = '''(?:        # 构造一个不捕获分组 用于使用 |                (?P<name>\w+\.\w+)    # 匹配 Mr.Gumby               |     # 或               (?P<no>\s+\.\w+) # 一个匹配不到的命名分组               )               .*? # 匹配  :                (\d+) # 匹配 2016               ''' p = re.compile (pat, re.X) print (f'p.flags: {p.flags} ' )print (f'p.groupindex: {p.groupindex} ' )print (f'p.groups: {p.groups} ' )print (f'p.pattern: {p.pattern} ' )p.flags: 96  p.groupindex: {'name' : 1 , 'no' : 2 } p.groups: 3  p.pattern: (?:                       (?P<name>\w+\.\w+)                   |                    (?P<no>\s+\.\w+)                )               .*?                (\d+)  
匹配模式 
Python正则表达式,请不要再用re.compile了 
 
在正则表达式中,采用预编译的优势就是可以节约时间,提高效率
re.compile(pattern, flags=0)给定一个正则表达式 pattern
指定使用的模式 flags 默认为0 即不使用任何模式,然后会返回一个 SRE_Pattern对象
参数说明 
 
pattern : 一个字符串形式的正则表达式flags : 可选,表示匹配模式,比如忽略大小写,多行模式等,使用按位或运算符 | 同时添加多个模式,具体参数为:
re.I :数值2,忽略大小写re.L :数值4,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境re.M :数值8,多行模式re.S :数值16,即为 . 并且包括换行符在内的任意字符(. 不包括换行符)re.U :数值32,表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库re.X :数值64,为了增加可读性,忽略空格和 # 后面的注释 
 
忽略大小写re.I 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import  restr1 = 'hello World!'  pattern_s = "hello world!"  pattern_r = re.compile (pattern_s, re.I) print (f'pattern_r.match(str1).group(): {pattern_r.match (str1).group()} ' )pattern_s = "(?#注释)(?i)hello world!"  pattern_r = re.compile (pattern_s) print (f'pattern_r.match(str1).group(): {pattern_r.match (str1).group()} ' )pattern_r.match (str1).group(): hello World! pattern_r.match (str1).group(): hello World! 
字符集本地化re.L 多行模式re.M 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import  restr1 = '''first line1  second line2 third line3''' regex_start = re.compile ("^\w+" ) print (f'regex_start.findall(str1): {regex_start.findall(str1)} ' )regex_start_m = re.compile ("^\w+" , re.M) print (f'regex_start_m.findall(str1): {regex_start_m.findall(str1)} ' )regex_end = re.compile ("\w+$" ) print (f'regex_end.findall(str1): {regex_end.findall(str1)} ' )regex_end_m = re.compile ("\w+$" , re.M) print (f'regex_end_m.findall(str1): {regex_end_m.findall(str1)} ' )regex_start.findall(str1): ['first' ] regex_start_m.findall(str1): ['first' , 'second' , 'third' ] regex_end.findall(str1): ['line3' ] regex_end_m.findall(str1): ['line1' , 'line2' , 'line3' ] 
所有字符re.S 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import  restr2 = '''first line1  second line2 third line3''' regex = re.compile (".+" ) print (f'regex.findall(str2): {regex.findall(str2)} ' )regex_dotall = re.compile (".+" , re.S) print (f'regex_dotall.findall(str2): {regex_dotall.findall(str2)} ' )regex.findall(str2): ['first line1' , 'second line2' , 'third line3' ] regex_dotall.findall(str2): ['first line1\nsecond line2\nthird line3' ] 
冗余模式re.X 1 2 3 4 5 6 7 8 9 10 import  reemail_regex = re.compile ("[\w+\.]+@[a-zA-Z\d]+\.(com|cn)" ) email_regex = re.compile ("""[\w+\.]+  # 匹配@符前的部分                              @  # @符                             [a-zA-Z\d]+  # 邮箱类别                             \.(com|cn)   # 邮箱后缀  """ , re.X)
UNICODE解析re.U re.Match类 若匹配成功,match/search返回的是Match对象,finditer返回的也是Match对象的迭代器
获取匹配结果需要调用Match对象的group()、groups或group(index)方法
属性说明 
 
endpos : 本次搜索结束位置索引lastgroup : 本次搜索匹配到的最后一个分组的别名lastindex : 本次搜索匹配到的最后一个分组的索引pos : 本次搜索开始位置索引re : 本次搜索使用的 SRE_Pattern 对象regs : 列表,元素为元组,包含本次搜索匹配到的所有分组的起止位置 
方法说明 
 
end([group=0]) :返回指定分组的结束位置,默认返回正则表达式所匹配到的最后一个字符的索引
expand(template) :根据模版返回相应的字符串,类似与 sub 函数里面的 repl, 可使用 \1 或者 \g 来选择分组
group([group1, …]) :根据提供的索引或名字返回响应分组的内容
默认返回 start() 到 end() 之间的字符串, 提供多个参数将返回一个元组
groupdict([default=None]) :返回一个包含所有匹配到的命名分组的字典,没有命名的分组不包含在内
key 为组名, value 为匹配到的内容,参数 default 为没有参与本次匹配的命名分组提供默认值
groups([default=None]) :以元组形式返回每一个分组匹配到的字符串,包括没有参与匹配的分组,其值为 default
span([group]) :返回指定分组的起止位置组成的元组,默认返回由 start() 和 end() 组成的元组
start([group]) :返回指定分组的开始位置,默认返回正则表达式所匹配到的第一个字符的索引
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import  res = 'Hello, Mr.Gumby : 2016/10/26'  m = re.search(', (?P<name>\w+\.\w+).*?(\d+)' , s) print (f'm.endpos: {m.endpos} ' )print (f'm.lastgroup: {m.lastgroup} ' )print (f'm.lastindex: {m.lastindex} ' )print (f'm.pos: {m.pos} ' )print (f'm.re: {m.re} ' )print (f'm.regs: {m.regs} ' )print (f'm.string: {m.string} ' )m.endpos: 28  m.lastgroup: None  m.lastindex: 2  m.pos: 0  m.re: re.compile (', (?P<name>\\w+\\.\\w+).*?(\\d+)' ) m.regs: ((5 , 22 ), (7 , 15 ), (18 , 22 )) m.string: Hello, Mr.Gumby : 2016 /10 /26  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import  res = 'Hello, Mr.Gumby : 2016/10/26'  m = re.search('''(?:        # 构造一个不捕获分组 用于使用 |                (?P<name>\w+\.\w+)    # 匹配 Mr.Gumby               |     # 或               (?P<no>\s+\.\w+) # 一个匹配不到的命名分组               )               .*? # 匹配  :                (\d+) # 匹配 2016               ''' ,              s, re.X) print (f'm.end(): {m.end()} ' )mExpend = m.expand('my name is \\1' ) print (f"m.expand('my name is \\1'): {mExpend} " )print (f'm.group(): {m.group()} ' )print (f'm.group(1, 2): {m.group(1 , 2 )} ' )m.groupdict('default_string' ) print (f"m.groups('default_string'): {m.groups('default_string' )} " )print (f'm.span(3): {m.span(3 )} ' )print (f'm.start(3): {m.start(3 )} ' )m.end(): 22  m.expand('my name is \1' ): my name is  Mr.Gumby m.group(): Mr.Gumby : 2016  m.group(1 , 2 ): ('Mr.Gumby' , None ) m.groups('default_string' ): ('Mr.Gumby' , 'default_string' , '2016' ) m.span(3 ): (18 , 22 ) m.start(3 ): 18  
匹配 match 
re.match(pattern, string[, flags])
 
从首字母开始匹配 ,string如果包含pattern子串,则匹配成功,返回Match对象,失败则返回None,若要完全匹配,pattern要以$结尾
参数说明 
pattern :匹配的正则表达式string :要匹配的字符串。flags :标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志 
match函数在string的开始位置 匹配,如果匹配则返回对象,否则返回None
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16     import  re    text = 'python world'  m = re.match (r"\w+" , text) print (m.group(0 )) if  m else  print ('not match' )text = '#python world'  m = re.match (r"\w+" , text) print (m.group(0 )) if  m else  print ('not match' )     output: python output: not  match  
1 2 3 4 5 6 7 8 import  restr1 = '333STR1666STR299'  pattern_s = r'([A-Z]+(\d))'  print (f're.match(pattern_s, str1): {re.match (pattern_s, str1)} ' )  re.match (pattern_s, str1): None  
search 
re.search(pattern, string[, flags])
 
若string中包含pattern子串 ,则返回Match对象,否则返回None,注意,如果string中存在多个 pattern子串,只返回第一个 
参数说明 
pattern :匹配的正则表达式string :要匹配的字符串flags :标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等 
search会扫描整个string 查找匹配,如果找到则返回一个相应的匹配对象,否则返回None
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import  retext = 'python world'  m = re.search(r"\w+" , text) print (m.group(0 )) if  m else  print ('not match' )text = '#python world'  m = re.search(r"\w+" , text) print (m.group(0 )) if  m else  print ('not match' )output: python output: python 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  restr1 = '333STR1666STR299'  pattern_s = r'([A-Z]+(\d))'  match_r = re.search(pattern_s, str1) print (f'match_r.group(0): {match_r.group(0 )} ' )  print (f'match_r.group(1): {match_r.group(1 )} ' )print (f'match_r.group(2): {match_r.group(2 )} ' )print (f'match_r.groups(): {match_r.groups()} ' )match_r.group(0 ): STR1 match_r.group(1 ): STR1 match_r.group(2 ): 1  match_r.groups(): ('STR1' , '1' ) 
检索 findall 
re.findall(pattern, string[, flags])
 
返回string中所有与pattern相匹配的全部字串 ,返回形式为数组 
参数说明 
string : 待匹配的字符串pos : 可选参数,指定字符串的起始位置,默认为 0endpos : 可选参数,指定字符串的结束位置,默认为字符串的长度 
findall返回所有匹配的指定模式的文本子串到列表中,一个元素一个匹配串
 
1 2 3 4 5 6 7 8 9 10 11 12 import  restr1 = '333STR1666STR299'  pattern_s = r'([A-Z]+(\d))'  match_r = re.findall(pattern_s, str1) for  m in  match_r:    print (f'm[0], m[1]: {m[0 ], m[1 ]} ' )      m[0 ], m[1 ]: ('STR1' , '1' ) m[0 ], m[1 ]: ('STR2' , '2' ) 
finditer 
re.finditer(pattern, string[, flags])
 
返回string中所有与pattern相匹配的全部字串 ,返回形式为迭代器 
参数说明 
pattern :匹配的正则表达式string :要匹配的字符串。flags :标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志 
finditer函数跟findall函数类似,但返回的是一个迭代器
 
1 2 3 4 5 6 7 8 9 10 11 12 import  restr1 = '333STR1666STR299'  pattern_s = r'([A-Z]+(\d))'  match_r = re.finditer(pattern_s, str1) for  m in  match_r:    print (f'm.group(0), m.group(1), m.group(2): {m.group(0 ), m.group(1 ), m.group(2 )} ' )        m.group(0 ), m.group(1 ), m.group(2 ): ('STR1' , 'STR1' , '1' ) m.group(0 ), m.group(1 ), m.group(2 ): ('STR2' , 'STR2' , '2' ) 
替换和分割 sub替换 
re.sub(pattern, repl, string, count=0, flags=0)
 
将字符串中匹配正则表达式的部分替换为其他值 
Return the string obtained by replacing the leftmost non-overlapping occurrences of the pattern in string by the replacement repl. 
repl can be either a string or a callable; if a string, backslash escapes in it are processed. If it is a callable, it’s passed the Match object and must return a replacement string to be used. 
 
参数说明 
pattern : 正则中的模式字符串。repl : 替换的字符串,也可为一个函数。string : 要被查找替换的原始字符串。count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。 
代码 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # -*- coding: utf-8 -* import re def add_one(match):     val = match.group()     num = int(val) + 1     return str(num) str2 = 'imooc videonum=200' reSub_1 = re.sub(r'\d+', add_one, str2) print(f"re.sub(r'\d+', add_one, str2): {reSub_1}") reSub_2 = re.sub(r'\d+', '203', str2) print(f"re.sub(r'\d+', '203', str2): {reSub_2}") sample_text = '2020-05-20 10:59:23 hello world 2020-05-21 10:59:24 hello kitty' sample_pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})' sample_repl = r'\g<month>/\g<day>/\g<year>' print(re.sub(sample_pattern, sample_repl, sample_text)) 
输出 
 
1 2 3 re.sub(r'\d+', add_one, str2): imooc videonum=201  re.sub(r'\d+', '203 ', str2): imooc videonum=203  05 /20 /2020  10 :59 :23  hello world 05 /21 /2020  10 :59 :24  hello kitty
高级用法
一日一技:如何正确使用 re.sub 的第二个参数 
 
re.sub第二个参数可以是函数
设想有一个字符串abc18123456794xyz123,这个字符串中有两段数字,并且长短是不一样的
第一个数字是11位的手机号。我想把字符串替换为:`abc[隐藏手机号]xyz* 
不是手机号的数字,每一位数字逐位替换为星号
1 2 3 4 5 6 7 8 9 10 11 import  redef  test (repl ): if  len (repl.group(0 )) == 11 :      return  '[隐藏手机号]'   else :      return  '*'  * len (repl.group(0 )) a = 'abc18123456794xyz123'  b = re.sub('\d+' , test, a) print (b)
subn替换 
subn(pattern, repl, string, count=0, flags=0)
 
作用与函数 sub 一样, 唯一不同之处在于返回值为一个元组,第一个值为替换后的字符串,第二个值为发生替换的次数
split分割 
re.split(pattern, string, maxsplit=0, flags=0)
 
根据匹配分割字符串,返回分割字符串组成的列表 
Split the source string by the occurrences of the pattern, returning a list containing the resulting substrings. 
If capturing parentheses are used in pattern, then the text of all groups in the pattern are also returned as part of the resulting list. If maxsplit is nonzero, at most maxsplit splits occur, and the remainder of the string is returned as the final element of the list. 
 
参数说明 
pattern :匹配的正则表达式string :要匹配的字符串。maxsplit :分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。flags :标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志 
代码 
 
1 2 3 4 5 import  restr3 = 'imooc:C C++ Java Python,c#'  resub_3 = re.split(r':| |,' ,str3) print (f"re.split(r':| |,',str3):{resub_3} " )
输出 
 
1 re.split(r':| |,',str3):['imooc', 'C', 'C++', 'Java', 'Python', 'c#'] 
正则缓存 当你在程序中使用 re 模块,无论是先使用 compile 还是直接使用比如 findall 来使用正则表达式操作文本
re 模块都会将正则表达式先编译一下, 并且会将编译过后的正则表达式放到缓存中
这样下次使用同样的正则表达式的时候就不需要再次编译, 因为编译其实是很费时的,这样可以提升效率
而默认缓存的正则表达式的个数是 100,当你需要频繁使用少量正则表达式的时候,缓存可以提升效率
而使用的正则表达式过多时,缓存带来的优势就不明显了
这个re.purge() 函数的作用是清除缓存中的正则表达式,可能在你需要优化占用内存 的时候会用到
爬虫例子
 
代码 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import  urllib.requestimport  reimport  osurl = r'https://www.imooc.com/course/list?c=ai'  res = urllib.request.urlopen(url) html = res.read().decode('utf-8' ) listurl = re.findall(r'src="//img\d.+?jpg"' , html) lurl = ['http:%s'  % x[5 :-1 ] for  x in  listurl] print (lurl)basepath = './res'  if  not  os.path.exists(basepath):    os.makedirs(basepath) imgspath = os.path.join(basepath, 're_op' ) if  not  os.path.exists(imgspath):    os.mkdir(imgspath) for  ii, uu in  enumerate (lurl):    savepath = os.path.join(imgspath, '%d.jpg'  % (ii))     res = urllib.request.urlretrieve(uu, savepath) 
输出 
1 ['http://img4.mukewang.com/5 bd8157a0001a7a506000336-240 -135 .jpg', 'http://img3.mukewang.com/5 bc6e6b80001434f06000338-240 -135 .jpg', 'http://img2.mukewang.com/5 ba2386600013d3705980337-240 -135 .jpg', 'http://img3.mukewang.com/5 b9105800001288905400300-240 -135 .jpg', 'http://img4.mukewang.com/5 b7f737a0001cfb706000336-240 -135 .jpg', 'http://img4.mukewang.com/5 abc6159000142f706000338-240 -135 .jpg', 'http://img1.mukewang.com/5 a40c6370001d13c06000338-240 -135 .jpg'] 
进阶用法 预查询 1 2 3 4 5 6 7 8 9 10 import  repattern = r'案由的相关法规:.*?(?=\n\n|$|```)'  matches = re.findall(pattern, content, re.DOTALL)  for  match  in  matches:    print (match ) 
解释 :
(?=\n\n|$|```) - 这是一个正向预查(positive lookahead),它不消耗字符,只是检查其后面的内容。这个预查包含三个可能的匹配条件:
\n\n 匹配两个连续的换行符$ 匹配字符串的结束```(三个反引号)匹配代码块的开始标记,这在某些文本格式(如Markdown)中用于表示代码块 
非贪婪匹配 
匹配模式
 
Python里数量词默认是贪婪模式 的,使用*? / +? / ??可使贪婪模式变成非贪婪模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import  restr1 = '9abc'    pattern_s = r'[0-9][a-z]*?'    pattern_r = re.compile (pattern_s)   match_r = pattern_r.match (str1) print (f'match_r.group(): {match_r.group()} ' )pattern_s = r'[0-9][a-z]*'    pattern_r = re.compile (pattern_s)   match_r = pattern_r.match (str1) print (f'match_r.group(): {match_r.group()} ' )pattern_s = r'[0-9][a-z]+?'    pattern_r = re.compile (pattern_s)   match_r = pattern_r.match (str1) print (f'match_r.group(): {match_r.group()} ' )pattern_s = r'[0-9][a-z]+'    pattern_r = re.compile (pattern_s)   match_r = pattern_r.match (str1) print (f'match_r.group(): {match_r.group()} ' )pattern_s = r'[0-9][a-z]??'    pattern_r = re.compile (pattern_s)   match_r = pattern_r.match (str1) print (f'match_r.group(): {match_r.group()} ' )pattern_s = r'[0-9][a-z]?'    pattern_r = re.compile (pattern_s)   match_r = pattern_r.match (str1) print (f'match_r.group(): {match_r.group()} ' )match_r.group(): 9  match_r.group(): 9abc match_r.group(): 9a match_r.group(): 9abc match_r.group(): 9  match_r.group(): 9a 
分组匹配(捕获组) 
1 2 3 4 5 6 7 8 import  regex as  repat = re.compile (r"有(?P<zj>(许可证[件]?|执照))的?(?P<xf_type>(暂扣|吊销).{0,4}(?P=zj))" ) doc = "有许可证的吊销许可证,并给予严厉惩罚"  res = list (re.finditer(pat, doc)) res[0 ].groupdict() Out[16 ]: {'zj' : '许可证' , 'xf_type' : '吊销许可证' } 
1 2 3 4 5 6 7 8 9 import  restr1 = '<book>python</book>'  pattern_s = r'<([\w]+>)[\w]+</\1'  print (re.match (pattern_s, str1).group())<book>python</book> 
1 2 3 4 5 6 7 8 9 10 11 import  restr1 = '<book>python</book>'  pattern_s = r'<(?P<mark1>[\w]+>)[\w]+</(?P=mark1)'  print (re.match (pattern_s, str1).group())Out[12 ]: <book>python</book> re.match (pattern_s, str1).groupdict() Out[13 ]: {'mark1' : 'book>' } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 group() / group(0 ): 母串中与模式pattern匹配的子串 group(index): 第index个group匹配成功的子串 groups(): 所有group组成的一个元组,与pattern中的()有关 import  rep = re.compile ('\d-\d-\d' ) m = p.match ('2-3-1' ) print (f'm.group(): {m.group()} ' )print (f'm.group(0): {m.group(0 )} ' )print (f'm.groups(): {m.groups()} ' )m.group(): 2 -3 -1  m.group(0 ): 2 -3 -1  m.groups(): () 
1 2 3 4 5 6 7 8 9 10 11 import  rep = re.compile ('(\d)-(\d)-(\d)' ) m = p.match ('2-3-1d5-4-3' ) print (f'm.group(): {m.group()} ' )print (f'm.group(0): {m.group(0 )} ' )print (f'm.groups(): {m.groups()} ' )m.group(): 2 -3 -1  m.group(0 ): 2 -3 -1  m.groups(): ('2' , '3' , '1' ) 
1 2 3 4 5 6 7 8 9 10 11 import  rep = re.compile ('(\d)-(\d)-(\d)' ) m = re.match (p,'2-3-1d5-4-3' ) print (f'm.group(): {m.group()} ' )print (f'm.group(0): {m.group(0 )} ' )print (f'm.groups(): {m.groups()} ' )m.group(): 2 -3 -1  m.group(0 ): 2 -3 -1  m.groups(): ('2' , '3' , '1' ) 
转义匹配替代函数 使用python的过程中,你肯定对转义字符的使用苦恼过,因为有的时候我们需要使用一些特殊符号如”$ * . ^”等的原意
有时候需要被转义后的功能,并且转义字符地使用很繁琐,容易出错
re.escape(pattern)
 
转义: 如果你需要操作的文本中含有正则的元字符,你在写正则的时候需要将元字符加上反斜扛 \ 去匹配自身 
而当这样的字符很多时,写出来的正则表达式就看起来很乱而且写起来也挺麻烦的,这个时候你可以使用这个函数 
可以对字符串中所有可能被解释为正则运算符的字符进行转义的应用函数 
 
1 2 3 4 5 6 7 8 9 10 11 12 import  restr2 = ".+\d123"  regex_str = re.escape(".+\d123" ) print (f'regex_str: {regex_str} ' )for  g in  re.findall(regex_str, str2):    print (g)      regex_str: \.\+\\d123 .+\d123 
re增强的包装 主要目的是 ,增强re的匹配能力,为match对象添加了start: int=0参数,start指定了当前字符串的起始位置,默认的起始位置是0
导入库,regex是re的增强版,需要pip安装,它支持更强的正则,比如一个表达式中可以出现两个一样的捕获组名字 
 
1 2 from  typing import  Iterable, List import  regex as  re
包装match对象为ReMatch类
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 class  ReMatch :    def  __init__ (self, match , start: int  = 0  ):         """          指定起始位置的Match类         :param match: re.Match对象         :param start:         """         self.match  = match          self.start_idx = start     @property     def  endpos (self ):         """ 本次搜索结束位置索引 """          return  self.match .endpos + self.start_idx     @property     def  lastgroup (self ):         """ 本次搜索匹配到的最后一个分组的别名 """          return  self.match .lastgroup     @property     def  lastindex (self ):         """ 本次搜索匹配到的最后一个分组的索引 """          return  self.match .lastindex + self.start_idx if  type (self.match .lastindex) == int  else  self.match .lastindex     @property     def  pos (self ):         """ 本次搜索开始位置索引 """          return  self.match .pos + self.start_idx     @property     def  re (self ):         """ 本次搜索使用的SRE_Pattern对象 """          return  self.match .re     @property     def  regs (self ):         """ 列表,元素为元组,包含本次搜索匹配到的所有分组的起止位置 """          return  tuple ([(s + self.start_idx, e + self.start_idx) for  s, e in  self.match .regs])     def  end (self, *args, **kwargs ):         """ 返回指定分组的结束位置,默认返回正则表达式所匹配到的最后一个字符的索引 """          return  self.match .end(*args, **kwargs) + self.start_idx     def  expand (self, *args, **kwargs ):         """ 根据模版返回相应的字符串,类似与 sub 函数里面的 repl, 可使用 \1 或者 \g 来选择分组 """          return  self.match .expand(*args, **kwargs)     def  group (self, *args, **kwargs ):         """ 根据提供的索引或名字返回响应分组的内容,默认返回 start() 到 end() 之间的字符串, 提供多个参数将返回一个元组 """          return  self.match .group(*args, **kwargs)     def  groupdict (self, *args, **kwargs ):         """ 返回一个包含所有匹配到的命名分组的字典,没有命名的分组不包含在内,key 为组名, value 为匹配到的内容,参数 default 为没有参与本次匹配的命名分组提供默认值 """          return  self.match .groupdict(*args, **kwargs)     def  _build_key_2_index (self ):         groups = self.match .groups()         group_2_index = []         for  idx, group in  enumerate (groups, start=1 ):             group_2_index.append((group, self.match .start(idx)+self.start_idx, self.match .end(idx)+self.start_idx))         return  group_2_index     def  groupdict_with_index (self, *args, **kwargs ):         """ 同groupdict, 但匹配结果包装为带索引的ContentWithIndex对象 """          value = self.match .groupdict(*args, **kwargs)         new_group_dict = dict ()         for  k, v in  value.items():             for  group, s, e in  self._build_key_2_index():                 if  v == group:                     new_group_dict[k] = ContentWithIndex(content=v, start=s, end=e)         assert  len (new_group_dict) == len (value)         return  new_group_dict     def  groups (self, *args, **kwargs ):         """ 以元组形式返回每一个分组匹配到的字符串,包括没有参与匹配的分组,其值为 default """          return  self.match .groups(*args, **kwargs)     def  span (self, *args, **kwargs ):         """ 返回指定分组的起止位置组成的元组,默认返回由 start() 和 end() 组成的元组 """          return  tuple ([(s + self.start_idx, e + self.start_idx) for  s, e in  self.match .span(*args, **kwargs)])     def  start (self, *args, **kwargs ):         """ 返回指定分组的开始位置,默认返回正则表达式所匹配到的第一个字符的索引 """          return  self.match .start(*args, **kwargs) + self.start_idx     def  start_and_end (self ):         return  self.start(), self.end()     def  __str__ (self ):         return  self.__repr__()     def  __repr__ (self ):         return  f"<regex.Match object; span=({self.start()} , {self.end()} ), match='{self.match .group()} '>"  
匹配结果类ContentWithIndex:主要是统一像split这样的方法返回的结果
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class  ContentWithIndex :    def  __init__ (self, content: str , start: int , end: int  ):         """          带索引的切分结果         :param content: 切分结果         :param start: 起始位置         :param end: 结束位置         """         self.content = content         self.start = start         self.end = end     @property     def  pos (self ):         return  self.start     @property     def  posend (self ):         return  self.end     def  start_and_end (self ):         return  self.start, self.end     def  __str__ (self ):         return  self.__repr__()     def  __repr__ (self ):         return  f"{self.content}  ({self.start} , {self.end} )"  
re包装的工具类ReUtils
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 class  ReUtils :    def  __init__ (self ):         """ 正则匹配的工具类 """          pass      @staticmethod     def  match (pattern, string, flags=0 , start: int  = 0  ):         match  = re.match (pattern=pattern, string=string, flags=flags)         return  ReMatch(match =match , start=start)     @staticmethod     def  fullmatch (pattern, string, flags=0 , start: int  = 0  ):         match  = re.fullmatch(pattern=pattern, string=string, flags=flags)         return  ReMatch(match =match , start=start)     @staticmethod     def  search (pattern, string, flags=0 , start: int  = 0  ):         match  = re.search(pattern=pattern, string=string, flags=flags)         return  ReMatch(match =match , start=start)     @staticmethod     def  sub (pattern, repl, string, count=0 , flags=0  ):         return  re.sub(pattern=pattern, repl=repl, string=string, count=count, flags=flags)     @staticmethod     def  subn (pattern, repl, string, count=0 , flags=0  ):         return  re.subn(pattern=pattern, repl=repl, string=string, count=count, flags=flags)     @staticmethod     def  split (pattern, string, maxsplit=0 , flags=0  ):         return  re.split(pattern=pattern, string=string, maxsplit=maxsplit, flags=flags)     @staticmethod     def  split_with_index (pattern, string, maxsplit: int  = 0 , flags=0 , start: int  = 0  ) -> List [ContentWithIndex]:         """          split带内容索引         :param pattern:         :param string:         :param maxsplit:         :param flags:         :param start:         :return:         """         matches = ReUtils.finditer(pattern, string, flags, start=start)           content_with_indexs = []         start_idx = start         for  match  in  matches:             end_idx = match .start()               content_with_index = ContentWithIndex(content=string[start_idx - start:end_idx - start], start=start_idx,                                                   end=end_idx)             content_with_indexs.append(content_with_index)               start_idx = match .end()               if  maxsplit and  len (content_with_indexs) >= maxsplit:                 content_with_index = ContentWithIndex(content=string[start_idx - start:], start=start_idx,                                                       end=len (string))                 content_with_indexs.append(content_with_index)                   break          else :                          content_with_index = ContentWithIndex(content=string[start_idx - start:], start=start_idx,                                                   end=len (string) + start)             content_with_indexs.append(content_with_index)                                             return  content_with_indexs     @staticmethod     def  findall (pattern, string, flags=0  ):         return  re.findall(pattern=pattern, string=string, flags=flags)     @staticmethod     def  finditer (pattern, string, flags=0 , start: int  = 0  ) -> Iterable[ReMatch]:         for  match  in  re.finditer(pattern=pattern, string=string, flags=flags):             yield  ReMatch(match =match , start=start)