正则表达式是一些由字符和特殊符号组成的字符串,用来匹配一系列有相似特征的字符串,也就是说从字符串中搜索字符串。
python中通过标准库的re模块来支持正则表达式
表示法 | 描述 | 实例 | |
foo | 匹配字符串的字面值 | foo | |
re1|re2 | 匹配正则表达式re1或re2 | foo|bar|a | 匹配字符串foo或字符串bar或字符串a,则一匹配表示从多个模式中匹配一个 |
. | 匹配任何一个字符(除 外) | b.b | 如果想匹配点号. 则需要使用转义. |
^ | 匹配字符串起始部分 | ^Dear | 以Dear作为起始的字符串,必须被查询的字符串一开头就是这个字符串才能匹配到 |
A | 同上 | ADear | |
$ | 匹配字符串终止部分 | /bin/tsh$ | 以/bin/tsh为结尾的字符串,必须被查询的字符串的结尾就是这个字符串才能匹配到,注意回车空格之类的字符 |
同上 | |||
* | 匹配0次或者多次前面出现的正则表达式 | [A-Za-z0-9]* | |
+ | 匹配1次或者多次前面出现的正则表达式 | [a-z]+.com | |
? | 匹配0次或者1次前面出现的正则表达式 | goo? | goo或者gooo |
{N} | 匹配N次前面出现的正则表达式 | [0-9]{3} | 匹配3位数的数字 |
{M,N} | 匹配M~N次前面出现的正则表达式 | [0-9]{5,9} | 配位5位数到9位数的数字 |
[...] | 匹配来自字符集的任意单一字符 | [aeiou] | a或者e或者i或者o或者u |
[..x-y..] | 匹配x~y范围中的任意单一字符 | [0-9],[A-Za-z] | 匹配一个数字,匹配一个字符 |
[^...] | 不匹配此字符集中出现的任何一个字符,包括某一范围的字符 | [^aeiou],[^A-Za-z-9] | |
(*|+|?|{})? | 匹配上面频繁出现/重复出现符号的非贪婪版本(*,+,?,{}) | .*?[a-z] | |
(...) | 匹配封闭的正则表达式,然后另存为子组 | ([0-9]{3})?,f(oo|u)bar | |
N | 匹配已保存的子组 | ||
d | 匹配任何十进制数字,与[0-9]一致 | datad+.txt | |
D | 不匹配任何十进制数字 | ||
w | 匹配任何字母数字字符,与[A-Za-z0-9_]相同 | [A-Za-z_]w+ | |
W | 不匹配任何字母数字字符 | ||
s | 匹配任何空格字符,与[ vf]相同 | ofsthe | |
S | 不匹配任何空格字符 | ||
匹配任何单词的边界 | r'The' | 匹配字符串中含有以The为开头的字符串,必须用r作为前缀,因为在python中是回退符 | |
B | 匹配任何不是单词的边界 | Bthe | 任何包含但并不以the作为起始的字符串,例如aaathebbb |
c | 逐字匹配任何特殊字符 | .,\,* | |
(?iLmsux) | 在正则表达式中嵌入一个或者多个特殊标记参数 | (?x),(? im) | |
(?....) | 表示一个匹配不用保存的分组 | (?:w+.)* | |
(?<name>exp) | 匹配exp,并捕获文本到名称为name的组里 | (?<data>.$) | 用.$进行匹配,匹配到的文本存储到名字为data的组里 |
(?P=name) | 在同一字符串中匹配由(?P<name)分组的之前文本 | (?P=data) | 匹配组名叫做data的字符串,连接上一个正则看 |
(?#...) | 表示注释,所有内容都被忽略) | (?#comment) | 此处不做匹配,只是作为注释 |
(?=...) | 匹配条件是如果...出现在之后的位置,而不适用输入字符串;称作正向前视断言 | "(?<txt>.+(?=ing))" | 如 "How are you doing" 这里取ing前所有的字符,并定义了一个捕获分组名字为 "txt" 而"txt"这个组里的值为"How are you do"; |
(?!...) | 匹配条件是如果...不出现在之后的位置,而不适用输入字符串;称作负向前视断言 | d{3}(?!d) | 如 "123abc" 匹配3位数字后非数字的结果 |
(?<=...) | 匹配条件是如果...出现在之前的位置,而不适用输入字符串;称作正向前视断言 | (?<txt>(?<=How).+) | 如 "How are you doing" 这里取"How"之后所有的字符,并定义了一个捕获分组名字为 "txt" 而"txt"这个组里的值为" are you doing"; |
(?<!...) | 匹配条件是如果...不出现在之前的位置,而不适用输入字符串;称作负向前视断言 | (?<![0-9])123 | 匹配"123"前面是非数字的结果 |
(?(id/name)Y/N) | 如果分组所提供的id或者name存在,就返回正则表达式的条件匹配Y,如果不存在,就返回N | (?(1)y|x) | |
(?i) | 忽略后面字符串的大小写 | ||
(?:exp) | 匹配exp,不捕获匹配的文本,也不给此分组分配组号 | ||
(exp) | 匹配exp,并捕获文本到自动命名的组里 |
匹配制表符, 匹配回车,*匹配星星,.匹配点,\匹配反斜线,2匹配第二个分组(例如正则(tina)(fei)haha2 完全匹配字符串tinafeihahafei,用第二组匹配到的字符串)
Unix系统:换行是
windows系统:换行是 ( 回车表示光标移到行首, 换行表示光标移到下一行)
Mac系统:换行是
Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix下打开的话,在每行的结尾会多车一个^M字符。
(xxx)小括号-对正则表达式进行分组,对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。例如([abc].+)#1(.?a*) 第一个括号是第一组,第二个括号是第二组,可以分别单独提取出来
[xxx]中括号-匹配里面任意一个字符,[^xx] 不匹配里面任意一个字符
{xxx}大括号-里面是数字,或者用逗号分隔的数字,例如{3}或{3,9},匹配前面出现的字符的次数范围
xx|xx管道符号-匹配前者或匹配后者
*和+和?都是属于贪婪模式,他们会尽可能多的匹配文字,只有在他们后面加上一个?就可以实现非贪婪模式或最小匹配
分组:
捕获组可以通过从左到右计算其开括号来编号 。例如,在表达式 (A)(B(C)) 中,存在四个这样的组:
0 |
(A)(B(C)) |
1 |
(A) |
2 |
(B(C)) |
3 |
(C) |
组零始终代表整个表达式
反向引用,引用的是前面捕获组中的文本而不是正则,也就是说反向引用处匹配的文本应和前面捕获组中的文本相同,这一点很重要。1就是匹配编号1的分组所匹配到的内!容!
以 (?) 开头的组是纯的非捕获 组,它不捕获文本 ,也不针对组合计进行计数。就是说,如果小括号中以?号开头,那么这个分组就不会捕获文本,当然也不会有组的编号,因此也不存在Back 引用。
group()返回整个匹配对象,或者返回特定子组
groups()返回一个包含唯一或者全部子组的元祖
match(),从字符串的起始部分对模式进行匹配,匹配成功返回一个匹配对象地址,匹配失败返回None
re.mathc(正则表达式,字符串).group() 返回完整匹配
re.match().group(1) 返回第一组数据
re.match().groups() 返回全部子组('xx','xxxx')
search(),要搜索的字符串在中间部分的概率大于在起始部分的概率就用search(),对给定正则表达式搜索第一次出现的匹配情况,搜索到成功的匹配返回一个匹配对象地址,否则返回None
re.search(正则,字符串).group()
findall(),匹配全部的非重复出现的字符,与search类似,但是它返回一个列表,如果没有找到匹配的部分,就返回一个空列表
re.findall(正则,字符串) 返回['xxx']
re.findall(r'(thw+) and (thw+)','this and that',re.I) 返回[('this','that')]
re.findall(r'(thw+) and (thw+)','this and that',re.I).next().groups() 返回('this','that')
re.findall(r'(thw+) and (thw+)','this and that',re.I).next().group(1) 返回‘this’
-
- (?iLmsux) 分组中可以设置模式,iLmsux之中的每个字符代表一个模式,用法参见 模式 I
- (?:...) 分组的不捕获模式,计算索引时会跳过这个分组
- (?P<name>...) 分组的命名模式,取此分组中的内容时可以使用索引也可以使用name
- (?P=name) 分组的引用模式,可在同一个正则表达式用引用前面命名过的正则
- (?#...) 注释,不影响正则表达式其它部分,用法参见 模式 I
- (?=...) 顺序肯定环视,表示所在位置右侧能够匹配括号内正则
- (?!...) 顺序否定环视,表示所在位置右侧不能匹配括号内正则
- (?<=...) 逆序肯定环视,表示所在位置左侧能够匹配括号内正则
- (?<!...) 逆序否定环视,表示所在位置左侧不能匹配括号内正则
- (?(id/name)yes|no) 若前面指定id或name的分区匹配成功则执行yes处的正则,否则执行no处的正则
二、模式
-
- I IGNORECASE, 忽略大小写的匹配模式, 样例如下
s = 'hello World!' regex = re.compile("hello world!", re.I) print regex.match(s).group() #output> 'Hello World!' #在正则表达式中指定模式以及注释 regex = re.compile("(?#注释)(?i)hello world!") print regex.match(s).group() #output> 'Hello World!'
- L LOCALE, 字符集本地化。这个功能是为了支持多语言版本的字符集使用环境的,比如在转义符w,在英文环境下,它代表[a-zA-Z0-9_],即所以英文字符和数字。如果在一个法语环境下使用,缺省设置下,不能匹配"é" 或 "ç"。加上这L选项和就可以匹配了。不过这个对于中文环境似乎没有什么用,它仍然不能匹配中文字符。
- M MULTILINE,多行模式, 改变 ^ 和 $ 的行为
s = '''first line second line third line''' # ^ regex_start = re.compile("^w+") print regex_start.findall(s) # output> ['first'] regex_start_m = re.compile("^w+", re.M) print regex_start_m.findall(s) # output> ['first', 'second', 'third'] #$ regex_end = re.compile("w+$") print regex_end.findall(s) # output> ['line'] regex_end_m = re.compile("w+$", re.M) print regex_end_m.findall(s) # output> ['line', 'line', 'line']
- S DOTALL,此模式下 '.' 的匹配不受限制,可匹配任何字符,包括换行符
s = '''first line second line third line''' # regex = re.compile(".+") print regex.findall(s) # output> ['first line', 'second line', 'third line'] # re.S regex_dotall = re.compile(".+", re.S) print regex_dotall.findall(s) # output> ['first line second line third line']
- X VERBOSE,冗余模式, 此模式忽略正则表达式中的空白和#号的注释,例如写一个匹配邮箱的正则表达式
email_regex = re.compile("[w+.]+@[a-zA-Zd]+.(com|cn)") email_regex = re.compile("""[w+.]+ # 匹配@符前的部分 @ # @符 [a-zA-Zd]+ # 邮箱类别 .(com|cn) # 邮箱后缀 """, re.X)
- I IGNORECASE, 忽略大小写的匹配模式, 样例如下
-
- U UNICODE,使用 w, W, , B 这些元字符时将按照 UNICODE 定义的属性.
正则表达式的模式是可以同时使用多个的,在 python 里面使用按位或运算符 | 同时添加多个模式
如 re.compile('', re.I|re.M|re.S)
每个模式在 re 模块中其实就是不同的数字
print re.I # output> 2 print re.L # output> 4 print re.M # output> 8 print re.S # output> 16 print re.X # output> 64 print re.U # output> 32
三、函数 (参见 python 模块 re 文档)
python 的 re 模块提供了很多方便的函数使你可以使用正则表达式来操作字符串,每种函数都有它自己的特性和使用场景,熟悉之后对你的工作会有很大帮助
-
- compile(pattern, flags=0)
给定一个正则表达式 pattern,指定使用的模式 flags 默认为0 即不使用任何模式,然后会返回一个 SRE_Pattern (参见 第四小节 re 内置对象用法) 对象
regex = re.compile(".+") print regex # output> <_sre.SRE_Pattern object at 0x00000000026BB0B8>
这个对象可以调用其他函数来完成匹配,一般来说推荐使用 compile 函数预编译出一个正则模式之后再去使用,这样在后面的代码中可以很方便的复用它,当然大部分函数也可以不用 compile 直接使用,具体见 findall 函数
s = '''first line second line third line''' # regex = re.compile(".+") # 调用 findall 函数 print regex.findall(s) # output> ['first line', 'second line', 'third line'] # 调用 search 函数 print regex.search(s).group() # output> first lin
-
- escape(pattern)
转义 如果你需要操作的文本中含有正则的元字符,你在写正则的时候需要将元字符加上反斜扛 去匹配自身, 而当这样的字符很多时,写出来的正则表达式就看起来很乱而且写起来也挺麻烦的,这个时候你可以使用这个函数,用法如下
s = ".+d123" # regex_str = re.escape(".+d123") # 查看转义后的字符 print regex_str # output> .+\d123 # 查看匹配到的结果 for g in re.findall(regex_str, s): print g # output> .+d123
-
- findall(pattern, string, flags=0)
参数 pattern 为正则表达式, string 为待操作字符串, flags 为所用模式,函数作用为在待操作字符串中寻找所有匹配正则表达式的字串,返回一个列表,如果没有匹配到任何子串,返回一个空列表。
s = '''first line second line third line''' # compile 预编译后使用 findall regex = re.compile("w+") print regex.findall(s) # output> ['first', 'line', 'second', 'line', 'third', 'line'] # 不使用 compile 直接使用 findall print re.findall("w+", s) # output> ['first', 'line', 'second', 'line', 'third', 'line']
-
- finditer(pattern, string, flags=0)
参数和作用与 findall 一样,不同之处在于 findall 返回一个列表, finditer 返回一个迭代器(参见http://www.cnblogs.com/huxi/archive/2011/07/01/2095931.html ), 而且迭代器每次返回的值并不是字符串,而是一个 SRE_Match (参见 第四小节 re 内置对象用法) 对象,这个对象的具体用法见 match 函数。
s = '''first line second line third line''' regex = re.compile("w+") print regex.finditer(s) # output> <callable-iterator object at 0x0000000001DF3B38> for i in regex.finditer(s): print i # output> <_sre.SRE_Match object at 0x0000000002B7A920> # <_sre.SRE_Match object at 0x0000000002B7A8B8> # <_sre.SRE_Match object at 0x0000000002B7A920> # <_sre.SRE_Match object at 0x0000000002B7A8B8> # <_sre.SRE_Match object at 0x0000000002B7A920> # <_sre.SRE_Match object at 0x0000000002B7A8B8>
-
- match(pattern, string, flags=0)
使用指定正则去待操作字符串中寻找可以匹配的子串, 返回匹配上的第一个字串,并且不再继续找,需要注意的是 match 函数是从字符串开始处开始查找的,如果开始处不匹配,则不再继续寻找,返回值为 一个 SRE_Match (参见 第四小节 re 内置对象用法) 对象,找不到时返回 None
s = '''first line second line third line''' # compile regex = re.compile("w+") m = regex.match(s) print m # output> <_sre.SRE_Match object at 0x0000000002BCA8B8> print m.group() # output> first # s 的开头是 "f", 但正则中限制了开始为 i 所以找不到 regex = re.compile("^iw+") print regex.match(s) # output> None
-
- purge()
当你在程序中使用 re 模块,无论是先使用 compile 还是直接使用比如 findall 来使用正则表达式操作文本,re 模块都会将正则表达式先编译一下, 并且会将编译过后的正则表达式放到缓存中,这样下次使用同样的正则表达式的时候就不需要再次编译, 因为编译其实是很费时的,这样可以提升效率,而默认缓存的正则表达式的个数是 100, 当你需要频繁使用少量正则表达式的时候,缓存可以提升效率,而使用的正则表达式过多时,缓存带来的优势就不明显了 (参考 《python re.compile对性能的影响》http://blog.trytofix.com/article/detail/13/), 这个函数的作用是清除缓存中的正则表达式,可能在你需要优化占用内存的时候会用到。
-
- search(pattern, string, flags=0)
函数类似于 match,不同之处在于不限制正则表达式的开始匹配位置
s = '''first line second line third line''' # 需要从开始处匹配 所以匹配不到 print re.match('iw+', s) # output> None # 没有限制起始匹配位置 print re.search('iw+', s) # output> <_sre.SRE_Match object at 0x0000000002C6A920> print re.search('iw+', s).group() # output> irst
-
- split(pattern, string, maxsplit=0, flags=0)
参数 maxsplit 指定切分次数, 函数使用给定正则表达式寻找切分字符串位置,返回包含切分后子串的列表,如果匹配不到,则返回包含原字符串的一个列表
s = '''first 111 line second 222 line third 333 line''' # 按照数字切分 print re.split('d+', s) # output> ['first ', ' line second ', ' line third ', ' line'] # .+ 匹配不到 返回包含自身的列表 print re.split('.+', s, 1) # output> ['first 111 line second 222 line third 333 line'] # maxsplit 参数 print re.split('d+', s, 1) # output> ['first ', ' line second 222 line third 333 line']
-
- sub(pattern, repl, string, count=0, flags=0)
替换函数,将正则表达式 pattern 匹配到的字符串替换为 repl 指定的字符串, 参数 count 用于指定最大替换次数
s = "the sum of 7 and 9 is [7+9]." # 基本用法 将目标替换为固定字符串 print re.sub('[7+9]', '16', s) # output> the sum of 7 and 9 is 16. # 高级用法 1 使用前面匹配的到的内容 1 代表 pattern 中捕获到的第一个分组的内容 print re.sub('[(7)+(9)]', r'21', s) # output> the sum of 7 and 9 is 97. # 高级用法 2 使用函数型 repl 参数, 处理匹配到的 SRE_Match 对象 def replacement(m): p_str = m.group() if p_str == '7': return '77' if p_str == '9': return '99' return '' print re.sub('d', replacement, s) # output> the sum of 77 and 99 is [77+99]. # 高级用法 3 使用函数型 repl 参数, 处理匹配到的 SRE_Match 对象 增加作用域 自动计算 scope = {} example_string_1 = "the sum of 7 and 9 is [7+9]." example_string_2 = "[name = 'Mr.Gumby']Hello,[name]" def replacement(m): code = m.group(1) st = '' try: st = str(eval(code, scope)) except SyntaxError: exec code in scope return st # 解析: code='7+9' # str(eval(code, scope))='16' print re.sub('[(.+?)]', replacement, example_string_1) # output> the sum of 7 and 9 is 16.
# 两次替换 # 解析1: code="name = 'Mr.Gumby'" # eval(code) # raise SyntaxError # exec code in scope # 在命名空间 scope 中将 "Mr.Gumby" 赋给了变量 name # 解析2: code="name" # eval(name) 返回变量 name 的值 Mr.Gumby print re.sub('[(.+?)]', replacement, example_string_2) # output> Hello,Mr.Gumby
-
- subn(pattern, repl, string, count=0, flags=0)
作用与函数 sub 一样, 唯一不同之处在于返回值为一个元组,第一个值为替换后的字符串,第二个值为发生替换的次数
-
- template(pattern, flags=0)
这个吧,咋一看和 compile 差不多,不过不支持 +、?、*、{} 等这样的元字符,只要是需要有重复功能的元字符,就不支持,查了查资料,貌似没人知道这个函数到底是干嘛的...
四、re 内置对象用法
-
- SRE_Pattern 这个对象是一个编译后的正则表达式,编译后不仅能够复用和提升效率,同时也能够获得一些其他的关于正则表达式的信息
属性:
- flags 编译时指定的模式
- groupindex 以正则表达式中有别名的组的别名为键、以该组对应的编号为值的字典,没有别名的组不包含在内。
- groups 正则表达式中分组的数量
- pattern 编译时用的正则表达式
s = 'Hello, Mr.Gumby : 2016/10/26' p = re.compile('''(?: # 构造一个不捕获分组 用于使用 | (?P<name>w+.w+) # 匹配 Mr.Gumby | # 或 (?P<no>s+.w+) # 一个匹配不到的命名分组 ) .*? # 匹配 : (d+) # 匹配 2016 ''', re.X) # print p.flags # output> 64 print p.groupindex # output> {'name': 1, 'no': 2} print p.groups # output> 3 print p.pattern # output> (?: # 构造一个不捕获分组 用于使用 | # (?P<name>w+.w+) # 匹配 Mr.Gumby # | # 或 # (?P<no>s+.w+) # 一个匹配不到的命名分组 # ) # .*? # 匹配 : # (d+) # 匹配 2016
函数:可使用 findall、finditer、match、search、split、sub、subn 等函数
-
- SRE_Match 这个对象会保存本次匹配的结果,包含很多关于匹配过程以及匹配结果的信息
属性:
- endpos 本次搜索结束位置索引
- lastgroup 本次搜索匹配到的最后一个分组的别名
- lastindex 本次搜索匹配到的最后一个分组的索引
- pos 本次搜索开始位置索引
- re 本次搜索使用的 SRE_Pattern 对象
- regs 列表,元素为元组,包含本次搜索匹配到的所有分组的起止位置
- string 本次搜索操作的字符串
s = 'Hello, Mr.Gumby : 2016/10/26'
m = re.search(', (?P<name>w+.w+).*?(d+)', s)
# 本次搜索的结束位置索引
print m.endpos
# output> 28
# 本次搜索匹配到的最后一个分组的别名
# 本次匹配最后一个分组没有别名
print m.lastgroup
# output> None
# 本次搜索匹配到的最后一个分组的索引
print m.lastindex
# output> 2
# 本次搜索开始位置索引
print m.pos
# output> 0
# 本次搜索使用的 SRE_Pattern 对象
print m.re
# output> <_sre.SRE_Pattern object at 0x000000000277E158>
# 列表,元素为元组,包含本次搜索匹配到的所有分组的起止位置 第一个元组为正则表达式匹配范围
print m.regs
# output> ((7, 22), (7, 15), (18, 22))
# 本次搜索操作的字符串
print m.string
# output> Hello, Mr.Gumby : 2016/10/26
函数:
- end([group=0]) 返回指定分组的结束位置,默认返回正则表达式所匹配到的最后一个字符的索引
- expand(template) 根据模版返回相应的字符串,类似与 sub 函数里面的 repl, 可使用 1 或者 g<name> 来选择分组
- group([group1, ...]) 根据提供的索引或名字返回响应分组的内容,默认返回 start() 到 end() 之间的字符串, 提供多个参数将返回一个元组
- groupdict([default=None]) 返回 返回一个包含所有匹配到的命名分组的字典,没有命名的分组不包含在内,key 为组名, value 为匹配到的内容,参数 default 为没有参与本次匹配的命名分组提供默认值
- groups([default=None]) 以元组形式返回每一个分组匹配到的字符串,包括没有参与匹配的分组,其值为 default
- span([group]) 返回指定分组的起止位置组成的元组,默认返回由 start() 和 end() 组成的元组
- start([group]) 返回指定分组的开始位置,默认返回正则表达式所匹配到的第一个字符的索引
s = '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 m.end() # output> 22 # 根据模版返回相应的字符串,类似与 sub 函数里面的 repl, 可使用 1 或者 g<name> 来选择分组 print m.expand("my name is \1") # output> my name is Mr.Gumby # 根据提供的索引或名字返回响应分组的内容,默认返回 start() 到 end() 之间的字符串, 提供多个参数将返回一个元组 print m.group() # output> Mr.Gumby : 2016 print m.group(1,2) # output> ('Mr.Gumby', None) # 返回 返回一个包含所有匹配到的命名分组的字典,没有命名的分组不包含在内,key 为组名, value 为匹配到的内容,参数 default 为没有参与本次匹配的命名分组提供默认值 print m.groupdict('default_string') # output> {'name': 'Mr.Gumby', 'no': 'default_string'} # 以元组形式返回每一个分组匹配到的字符串,包括没有参与匹配的分组,其值为 default print m.groups('default_string') # output> ('Mr.Gumby', 'default_string', '2016') # 返回指定分组的起止未知组成的元组,默认返回由 start() 和 end() 组成的元组 print m.span(3) # output> (18, 22) # 返回指定分组的开始位置,默认返回正则表达式所匹配到的第一个字符的索引 print m.start(3) # output> 18
五、分组用法
python 的正则表达式中用小括号 "(" 表示分组,按照每个分组中前半部分出现的顺序 "(" 判定分组的索引,索引从 1 开始,每个分组在访问的时候可以使用索引,也可以使用别名
s = 'Hello, Mr.Gumby : 2016/10/26' p = re.compile("(?P<name>w+.w+).*?(d+)(?#comment)") m = p.search(s) # 使用别名访问 print m.group('name') # output> Mr.Gumby # 使用分组访问 print m.group(2) # output> 2016
有时候可能只是为了把正则表达式分组,而不需要捕获其中的内容,这时候可以使用非捕获分组
s = 'Hello, Mr.Gumby : 2016/10/26' p = re.compile(""" (?: # 非捕获分组标志 用于使用 | (?P<name>w+.w+) | (d+/) ) """, re.X) m = p.search(s) # 使用非捕获分组 # 此分组将不计入 SRE_Pattern 的 分组计数 print p.groups # output> 2 # 不计入 SRE_Match 的分组 print m.groups() # output> ('Mr.Gumby', None)
如果你在写正则的时候需要在正则里面重复书写某个表达式,那么你可以使用正则的引用分组功能,需要注意的是引用的不是前面分组的 正则表达式 而是捕获到的 内容,并且引用的分组不算在分组总数中.
s = 'Hello, Mr.Gumby : 2016/2016/26' p = re.compile(""" (?: # 非捕获分组标志 用于使用 | (?P<name>w+.w+) | (d+/) ) .*?(?P<number>d+)/(?P=number)/ """, re.X) m = p.search(s) # 使用引用分组 # 此分组将不计入 SRE_Pattern 的 分组计数 print p.groups # output> 3 # 不计入 SRE_Match 的分组 print m.groups() # output> ('Mr.Gumby', None, '2016') # 查看匹配到的字符串 print m.group() # output> Mr.Gumby : 2016/2016/
六、环视用法
环视还有其他的名字,例如 界定、断言、预搜索等,叫法不一。
环视是一种特殊的正则语法,它匹配的不是字符串,而是 位置,其实就是使用正则来说明这个位置的左右应该是什么或者应该不是什么,然后去寻找这个位置。
环视的语法有四种,见第一小节元字符,基本用法如下。
s = 'Hello, Mr.Gumby : 2016/10/26 Hello,r.Gumby : 2016/10/26' # 不加环视限定 print re.compile("(?P<name>w+.w+)").findall(s) # output> ['Mr.Gumby', 'r.Gumby'] # 环视表达式所在位置 左边为 "Hello, " print re.compile("(?<=Hello, )(?P<name>w+.w+)").findall(s) # output> ['Mr.Gumby'] # 环视表达式所在位置 左边不为 "," print re.compile("(?<!,)(?P<name>w+.w+)").findall(s) # output> ['Mr.Gumby'] # 环视表达式所在位置 右边为 "M" print re.compile("(?=M)(?P<name>w+.w+)").findall(s) # output> ['Mr.Gumby'] # 环视表达式所在位置 右边不为 r print re.compile("(?!r)(?P<name>w+.w+)").findall(s) # output> ['Mr.Gumby']
高级一些的例子参见《正则基础之——环视(Lookaround)》(http://www.cnblogs.com/kernel0815/p/3375249.html)