正则语法
字符:是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。
字符串:是0个或更多个字符的序列。
文本:也就是文字,字符串,说某个字符串匹配某个正则表达式,通常是指这个字符串里有一部分(或几部分)分别能满足表达式给出的条件。
正则的组成:原子,元字符【原子修饰符】,模式修正符
- 原子:
- 它是组成正则表达式的最小单位,一个正则表达式至少需要一个原子;
- 所有可见的字符都是原子:a, b, c, d, A, B, C, D, 你,我,他,+ -* # ¥ % ;
- 所有不可见字符也都是原子:\t,\n,\r…..
匹配一个字符【原子字符】
| 原子字符 | 描述 | 组合元字符 | 描述 | |
|---|---|---|---|---|
| . | 匹配任意【一个】字符,除了换行符(\n); | |||
| \d | 匹配0~9之间任意【一个】字符; | === 等价于 === | [0-9] | 自定义原子列表格式,匹配列表中任意【一个】字符; |
| \D | 匹配除了0~9之外的任意【一个】字符; | === 等价于 === | [^0-9] | 自定义排除列表格式; |
| \w | 匹配0~9a~zA~Z_中的任意【一个】字符; | === 等价于 === | [a-zA-Z0-9_] | 自定义原子列表格式,匹配列表中任意【一个】字符; |
| \W | 匹配除了0~9a~zA~Z_之外的任意【一个】字符; | === 等价于 === | [^a-zA-Z0-9_] | 自定义排除列表格式; |
| \s | 匹配任意【一个】空白字符(不可见字符); | === 等价于 === | [\n\r\t\v\f] | 自定义原子列表格式,匹配列表中任意【一个】空白字符; |
| \S | 匹配任意【一个】非空白字符(可见字符); | === 等价于 === | [^\n\r\t\v\f] | 自定义排除列表格式; |
匹配多个字符【次数元字符】
| 次数元字符 | 描述 | 等价元字符 | 描述 | |
|---|---|---|---|---|
| * | 匹配到前面的字符,重复0次或多次【>=0】; | === 等价于 === | {0,} | 如: 0* = 0{0,} —————> 表示重复0次或更多次 |
| + | 匹配到前面的字符,至少重复1次或多次【>=1】; | === 等价于 === | {1,} | 如: 0+ = 0{1,} —————> 表示重复1次或更多次 |
| ? | 匹配到前面的字符,重复0次或1次【0,1】; | === 等价于 === | {0,1} | 如: 0? = 0{0,1} —————> 表示重复0次或1次 |
| *? | 匹配0次或无穷次,所以?只能匹配【0次】 | |||
| +? | +匹配至少1次或无穷次,所以+?只能匹配【1次】 | |||
| a{n} | 重复a字符n次; | |||
| a{n,} | 重复a字符n~无穷大; | |||
| a{,m} | 重复a字符0~m次; | |||
| a{n,m} | 重复a字符n到m次,至少匹配n次且最多匹配m次; |
?的使用范例:
字符串:str = “description GZA_R-Seg4 (28Aug2014)”
匹配出:GZA_R-Seg4
正则写法:GZA.?\d
解释:这里的.表示任意字符,表示重复0次或多次,如果没有?,结尾又是匹配到\d,所以会贪婪的匹配到GZA_R-Seg4 (28Aug2014,加了?,贪婪得到控制,则看到第一个\d就结束匹配
匹配行首行尾
| 匹配行首行尾的元字符 | 描述 |
|---|---|
| \A | 匹配的内容必须在整个字符串的开头部位; |
| \Z | 匹配的内容必须在整个字符串的结尾部位; |
| ^ | 匹配以某一个字符开头的行, 如果匹配的整个字符串中带有\n换行符,并且模式为:re.M(多行匹配模式),则能匹配到\n换行符后面以某一个字符开头的行; |
| $ | 匹配以某一个字符结尾的行; 如果匹配的整个字符串中带有\n换行符,并且模式为:re.M(多行匹配模式),则能匹配到\n换行符后面以某一个字符结尾的行; |
| ^$ | 匹配空行,注意:不是空格; |
| ^\s*$ | 匹配空白行; |
匹配单词边界
| 匹配单词边界的元字符 | 描述 |
|---|---|
| \b | 匹配【一个】能够当作单词分割的符号,比如:空格,标点符号,换行符等等;(不能当作分割单词的符号只有两种:数字和字母(汉字),因为字母是单词的组成部分); |
| \B | 匹配【一个】不能够当作单词分割的符号(非词边界,也就是数字和字母), 比如:lose mysql feel my love—-> 匹配第一个my:\bmy\B |
| \ba\w*\b | 匹配以字母a开头的单词:先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b); |
| \b\w{6}\b | 匹配刚好6个字母/数字的单词; |
精确匹配一个单词,这里匹配以root开头并以root结尾的单词;<(词首),>(词尾),如:<xellga>,xellga匹配,taoxellgat不匹配,xellgatang不匹配; |
匹配选择
| 选择关系元字符 | 描述 |
|---|---|
| | | 分支结构, 匹配该符号左右的内容二选一; |
| a|b | 匹配a或b |
| C|cat | 匹配C或cat |
| (C|c)at | 匹配Cat或cat |
| 0\d{2}-\d{8}|0\d{3}-\d{7} | 匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445); |
| (0\d{2})[- ]?\d{8}|0\d{2}[- ]?\d{8} | 匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔; |
| \d{5}-\d{4}|\d{5} | 匹配美国的邮政编码规则, 5位数字,或者用连字号间隔的9位数字, 给出这个例子需要说明一个问题:使用替换时,顺序是很重要; |
| \d{5}|\d{5}-\d{4} | 只匹配5位的邮编(以及9位邮编的前5位),原因是匹配替换时,将会从左到右测试每个条件,如果满足某个条件,就不会再去管其它的替换条件了; |
分组
| 分组元字符 | 描述 |
|---|---|
| (xyz) | (扩展字符) 括起来的东西当作一个整体进行处理(这个整体可以看成一个字符),用于分组过滤,后向引用,按照确切的顺序匹配字符xyz; |
| (wang){3} | 匹配wang这个单词3次的字符串;wangwangwang匹配; |
| g(o|la)d | 匹配god,glad,但不能匹配good; |
| g(o+|la)d | 匹配god,glad,good,goood;但不匹配gd; |
| g(o*|la)d | 匹配god,glad,good,goood,gd; |
| (go)+gle | 匹配gogogogogogle; 将多个原子放在一起当作一个原子处理,方便使用元字符; |
后向引用
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理,默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推…
\b(\w+)\b\s+\1\b
可以用来匹配重复的单词,像go go, 或者kitty kitty,这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)
也可以自己指定子表达式的组名,要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+) (或者把尖括号换成’也行:(?'Word'\w+)),这样就把\w+的组名指定为Word了,要反向引用这个分组捕获的内容,可以使用 \k<Word>,所以上一个例子也可以写成以下的语法:
\b(?<Word>\w+)\b\s+\k<Word>\b
使用小括号的时候,还有很多特定用途的语法:
| 捕获 | 语法 | 说明 |
|---|---|---|
| (exp) | 匹配exp,并将匹配到文本捕获到 自动命名的组里 |
|
| (? |
匹配exp,并将匹配到文本捕获到 名称为name的组里,该语法也可以写成(?’name’exp) |
|
| (?:exp) | 匹配exp,不捕获匹配的文本,也不给此分组分配组号,这样的组匹配的内容不会像前两种那样被捕获到某个组里面,也不会拥有组号 | |
| (?#comment) | 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读 |
示例:
import res = """138001138000 , 13426060134"""result = re.findall(r'(\d{3})\d+(\1)',s)print(result) # [('138', '138'), ('134', '134')]
import res = """taobao taobao,home home"""result = re.findall(r'(\w+)\s(\1)',s) # 表达式:(\w+)\s+(\1) —-> \b(\w+)\b表示匹配taobao,\1引用前面的分组,再匹配一份print(result)
零宽断言
- 用于查找匹配某些内容(包括不匹配某些内容)之前或之后的文本,它们就像\b,^,$那样,用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言;
| 零宽断言 | 语法 | 说明 | 断言 |
| —- | —- | —- | —- |
|
| (?=exp) | 匹配exp前面的位置 | 断言自身出现的位置的后面能匹配表达式exp | |
| (?<=exp) | 匹配exp后面的位置 | 断言自身出现的位置的前面能匹配表达式exp | |
| (?!exp) | 匹配后面跟的不是exp的位置 |
| |
| (?<!exp) | 匹配前面不是exp的位置 |
|
(?=exp)
# 匹配以ing结尾单词的前面部分(不包括这个结尾ing)\b\w+(?=ing\b)eg: I'm singing while you're dancing. ===> sing和danc
(?<=exp)
# 匹配以re开头单词的后面部分(不包括这个开头re)(?<=\bre)\w+\beg: reading a book. ===> ading
# 给一个很长的数字中,每三位间加一个逗号(从右边加起)((?<=\d)\d{3})+\beg: 1234567890 ===> 234567890# 匹配以空白符间隔的数字(再次强调,不包括这些空白符)(?<=\s)\d+(?=\s)
# 过滤出不包含‘hede'字串的信息^((?!hede).)*$
re模块的使用
生成正则表达式对象
| 函数 | 描述 |
|---|---|
| re.compile( ) | 用于编辑正则表达式,生成一个【正则表达式对象】,供match( ) 和 search( ) 这两个函数使用【当正则表达式重复被使用的次数比较多,可以使用这个方法重复调用】 |
| re.match( ) | 返回开头部分被匹配到的match对象【从字符串的开始部分进行匹配】 |
| re.search( ) | 返回第一个被匹配到的search对象【在整个字符串中进行匹配】 |
| group( ) | 返回的是字符串【group( )获取整个匹配到的内容(大分组),group(1)获取第一个分组中匹配到的内容,依次类推,一般在match( )和search( )函数中使用,来提取匹配到的内容】 |
| groups( ) | 返回的是一个元组【提取所有的子分组匹配到的内容】 |
| re.findall( ) | 返回的是一个列表【扫描整个字符串,被匹配到的字符串放在列表中】 |
| re.finditer( ) | 返回的是一个迭代器【在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回】 |
| re.split( ) | 返回的是一个列表【按照能够匹配的子串将字符串分割后返回列表】 |
| re.rub( ) | 用于替换字符串中的匹配项 |
| start( ) | 用于获取使用分组匹配到的子串在整个字符串中的起始位置(子串中第一个字符对应的索引号),默认值为0 |
| end( ) | 用于获取使用分组匹配到的子串在整个字符串中的结束位置(子串中最后一个字符对应的索引号+1),默认值为0 |
| span( ) | 该方法用于返回匹配到的整个字符串所占的位置索引 |
修饰符
正则表达式可以包含一些可选标志修饰符来控制匹配的模式,修饰符被指定为一个可选的标志,多个标志可以通过按位 or ( | ) 它们来指定,如:re.I | re.M 被设置成 I 和 M 标志:
| 修饰符 (可选的) | 描述 |
|---|---|
| re.A | re.ASCII, 表示在ASCII模式下进行正则匹配操作,只支持字母和数字和一些符号; |
| re.U | re.Unicode, 表示在Unicode模式下进行正则匹配操作,可以匹配依赖于Unicode字符属性数据库中的字符,比如:可以匹配到汉字; |
| re.S | 可以让 . 匹配到任意字符(包括换行符\n); |
| re.M | re.Multiline 多行匹配,都是与 ^ 和 $ 相关; |
| re.I | 忽略大小写; |
| re.X | 匹配的时候忽略正则表达式中的空格或注释; |
| re.L | 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境; |
| re.VERBOSE | 当正则表达式特别长的时候,可以添加注释 |
s = 'the number is 20.50'r = re.compile(r"""\d+ #小数点前面的数字\.? #小数点\d* #小数点后面的数字)""",re.VERBOSE)ret = re.search(r,s)print(ret)
s = 'the number is 20.50'r = re.compile("""\d+ #小数点之前的数字\. #小数点\d+ #小数点后面的数字""",re.VERBOSE)ret = re.search(r,s)print(ret.group())
re.compile( )
该函数主要是用于编辑正则表达式,生成一个【正则表达式对象】,供match( ) 和 search( ) 这两个函数使用.
re.compile(pattern[, flags])
| 参数 | 描述 |
|---|---|
| pattern | 一个字符串形式的正则表达式 |
| flags | 可选,表示匹配模式,比如忽略大小写,多行模式等 |
# 通过对象调用match方法import repattern = re.compile(r'\d+') # 创建一个正则表达式对象,这里创建的表达式为:匹配至少一个数字.m1 = pattern.match('abc12345dfe')print(m1) # match方法用于查找头部部分,没有匹配到,则返回None.m2 = pattern.match('123abcd456')print(m2) # 匹配到,则返回一个match对象 <re.Match object; span=(0, 3), match='123'>m3 = pattern.match('one12twothree34four',2,10)print(m3) # 指定位置开始匹配,这里指定2~10,说明是从'e'的位置开始匹配,没有匹配到.m4 = pattern.match('one12twothree34four',3,10)print(m4) # 指定位置开始匹配,这里指定3~10,说明是从'1'的位置开始匹配,有匹配到.print('\n-----使用一些方法-----')print(m2.group()) # 提取被匹配到的字符串print(m2.start()) # 提取被匹配到的字符串中第一个字符所对应的索引号print(m2.end()) # 提取被匹配到的字符串中最后一个字符所对应的索引号+1print(m2.span()) # 提取这个被匹配到的字符串所占的位置(start,end+1)
# 通过对象调用search方法pattern = re.compile(r'([a-z]+) ([a-z]+)',re.I) # re.I 表示忽略大小写s = pattern.search('Hello World Wide Web')print(s) # 匹配成功,返回一个Match对象print(s.group()) # 返回匹配成功的整个子串print(s.span()) # 返回匹配成功的整个子串的索引print(s.group(1)) # 返回第一个分组匹配成功的子串print(s.span(1)) # 返回第一个分组匹配成功的子串的索引print(s.group(2)) # 返回第二个分组匹配成功的子串print(s.span(2)) # 返回第二个分组匹配成功的子串索引print(s.groups()) # 等价于 (m.group(1), m.group(2), ...)print(s.group(3)) # 不存在第三个分组
# group用于把匹配结果分组import rea = "123abc456"print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0)) #123abc456,返回整体print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1)) #123print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2)) #abcprint(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3)) #456
re.match( )
该函数只匹配字符串的开始部分,如果字符串的开始部分不符合正则表达式,则匹配失败,match( )就返回none
re.match(pattern, string, flags=0)
| 参数 | 描述 |
|---|---|
| pattern | 匹配的正则表达式 |
| string | 要匹配的字符串 |
| flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等 |
import reprint(re.match('www','www.runoob.com')) # 在起始位置匹配,匹配成功,返回的是一个search对象print(re.match('com','www.runoob.com')) # 不在起始位置匹配,返回None.print('\n==============================================')line = "Cats are smarter than dogs"matchObj = re.match(r'(.*) are (.*?) .*',line,re.M|re.I) # .* 表示任意匹配除换行符(\n、\r)之外的任何单个或多个字符if matchObj:print('matchObj.group():',matchObj.group())print('matchObj.group(1):',matchObj.group(1))print('matchObj.group(2):',matchObj.group(2))else:print("No match!!")
re.search( )
该函数扫描的是整个字符串,返回的是第一个被匹配的字符串,匹配成功,返回一个匹配的对象,匹配不成功,返回None
re.search(pattern, string, flags=0)
| 参数 | 描述 |
|---|---|
| pattern | 匹配的正则表达式 |
| string | 要匹配的字符串 |
| flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等 |
import reprint(re.search('www','www.runoob.com').span()) # 在起始位置匹配print(re.search('com','www.runoob.com').span()) # 不在起始位置匹配
import reline = "Cats are smarter than dogs"searchObj = re.search(r'(.*) are (.*?) .*',line,re.M|re.I)if searchObj:print ("searchObj.group():", searchObj.group())print ("searchObj.group(1):", searchObj.group(1))print ("searchObj.group(2):", searchObj.group(2))else:print ("Nothing found!!")
re.match( )与re.search( )的区别:
- re.match 只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;
- re.search 匹配的是整个字符串,直到匹配到第一个就不再往后匹配;
- re.match 和 re.search 都只会匹配一次, 使用group(组号) 返回的匹配结果都放在一个元组中; ```python import re
line = “Cats are smarter than dogs dogs”
matchObj = re.match(r’dogs’,line,re.M|re.I) # dogs在字符串中的末尾处,使用mathc函数,返回的是None.
if matchObj: print (“match —> matchObj.group() : “,matchObj.group()) else: print (“No match!!”)
matchObj = re.search(r’dogs’,line, re.M|re.I)
if matchObj: print (“search —> matchObj.group():”,matchObj.group()) else: print (“No match!!”)
<a name="49032147"></a>## re.findall( )- 该函数扫描整个字符串,并且返回一个列表,所有被匹配到的字符串都放在列表中,如果没有匹配到,则返回空列表- 当正则表达式中有分组,则匹配的是分组中的内容- 当正则表达式中没有的分组,则匹配到的是整个字符串```pythonre.findall(string[, pos[, endpos]])
| 参数 | 描述 |
|---|---|
| string | 要匹配的字符串 |
| pos | 可选参数,指定字符串的起始位置,默认为 0 |
| endpos | 可选参数,指定字符串的结束位置,默认为字符串的长度 |
# 查找字符串中的所有数字
import re
pattern = re.compile(r'\w+\d+')
result1 = pattern.findall('runoob 123 god gole456')
result2 = pattern.findall('run88oob123google456',0,10)
print(result1)
print(result2)
# 在分组中使用
p = '^([A-F0-9]{2}:){5}[A-F0-9]{2}$'
string = '81:0B:7C:18:32:A0'
print(re.match(p,string).group()) # 81:0B:7C:18:32:A0 匹配正常
print(re.findall(p,string)) # 因为存在分组,所以匹配到的是:['32:']
print(re.findall('(ab)+123','ababab123 abc123 abb123 aa1234 ab1234')) #这里期望是匹配出['ababab123', 'ab123'],但是实际得到的只有 ['ab', 'ab']
# 正确的写法
print(re.findall('(?:ab)+123','ababab123 abc123 abb123 aa1234 ab1234'))
# 一种方法是去掉捕获分组,就是去掉()
p = '^[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}$'
# 另一种方法是将捕获的分组修改成非捕获的分组,只需要添加?:即可
p = '^(?:[A-F0-9]{2}:){5}[A-F0-9]{2}$'
re.finditer( )
它和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回
re.finditer(pattern, string, flags=0)
| 参数 | 描述 |
|---|---|
| pattern | 匹配的正则表达式 |
| string | 要匹配的字符串 |
| flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等 |
import re
it = re.finditer(r"\d+","12a32bc43jf3")
for match in it:
print (match.group())
re.rub( )
re.sub用于替换字符串中的匹配项
re.sub(pattern, repl, string, count=0, flags=0) # 前三个为必选参数,后两个为可选参数
| 参数 | 描述 |
|---|---|
| pattern | 一个字符串形式的正则表达式 |
| repl | 替换的字符串,也可为一个函数 |
| string | 要被替换的原始字符串 |
| count | 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配 |
| flags | 编译时用的匹配模式,数字形式 |
import re
phone = "2004-959-559 # 这是一个电话号码"
# 删除注释
num = re.sub(r'#.*$','', phone) # 将#到末尾处替换成空
print ('电话号码:', num)
# 移除非数字的内容
num = re.sub(r'\D','', phone) # 将剩下的部分中,非数字的替换成空
print ("电话号码:", num)
#repl参数是一个函数的实例
import re
s = 'A23G4HFD567'
def double(matched):
value = int(matched.group('value'))
return str(value * 2) # 将匹配的数字乘于2
print(re.sub('(?P<value>\d+)',double,s))
匹配模式替换
# 比如,需要在下面这段文本中的所有链接中找到以 /avxxxxxx/ 这种以 /av 开头,后面接一串数字的这种模式字符串,然后,这些字符串全部替换为 /cn345677/
names = '''
下面是这学期要学习的课程:
<a href='https://www.bilibili.com/video/av66771949/?p=1' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是牛顿第2运动定律
<a href='https://www.bilibili.com/video/av46349552/?p=125' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是毕达哥拉斯公式
<a href='https://www.bilibili.com/video/av90571967/?p=33' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是切割磁力线
'''
# 被替换的内容不是固定的,所以没法用字符串的replace方法,这时,可以使用正则表达式里面的sub方法
# 第一个参数: /av\d+?/这个正则表达式,表示以 /av 开头,后面是一串数字,再以 / 结尾的这种特征的字符串是需要被替换的
# 第二个参数: '/cn345677/' 这个字符串,表示用什么来替换
# 第三个参数: 源字符串
newStr = re.sub(r'/av\d+?/','/cn345677/', names)
print(newStr)
指定替换函数
- 刚才的例子中,我们用来替换的是一个固定的字符串 /cn345677/
- 如果,我们要求,替换后的内容的是原来的数字+6, 比如 /av66771949/ 替换为 /av66771955/ ,怎么办?
- 这种更加复杂的替换,我们可以把 sub的第2个参数指定为一个函数 ,该函数的返回值,就是用来替换的字符串 ```python import re
names = ‘’’ 下面是这学期要学习的课程:
点击这里,边看视频讲解,边学习以下内容 这节讲的是牛顿第2运动定律
点击这里,边看视频讲解,边学习以下内容 这节讲的是毕达哥拉斯公式
点击这里,边看视频讲解,边学习以下内容 这节讲的是切割磁力线 ‘’’
def subFunc(match): # 替换函数,参数是Match对象
src = match.group(0) # Match对象的group(0)返回的是整个匹配上的字符串
number = int(match.group(1)) + 6 # Match对象的group(1)返回的是第一个group分组的内容
dest = f'/av{number}/'
print(f'{src} 替换为 {dest}')
return dest # 返回值就是最终替换的字符串
newStr = re.sub(r’/av(\d+?)/‘, subFunc, names) print(newStr)
<a name="185d38d3"></a>
## re.split( )
split 方法按照能够匹配的子串将字符串分割后返回列表
```python
re.split(pattern, string[, maxsplit=0, flags=0])
| 参数 | 描述 |
|---|---|
| pattern | 匹配的正则表达式 |
| string | 要匹配的字符串 |
| maxsplit | 分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数 |
| flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等 |
import re
print(re.split('\W+','runoob, runoob, runoob.'))
print(re.split('(\W+)',' runoob, runoob, runoob.'))
print(re.split('\W+',' runoob, runoob, runoob.', 1))
print(re.split('a*','hello world')) # 对于一个找不到匹配的字符串而言,split不会对其作出分割
# 从下面字符串中提取武将的名字
names = '关羽; 张飞, 赵云,马超, 黄忠 李逵'
# 发现这些名字之间,有的是分号隔开,有的是逗号隔开,有的是空格隔开,而且分割符号周围还有不定数量的空格, 那么使用正则表达式里面的split方法.
namelist = re.split(r'[;,\s]\s*', names) # [;,\s]\s* 指定了,分割符为分号、逗号、空格里面的任意一种均可,并且该符号周围可以有不定数量的空格。
print(namelist)
# 我们要把下面的字符串中的所有html标签都提取出来
source = '<html><head><title>Title</title>' # 得到这样的一个列表: ['<html>', '<head>', '<title>', '</title>']
import re
p = re.compile(r'<.*>')
print(p.findall(source))
练习题
匹配用户名【邮箱】
设定规则:有字母,必须3个字符以上,可以是使用数字,不能使用特殊符号,可以使用下划线_ ;【如:congha, congha666, cong_ha,cong_666】
import re
# 检测的字符串
s = 'xellgat@qq.com'
# 正则表达式
pattern = r'^\w{3,}@[a-z0-9]+\.[a-z]+$' # 使用范围限定符号^$来精确匹配
# 匹配操作
result = re.search(pattern,s)
print(result)
匹配IP地址
设定规则:四个数字段,每个数字段使用 . 分隔,数字范围0~255;【192.168.0.1,127.0.0.1,0.0.0.0,255.255.255.255】
import re
pattern = r'\d|\.'
s = 'xellgat'
result = re.search(pattern,s)
print(result)
(\d{1,3}.){3}\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}匹配1到3位的数字,(\d{1,3}.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})
不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?).){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
匹配电话号码
验证手机号码:手机号码的规则是以1开头,第二位可以是34578,后面的9位就可以随意了。
import re
s = '1357063158789921'
ret = re.match('1[34578]\d{9}$',s) #不加^是由于match就是从头开始匹配的
print(ret.group())
匹配身份证
身份证的规则是:总共18位数字,前面17位都是数字,后面以为可以是数字,也可以是小写的x,也可以是大写的X
import re
s = '42058119000601101x'
ret = re.match('\d{17}[\dxX]',s)
print(ret.group())
匹配URL
URL的规则:前面一般是:http,https,ftp,然后再加上一个冒号,再加上一个斜杠,最后面就是可以出现任意非空白字符了
import re
s = 'ftp://10.201.56.88/tool/1.txt'
ret = re.match('(http|https|ftp)://[^\s]+',s)
print(ret.group())
匹配0~100之间的数字
- 可以匹配1,2,3,4,5,10,55,99,100
- 不可以匹配08,09,101
- 有三种情况:3,20,100 ```python import re
s = ‘0’
ret = re.match(‘[1-9]?\d$|100$’,s)
print(ret.group())
<a name="b7a728bc"></a>
### 判断字符串是否是全部小写
```python
import re
s1 = 'adkkdk'
s2 = 'abc123efg'
a1 = re.search('^[a-z]+$',s1) # re.search('^[a-z]+$',s1) 等价于 re.match('[a-z]+$',s2) 如果匹配失败,则a1 = re.search('^[a-z]+$',s1)返回None
if a1:
print('s1:',a1.group(),'全为小写')
else:
print(s1, "不全是小写")
a2 = re.match('[a-z]+$', s2)
if a2:
print('s2:',a2.group(),'全为小写')
else:
print(s2, "不全是小写")
重复前边的字串多次
import re
a = "kdla123dk345"
b = "kdla1123345"
m = re.search("([0-9]+(dk){0,1})[0-9]+", a)
print(m.group())
print(m.group(1))
print(m.group(2))
m = re.search("([0-9]+(dk){0,1})[0-9]+", b)
print(m.group())
print(m.group(1))
print(m.group(2))
正则爬取古诗词网站内容
import re
import requests
def open_page(url):
headers = {
'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
}
response = requests.get(url,headers)
text = response.text
titles = re.findall(r'<div\sclass="cont">.*?<b>(.*?)</b>',text,re.S) # 使用()表示只想提取哪一部分的内容,它的优先级比较高
dynasties = re.findall(r'<p\sclass="source">.*?>(.*?)</a>',text,re.S) # 获取朝代
authors = re.findall(r'<p\sclass="source">.*?<a.*?><a.*?>(.*?)</a>',text,re.S) # 获取作者
content_tags = re.findall(r'<div class="contson".*?>(.*?)</div>',text,re.S) #获取内容
contents = []
for content in content_tags:
x = re.sub(r'<.*?>','',content)
contents.append(x.strip())
poems = []
for value in zip(titles,dynasties,authors,contents):
title,dynasty,author,content = value
poem = {
'title':title,
'dynasty':dynasty,
'author':author,
'content':content
}
poems.append(poem)
for poem in poems:
print(poem)
print('='*50)
def main():
for x in range(1,12):
url = 'https://www.gushiwen.cn/default_%s.aspx' %x
open_page(url)
if __name__=='__main__':
main()
正则爬取糗事百科网站内容
import re
import requests
def open_page(url):
headers = {
'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
}
response = requests.get(url,headers)
text = response.text
authors = re.findall(r'<div\sclass="author clearfix">.*?<h2>(.*?)</h2>',text,re.S) # 获取作者
content_tags = re.findall(r'<div class="content".*?<span>(.*?)</span>',text,re.S) # 获取内容
contents = []
for content in content_tags:
x = re.sub(r'<.*?>','',content)
contents.append(x.strip())
poems = []
for value in zip(authors,contents):
author,content = value
poem = {
'作者':author,
'内容':content
}
poems.append(poem)
for poem in poems:
print(poem,end='\n')
print('='*100)
def main():
for x in range(1,5):
url = 'https://www.qiushibaike.com/text/page/%s/' %x
open_page(url)
if __name__=='__main__':
main()
# 写一个正则表达式,使其能同时识别下面所有的字符串:'bat','bit', 'but', 'hat', 'hit', 'hut'
import re
s = 'bat,bit,but,hat,hit,hut'
re.findall(r'[bh][aiu]t',s)
# 匹配由单个空格分隔的任意单词对,也就是姓和名
import re
s = 'Han meimei, Li lie, Zhang san, Li si'
re.findall(r'([A-za-z]+)\s([A-Za-z]+)',s)
# 匹配由单个逗号和单个空白符分隔的任何单词和单个字母,如姓氏的首字母
import re
s = 'yu, Guan bei, Liu fei, Zhang'
re.findall(r'([a-zA-Z]+),\s([a-zA-Z]+)',s)
# 匹配所有有效的Python标识符集合
import re
s = '_hello , python_1 , 2world , Pra_ni , @dfa_ , ewq* '
re.findall(r'\b[a-zA-Z][\w]*(?!=\W) ',s)
# 匹配以“www”起始并且以“.com”结尾的简单Web域名,例如:http://www.yahoo.com ,也支持其他域名,如.edu .net等
import re
s = 'http://www.yahoo.com aaa www.foothill.edu'
re.findall(r'w{3}\.[a-zA-Z]+\.\w{2,3}\b',s)
# 匹配所有能够表示Python整数的字符串集
import re
s = '520a1 20L 0 156 -8 -10a A58'
ite = re.finditer(r'-?\d+',s)
list1 = [i.group() for i in ite]
print(list1)
# 匹配所有能够表示Python长整数的字符串集
import re
s = '520a1 20L 0 156 -8 -10a A58'
ite = re.finditer(r'-?\d+L',s)
list1 = [i.group() for i in ite]
print(list1)
# 匹配所有能够表示Python浮点数的字符串集
import re
s = '80.2 fds2.1 0.003'
re.findall(r'\d+\.\d+',s)
# 匹配一行文字中的所有单词开头是字母内容和所有开头为数字的内容
import re
s = "Now,let's take a closer look at some iconic moments from the show's stage made by Chinese Angels 33abc."
print(re.findall(r'\b\w',s))
print(re.findall(r'\b\d',s))
# 匹配一行文字中的所有开头是数字或字母的内容
import re
s = '577fsda3f you12daf f1s32daffffff'
print(re.findall(r'\b\d+|\b[a-zA-Z]+',s))
# 只匹配包含字母和数字的行
import re
s = 'nihao fsadf \n789! 3asfd 1\nfdss12df e4 4564'
re.findall(r'^([a-zA-Z\d ]+)$',s,re.M)
# 提取每行中完整的年月日和时间字段
import re
s = 'time 2019-01-01 17:20:10 fsadf 2018-02-02 02:29:01'
print(re.findall(r'[12]\d{3}-[01]\d-[0-3]\d\s*[0-2]\d:[0-2]\d:[0-5]\d',s))
# 将每行中的电子邮件地址替换为你自己的电子邮件地址
import re
s = 'xss@qq.com, 465465@163.com, ppp@sina.com, s121f@139.com, soifsdfj@134.com, pfsadir423@123.com'
re.sub(r'\w+?@\w+?.com','python@qq.com',s)
# 从下面算式中匹配出最内层小括号以及小括号内的表达式
import re
s = '1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'
re.findall(r'\([^()]+\)',s) # \(和\)表示前后位( ),[^()]就表示外面的()里面没有()
import re
s = '(abc)def'
m = re.search('(\(.*\)).*',s) # 转义字符
print(m.group())
print(m.group(1))
import re
# 匹配整数或者小数(包括正数和负数)
# -?表示-匹配0次或一次,\d表示整数,+表示匹配一次或多次,(\.\d+)?表示小数
pattern = re.compile(r'-?\d+(\.\d+)?')
# 匹配年月日日期 格式 2018-12-6
# ^[1-9]表示年是以数字1-9开头的, \d{0,3}表示年的位数, ^[1-9]\d{0,3}就表示1-9999年之间
# (1[0-2]|0?[1-9])中|前面的1[0-2]表示从10到12,后面的0?[1-9]表示01-09或者1-9, (1[0-2]|0?[1-9])表示月,01-12或者1-12
# (3[01]|[12]\d|0?[1-9]) $其中3[01]表示30或31,[12]\d表示从10-29,最后的0?[1-9]表示从01-09或者是从1-9.整体就表示从01-31或者1-31
pattern = re.compile(r'^[1-9]\d{0,3}-(1[0-2]\|0?[1-9])-(3[01]\|[12]\d\|0?[1-9])$')
# 匹配qq号,表示5位到12位qq.第一位为非0
pattern = re.compile(r'[1-9]\d{4,11}')
# 匹配11位的电话号码, 第一位数字为1,第二位为3-9,后面随便9位数
pattern = re.compile(r'1[3-9]\d{9}')
# 匹配长度为8-10位的用户密码 : 包含数字字母下划线
pattern = re.compile(r'\w{8,10}')
# 匹配验证码:4位数字字母组成的,[ ]里面的表示数字,或者a-z或者A-Z,{4}表示4位
pattern = re.compile(r'[\da-zA-Z]{4}')
pattern = re.compile(r'[0-9a-zA-Z]{4}')
# 匹配邮箱地址
# [0-9a-zA-Z][\w\-.]+ @前面必须有内容且只能是字母(大小写),数字,下划线,减号,点
# [a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)* @和最后一个点之间必须有内容且只能是字母(大小写),数字,点,减号,且两个点不能挨着
# [A-Za-z0-9]{2,6} 最后一个点之后必须有内容且内容只能是字母(大小写),数字长度为大于等于2,小于等于6
pattern = re.compile(r'[0-9a-zA-Z][\w\-.]+@[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*\.[A-Za-z0-9]{2,6}')
