一 python正则简介
就其本质而言,正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。
正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。
正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。得益于这一点,在提供了正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同;但不用担心,不被支持的语法通常是不常用的部分。如果已经在其他语言里使用过正则表达式,只需要简单看一看就可以上手了。
下图展示了使用正则表达式进行匹配的流程:

正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。如果表达式中有量词或边界,这个过程会稍微有一些不同,但也是很好理解的,看下图中的示例以及自己多使用几次就能明白。
二 python正则的字符
1、正则表达式导入:
python中re模块提供了正则表达式相关操作,故我们只需要导入re模块就行 导入方法: import re
2、普通字符:
大多数字符和字母都会和自身匹配
import re #导入re模块 r=re.findall('alex','yuanaleSxalexwupeiqi') print(r) #执行结果:['alex']
3、原生字符r:
首先,我们来看普通字符串,有如下例子:
>>>foo = "hello" >>>bar = r"hello"
前者是常规字符串,后者 r 开头的是原始字符串,两者有什么区别?因为在上面的例子中,它们都是由普通文本字符组成的串,在这里没什么区别,下面可以证明:
>>> foo is bar True >>> foo == bar True
其次,如果字符串中包括有特殊字符,会是什么情况呢?再来看一个例子:
>>> foo = "\n" >>> bar = r"\n" >>> foo, len(foo) ('\n', 1) >>> bar, len(bar) ('\\n', 2) >>> foo == bar False >>>
"\n" 是一个转义字符,它在 ASCII 中表示换行符。而 r"\n" 是一个原始字符串,即不对特殊字符进行转义,它就是你看到的字面意思,由 "\" 和 "n" 两个字符组成的字符串。
定义原始字符串可以用小写r或者大写R开头,比如 r"\b" 或者 R"\b" 都是允许的。在 Python 中,正则表达式一般用原始字符串的形式来定义,为什么呢?
举例来说,对于字符 "\b" 来说,它在 ASCII 中是有特殊意义的,表示退格键。而在正则表达式中,它是一个特殊的元字符,用于匹配一个单词的边界,为了能让正则编译器正确地表达它的意义就需要用原始字符串,当然也可以使用反斜杠 "\" 对常规定义的字符串进行转义
>>> foo = "\\b" >>> bar = r"\b" >>> foo == bar True
总结一点:在正则表达式中,总是加上r是不会错的选择
4、基本元字符:
字符:用于代替一个字符 . 匹配除换行符以外的任意字符 \ 转义字符,使特殊字符具有本来的意义 如: 1\.2 可以匹配 1.2 ^ 匹配字符串的开始 $ 匹配字符串的结束 [] 指定范围内的的任意单个字符 如:[0-9]: 0到9的数字 [q_u]:匹配q/_/u三个元素中任意一个 [^..] 字符集取反,表示只要不是括号中出现的字符都可以匹配,注意:在[]里只有^/-/[]以及\有特殊含义。 如:a[^bcd]e 可匹配 aee、afe等
>>> re.match(r"a.c", "abc").group() #用.匹配一个字符 'abc' >>> re.match(r"a.c", "abcef").group() 'abc' >>> re.match(r"1\.2", "1.2").group() #用转义符\将.变为本来意义 '1.2' >>> re.match(r"a[0-9]b", "a2b").group() #用[]来匹配一个范围内的字符 'a2b' >>> re.match(r"a[0-9]b", "a5b11").group() 'a5b' >>> re.match(r"a[.*?]b", "a.b").group() 'a.b' >>> re.match(r"abc[^\w]", "abc!123").group() 'abc!
5、转义元字符:
元字符\ :对一些字母转义,并赋予特殊的意义 \d 匹配任何十进制数;它相当于类 [0-9]。 \D 匹配任何非数字字符;它相当于类 [^0-9]。 \s 匹配任何空白字符;它相当于类 [ \t\n\r\f\v]。 \S 匹配任何非空白字符;它相当于类 [^ \t\n\r\f\v]。 \w 匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]。 \W 匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_] \b 匹配一个单词边界,也就是指单词和空格间的位置。(详见“位置锚定”)
>>> a = "123 ..*#$% ABC abc"<br>>>> re.findall("\d",a) ['1', '2', '3'] >>> re.findall(r"\s",a) [' ', ' ', ' '] >>> re.findall(r"\w",a) ['1', '2', '3', 'A', 'B', 'C', 'a', 'b', 'c'] >>> re.findall(r"\D",a) [' ', '.', '.', '*', '#', '$', '%', ' ', 'A', 'B', 'C', ' ', 'a', 'b', 'c'] >>> re.findall(r"\S",a) ['1', '2', '3', '.', '.', '*', '#', '$', '%', 'A', 'B', 'C', 'a', 'b', 'c'] >>> re.findall(r"\W",a) [' ', '.', '.', '*', '#', '$', '%', ' ', ' '] >>>re.findall(r"\babc\b","abc abcdeafefeabc")
["abc"
6、边界匹配:
位置锚定: 用于指定字符出现的位置 ^ 用于锚定行首 如:^Char 以Char开头 $ 锚定行尾,注意只是行尾 如:char$ 以char结尾 ^$ 空白行 \b 锚定词首 如:\bchar char单词的c字母是与空格连在一起的 锚定词尾 如:char\b char单词的r字母是与空格连在一起的 如: \bhello\b 匹配hello单词
>>> re.match(r"^abc","abc").group() 'abc' >>> re.match(r"abc$","sdsdabc").group() 'abc'
7、重复匹配:
次数:用于描述一个元素出现的次数 * 重复0次或更多次 + 重复1次或更多次 ? 重复0次或1次 {n} 重复n次 {n, } 重复n次或更多次 {n, m}重复n到m次
# 简单匹配身份证号码,前面17位是数字,最后一位可以是数字或者字母X >>> re.match(r"\d{17}[\dX]", "42350119900101153X").group() '42350119900101153X' # 匹配5到12的QQ号码 >>> re.match(r"\d{5,12}$", "4235011990").group() '4235011990' >>>re.findall(r"abc*","sds dab abcdfeabccc") ['ab', 'abc', 'abccc'] >>>re.findall(r"abc+","sds dab abcdfeabccc") ['abc', 'abccc'] >>>re.findall(r"abc?","sds dab abcdfeabccc") ['ab', 'abc', 'abc'] >>>
8、逻辑匹配:
逻辑分支条件字符 |,它把表达式分为左右两部分,先尝试匹配左边部分,如果匹配成功就不再匹配后面部分了,这是逻辑 "或" 的关系
# abc|cde 可以匹配abc 或者 cde,但优先匹配abc >>> re.match(r"aa(abc|cde)","aaabccde").group() 'aaabc' #0\d{2}-\d{8}|0\d{3}-\d{7} 表达式以0开头,既可以匹配3位区号8位号码,也可以匹配4位区号7位号码 >>> re.match(r"0\d{2}-\d{8}|0\d{3}-\d{7}", "0755-4348767").group() '0755-4348767' >>> re.match(r"0\d{2}-\d{8}|0\d{3}-\d{7}", "010-34827637").group() '010-34827637'
9、分组:
前面介绍的匹配规则都是针对单个字符而言的,如果想要重复匹配多个字符怎么办,答案是,用子表达式(也叫分组)来表示,分组用小括号"()"表示,例如 (abc){2} 表示匹配abc两次。
关于分组,group 方法可用于提取匹配的字符串分组,默认它会把整个表达式的匹配结果当做第0个分组,就是不带参数的 group() 或者是 group(0)。但是,group(1)获取第一组括号中的内容,以此类推
>>> m = re.match(r"(\d+)(\w+)", "123abc") #分组0,匹配整个正则表达式 >>> m.group() '123abc' #等价 >>> m.group(0) '123abc' # 分组1,匹配第一对括号 >>> m.group(1) '123' # 分组2,匹配第二对括号 >>> m.group(2) 'abc'
#注意区别:
import re origin = "hello ale1x bcd abcd lge ale2x acd 19" #此时,*匹配\w。代表可以有0到n个\w元素 r=re.findall(r"a\w*",origin) print(r) #执行结果:['alex', 'abcd', 'acd'] #此时,*同样匹配的是\w,不同的是findall只是输出分组中的内容 r=re.findall(r"a(\w*)",origin)print(r) #执行结果:['lex', 'bcd', 'cd'] #此时,*匹配的是\w这个分组,即等同于可以有0到n个(\w)如:(\w)(\w)(\w)...。此时python的re模块中有一个规则就是,当书写成"(\dalex)*"。只会匹配输出最后一次出现的 r=re.findall(r"(\dalex){2}","hello 2alex3alex lge 4alex 19") #目的匹配的是:“2alex3alex”,但是在取值的时候,只会提取最后一次出现的 print(r) #执行结果:['3alex']
三、正则函数的应用:
1、match:
re.match() #只匹配开头,如果开头没有匹配到对象就返回NONE
#未分组 import re origin = "hello alex bcd abcd lge acd 19" r=re.match("h\w*",origin) print(r) #执行结果:<_sre.SRE_Match object; span=(0, 5), match='hello'> # group() 就是从match对象中取出元素 print(r.group()) #执行结果:hello #因groups()和groupdict()都是提取分组元素的,故为空 print(r.groups()) # 执行结果:() print(r.groupdict())#执行结果:{} #分组 import re origin = "hello alex bcd abcd lge acd 19" #(\w)将\w括起来,就是代表对\w进行分组,分组的意义是:在匹配成功的范围内,再进行匹配。 r=re.match("h(\w*)",origin) print(r) #执行结果:<_sre.SRE_Match object; span=(0, 5), match='hello'> print(r.group()) #执行结果:hello print(r.groups())#执行结果:('ello',) #注意?P<name> 的意思是:对分组的内容定一个key值, r=re.match("h(?P<name>\w*)",origin) print(r.groupdict()) #执行结果:{'name': 'ello'}
2、search:
浏览整个字符串去匹配第一个,未匹配成功返回None
#未分组 import re origin = "hello alex bcd abcd lge acd 19" r=re.search("a\w*",origin) print(r) #执行结果:<_sre.SRE_Match object; span=(6, 10), match='alex'> print(r.group()) #执行结果:alex print(r.groups()) #执行结果:() print(r.groupdict()) #执行结果:{} #分组 import re origin = "hello alex bcd abcd lge acd 19" r=re.search("a(\w*)",origin) print(r) #执行结果:<_sre.SRE_Match object; span=(6, 10), match='alex'> print(r.group()) #执行结果:alex print(r.groups()) #执行结果:('lex',) r=re.search("h(?P<name>\w*)",origin) print(r.groupdict()) #执行结果:{'name': 'ello'}
3、findall:
将匹配到的所有内容都放置在一个列表中
#未分组 import re origin = "hello alex bcd abcd lge acd 19" r=re.findall("a\w*",origin) print(r) #执行结果:['alex', 'abcd', 'acd'] #print(r.group()) #没有这种用法 #分组 import re origin = "hello alex bcd abcd lge acd 19" r=re.findall("a(\w*)",origin) print(r) #执行结果:['lex', 'bcd', 'cd'] #print(r.groups()) #没有这种用法
4、finditer:
将匹配到的所有内容都放置在迭代器中
#未分组 import re origin = "hello alex bcd abcd lge acd 19" #注意:finditer 以iter结尾的单词,iter在大多数情况下表示的可迭代的意思 r=re.finditer("a\w*",origin) print(r) #执行结果:<callable_iterator object at 0x0038F370> for i in r: print(i.group()) """ 执行结果: <callable_iterator object at 0x006FF890> alex abcd acd """ #分组 import re origin = "hello alex bcd abcd lge acd 19" #注意:finditer 以iter结尾的单词,iter在大多数情况下表示的可迭代的意思 r=re.finditer("a(\w*)",origin) print(r) #执行结果:<callable_iterator object at 0x0051F370> for i in r: print(i.group(),i.groups()) r=re.finditer("a(?P<name>\w*)",origin) for i in r: print(i.groupdict()) """ 执行结果: <callable_iterator object at 0x00708A50> alex ('lex',) abcd ('bcd',) acd ('cd',) {'name': 'lex'} {'name': 'bcd'} {'name': 'cd'} """
5、split:
分割,注意分割时加有分组和未分组的区别
import re origin = "hello alex bcd abcd lge acd 19"
#未分组 #未分组的是不会将匹配的内容输出的 r=re.split("a\w+",origin,1) #注意这个flags=1,表示的是分割的次数。不填写默认为全部分割 print(r) #执行结果:['hello ', ' bcd abcd lge acd 19'] #分组 #分组了,会将匹配的内容一同输出
r=re.split("a(\w+)",origin) print(r) #执行结果:['hello ', 'lex', ' bcd ', 'bcd', ' lge ', 'cd', ' 19']
练习题:计算器
#计算器
import re
origin="1 - 2 * ((60-30+1*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))"
while True:
r=re.split("\(([^()]+)\)",origin,1) #注意这个flags=1,表示的是分割的次数。不填写默认为全部分割
if len(r)==3:
r[1]=str(eval(r[1]))
origin="".join(r)
print(origin)
else:
print(eval(origin)) #-347153.161904762
break
6、sub和subn:
都是用于替换,sub和subn的区别就在于,sub替换后返回字符串,subn替换后,返回的是元组,元组元素包括:替换后的字符串,和替换次数
import re origin = "hello alex bcd abcd lge acd 19" r=re.sub("a\w+","kkk",origin,1) #注意这个flags=1,表示的是替换的次数。不填写默认为全部替换 print(r) #执行结果:hello kkk bcd abcd lge acd 19 r=re.subn("a\w+","kkk",origin) #注意这个flags=1,表示的是替换的次数。不填写默认为全部替换 print(r) #执行结果:('hello kkk bcd kkk lge kkk 19', 3)
浙公网安备 33010602011771号