正则表达式
在学习re模块前我们得先理解使用它的原理,而re模块就是基于正则表达式工作的,正可谓磨刀不误砍柴工.
现在就让我们先来看一些实际的应用。在线测试工具 http://tool.chinaz.com/regex/
首先你要知道的是,谈到正则,就只和字符串相关了。在我给你提供的工具中,你输入的每一个字都是一个字符串。
其次,如果在一个位置的一个值,不会出现什么变化,那么是不需要规则的。
比如你要用"1"去匹配"1",或者用"2"去匹配"2",直接就可以匹配上。这连python的字符串操作都可以轻松做到。
那么在之后我们更多要考虑的是在同一个位置上可以出现的字符的范围。
什么是正则表达式
正则就是用一些居右特殊含义的符号组合到一起(称之为正则表达式) 来描述字符或者字符串的方法.或者说: 正则
就是用来描述这一类事物的规则.(在python中)塔内嵌在python中,通过re模块实现.正则表达式模式被编译成一系列
的字节码,然后由用C编写的匹配引擎执行.
字符匹配
字符组
字符组 : [ ] 在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示 字符分为很多类,比如数字、字母、标点等等。 假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一。
正则 |
待匹配字符 |
匹配 |
说明 |
[0123456789] |
8 |
True |
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符 |
[0123456789] |
a |
False |
由于字符组中没有"a"字符,所以不能匹配 |
[0-9] |
7 |
True |
也可以用-表示范围,[0-9]就和[0123456789]是一个意思 |
[a-z] |
s |
True |
同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示 |
[A-Z] |
B |
True |
[A-Z]就表示所有的大写字母 |
[0-9a-fA-F] |
e |
True |
可以匹配数字,大小写形式的a~f,用来验证十六进制字符 |
字符
元字符 |
匹配内容 |
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
\b | 匹配一个单词的结尾 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结尾 |
\W | 匹配非字母或数字或下划线 |
\D | 匹配非数字 |
\S | 匹配非空白符 |
a|b | 匹配字符a或字符b |
() | 匹配括号内的表达式,也表示一个组 |
[...] | 匹配字符组中的字符 |
[^...] | 匹配除了字符组中字符的所有字符 |
量词
量词 |
用法说明 |
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
ps:伟大的灵魂画师再次上线
-
一次性匹配a1b2c3
用[a-z][0-9]会匹配到三个结果
想法:重复写三次,但是太麻烦,加量词的话只能限制离得最近的正则表达式
这个时候就可以用分组([a-z][0-9])+
-
匹配身份证号
依据博客一次分析(下面的正则之所以加^和$是因为我们仅仅只想匹配身份证号,教学演示方便,不加照样也能匹配上,只不过前后可以出现很多其他字符)
1.^[1-9]\d{13,16}[0-9x]$:不完善的地方在于默认是x的情况只在18位才可能出现,但是这个表达式没有做这一层的限制
2.^[1-9]\d{14}(\d{2}[0-9x])?$
3.^([1-9]\d{16}[0-9x]|[1-9]\d{14})$
转义符
在正则表达式中,有很多有特殊意义的是元字符,比如\n和\s等,如果要在正则中匹配正常的"\n"而不是"换行符"就需要对""进行转义,变成'\'。
在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。
所以如果匹配一次"\n",字符串中要写成'\\n'
所以如果匹配一次"\\n",字符串中要写成'\\\\n'
简便操作,利用r可以让整个字符串都不再转义(了解:r其实就是real的意思,真实不转义)
贪婪匹配与非贪婪匹配
-
<.*>:先拿着里面的.*去匹配所有的内容,然后再根据>往回退着找,遇到即停止
-
<.*?>:先拿着?后面的>去匹配符合条件的最少的内容,然后把匹配的结果返回
ps:根据匹配的内部原理可以很好的理解
.*?x
就是取前面任意长度的字符,直到一个x出现
re模块方法
import re # \w与\W print(re.findall('\w', 'hello egon 123')) # ['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3'] print(re.findall('\W', 'hello egon 123')) # [' ', ' '] # \s与\S print(re.findall('\s', 'hello egon 123')) # [' ', ' ', ' ', ' '] print(re.findall('\S', 'hello egon 123')) # ['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3'] # \n \t都是空,都可以被\s匹配 print(re.findall('\s', 'hello \n egon \t 123')) # [' ', '\n', ' ', ' ', '\t', ' '] # \n与\t print(re.findall(r'\n', 'hello egon \n123')) # ['\n'] print(re.findall(r'\t', 'hello egon\t123')) # ['\n'] # \d与\D print(re.findall('\d', 'hello egon 123')) # ['1', '2', '3'] print(re.findall('\D', 'hello egon 123')) # ['h', 'e', 'l', 'l', 'o', ' ', 'e', 'g', 'o', 'n', ' '] # \A与\Z print(re.findall('\Ahe', 'hello egon 123')) # ['he'],\A==>^ print(re.findall('123\Z', 'hello egon 123')) # ['he'],\Z==>$ # ^与$ print(re.findall('^h', 'hello egon 123')) # ['h'] print(re.findall('3$', 'hello egon 123')) # ['3'] # . print(re.findall('a.b', 'a1b')) # ['a1b'] print(re.findall('a.b', 'a1b a*b a b aaab')) # ['a1b', 'a*b', 'a b', 'aab'] print(re.findall('a.b', 'a\nb')) # [] print(re.findall('a.b', 'a\nb', re.S)) # ['a\nb'] print(re.findall('a.b', 'a\nb', re.DOTALL)) # ['a\nb']同上一条意思一样 # 重复匹配:| * | ? | .* | .*? | + | {n,m} | # * print(re.findall('ab*', 'bbbbbbb')) # [] print(re.findall('ab*', 'a')) # ['a'] print(re.findall('ab*', 'abbbb')) # ['abbbb'] # ? print(re.findall('ab?', 'a')) # ['a'] print(re.findall('ab?', 'abbb')) # ['ab'] # 匹配所有包含小数在内的数字 print(re.findall('\d+\.?\d*', "asdfasdf123as1.13dfa12adsf1asdf3")) # ['123', '1.13', '12', '1', '3'] # .*默认为贪婪匹配 print(re.findall('a.*b', 'a1b22222222b')) # ['a1b22222222b'] # .*?为非贪婪匹配:推荐使用 print(re.findall('a.*?b', 'a1b22222222b')) # ['a1b'] # + print(re.findall('ab+', 'a')) # [] print(re.findall('ab+', 'abbb')) # ['abbb'] # {n,m} print(re.findall('ab{2}', 'abbb')) # ['abb'] print(re.findall('ab{2,4}', 'abbb')) # ['abb'] print(re.findall('ab{1,}', 'abbb')) # 'ab{1,}' ===> 'ab+' print(re.findall('ab{0,}', 'abbb')) # 'ab{0,}' ===> 'ab*' #字符集[] ret=re.findall('a[bc]d','acd') print(ret)#['acd'] ret=re.findall('[a-z]','acd') print(ret)#['a', 'c', 'd'] ret=re.findall('[.*+]','a.cd+') print(ret)#['.', '+'] #在字符集里有功能的符号: - ^ \ ret=re.findall('[1-9]','45dha3') print(ret)#['4', '5', '3'] ret=re.findall('[^ab]','45bdha3') print(ret)#['4', '5', 'd', 'h', '3'] ret=re.findall('[\d]','45bdha3') print(ret)#['4', '5', '3'] # \# print(re.findall('a\\c','a\c')) #对于正则来说a\\c确实可以匹配到a\c,但是在python解释器读取a\\c时,会发生转义,然后交给re去执行,所以抛出异常 print(re.findall(r'a\\c', 'a\c')) # r代表告诉解释器使用rawstring,即原生字符串,把我们正则内的所有符号都当普通字符处理,不要转义 print(re.findall('a\\\\c', 'a\c')) # 同上面的意思一样,和上面的结果一样都是['a\\c'] # ():分组 print(re.findall('ab+', 'ababab123')) # ['ab', 'ab', 'ab'] print(re.findall('(ab)+123', 'ababab123')) # ['ab'],匹配到末尾的ab123中的ab print(re.findall('(?:ab)+123', 'ababab123')) # findall的结果不是匹配的全部内容,而是组内的内容,?:可以让结果为匹配的全部内容 print(re.findall('href="(.*?)"', '<a href="http://www.baidu.com">点击</a>')) # ['http://www.baidu.com'] print(re.findall('href="(?:.*?)"', '<a href="http://www.baidu.com">点击</a>')) # ['href="http://www.baidu.com"'] # | print(re.findall('compan(?:y|ies)', 'Too many companies have gone bankrupt, and the next one is my company')) import re print(re.findall("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>", "<h1>hello</h1>")) print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>", "<h1>hello</h1>")) print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>", "<h1>hello</h1>").group('tag_name')) print(re.search(r"<(\w+)>\w+</\1>", "<h1>hello</h1>")) # ['h1'] # <re.Match object; span=(0, 14), match='<h1>hello</h1>'> # h1 # <re.Match object; span=(0, 14), match='<h1>hello</h1>'> #匹配出所有的整数 import re #ret=re.findall(r"\d+{0}]","1-2*(60+(-40.35/5)-(-4*3))") ret=re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))") ret.remove("") print(ret)
常用方法
import re #1 re.findall('a','alvin yuan') #返回所有满足匹配条件的结果,放在列表里 #2 re.search('a','alvin yuan').group() #函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以 # 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。 #3 re.match('a','abc').group() #同search,不过尽在字符串开始处进行匹配 #4 ret=re.split('[ab]','abcd') #先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割 print(ret)#['', '', 'cd'] #5 ret=re.sub('\d','abc','alvin5yuan6',1) print(ret)#alvinabcyuan6 ret=re.subn('\d','abc','alvin5yuan6') print(ret)#('alvinabcyuanabc', 2) #6 obj=re.compile('\d{3}') ret=obj.search('abc123eeee') print(ret.group())#123 import re ret=re.finditer('\d','ds3sy4784a') print(ret) #<callable_iterator object at 0x10195f940> print(next(ret).group()) print(next(ret).group())
课后作业:
import re # Errors=['验证通过!','身份证号码位数不对!','身份证号码出生日期超出范围或含有非法字符!','身份证号码校验错误!','身份证地区非法!'] def checkIdcard(idcard): Errors = ['验证通过!', '身份证号码位数不对!', '身份证号码出生日期超出范围或含有非法字符!', '身份证号码校验错误!', '身份证地区非法!'] area = {"11": "北京", "12": "天津", "13": "河北", "14": "山西", "15": "内蒙古", "21": "辽宁", "22": "吉林", "23": "黑龙江", "31": "上海", "32": "江苏", "33": "浙江", "34": "安徽", "35": "福建", "36": "江西", "37": "山东", "41": "河南", "42": "湖北", "43": "湖南", "44": "广东", "45": "广西", "46": "海南", "50": "重庆", "51": "四川", "52": "贵州", "53": "云南", "54": "*", "61": "陕西", "62": "甘肃", "63": "青海", "64": "宁夏", "65": "*", "71": "*", "81": "香港", "82": "澳门", "91": "国外"} idcard = str(idcard) idcard = idcard.strip() idcard_list = list(idcard) # 地区校验 if (not area[(idcard)[0:2]]): print(Errors[4]) # 15位身份号码检测 if (len(idcard) == 15): if ((int(idcard[6:8]) + 1900) % 4 == 0 or ( (int(idcard[6:8]) + 1900) % 100 == 0 and (int(idcard[6:8]) + 1900) % 4 == 0)): erg = re.compile( '[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}$') # //测试出生日期的合法性 else: ereg = re.compile( '[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}$') # //测试出生日期的合法性 if (re.match(ereg, idcard)): print(Errors[0]) else: print(Errors[2]) # 18位身份号码检测 elif (len(idcard) == 18): # 出生日期的合法性检查 # 闰年月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9])) # 平年月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8])) if (int(idcard[6:10]) % 4 == 0 or (int(idcard[6:10]) % 100 == 0 and int(idcard[6:10]) % 4 == 0)): ereg = re.compile( '[1-9][0-9]{5}(19[0-9]{2}|20[0-9]{2})((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}[0-9Xx]$') # //闰年出生日期的合法性正则表达式 else: ereg = re.compile( '[1-9][0-9]{5}(19[0-9]{2}|20[0-9]{2})((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}[0-9Xx]$') # //平年出生日期的合法性正则表达式 # //测试出生日期的合法性 if (re.match(ereg, idcard)): # //计算校验位 S = (int(idcard_list[0]) + int(idcard_list[10])) * 7 + (int(idcard_list[1]) + int(idcard_list[11])) * 9 + ( int(idcard_list[2]) + int(idcard_list[12])) * 10 + ( int(idcard_list[3]) + int(idcard_list[13])) * 5 + ( int(idcard_list[4]) + int(idcard_list[14])) * 8 + ( int(idcard_list[5]) + int(idcard_list[15])) * 4 + ( int(idcard_list[6]) + int(idcard_list[16])) * 2 + int(idcard_list[7]) * 1 + int( idcard_list[8]) * 6 + int(idcard_list[9]) * 3 Y = S % 11 M = "F" JYM = "10X98765432" M = JYM[Y] # 判断校验位 if (M == idcard_list[17]): # 检测ID的校验位 print(Errors[0]) else: print(Errors[3]) else: print(Errors[2]) else: print(Errors[1]) if __name__ == "__main__": while True: cdcard = input("请输入你的身份证号:") if cdcard == "exit": print(u"程序已结束!") break else: checkIdcard(cdcard)