RE模块(正则模块)
re模块
re是高度专业化的小型语言,有着自己的语法,但是在python中我们可以通过re模块直接使用re这么语言,re也就是正则是专门用来处理字符串的,比如很多的模糊匹配如果不使用re会很难很难完成
re模块的元字符 -- 元字符是特殊的字符,不是普通的字符,在原字符串里出现的元字符仅仅只是个普通字符,而在需要匹配的子字符串里的元字符是特殊含义的字符,两者不是一个东西
//'.'元字符
'.'这个元字符也可以叫做通配符,也就是说 '.' 这个元字符可以代指任意一个字符(除了换行符以外的任意字符),注意 一个 '.' 代指一个任意字符而不是多个
import re a = re.findall('a..x','hannhalexfjgjakex')//两个'.'代指两个任意字符,也就是以 ‘a'开头以’x'结尾的长度为4的子字符串 print(a) 结果 ['alex', 'akex']//findall()方法会将所有满足的结果都展示出来
// '^' 元字符,表示从原字符串的开头开始匹配 ----- 在 '^' 前面不可以再加任何东西 因为 '^' 本身就代表是开头的意思,所以前面就不可以放任何东西
import re a = re.findall('^a..x','hannhalexfjgjakex') print(a) 结果 [] import re a = re.findall('^a..x','aanxhalexfjgjakex') print(a) 结果 ['aanx']
// '$' 元字符,和 '^' 类似,只不过 '$' 是代表结尾的意思
1 import re 2 a = re.findall('a..x$','aanxhalexfjgjakex') 3 print(a) 4 5 结果 6 ['akex'] 7 import re 8 a = re.findall('a..x$','aanxhalexfjgjakex$')//再次强调,在待匹配的子字符串里 $ 是有特殊含义的元字符 而在原字符串里 $ 只不过是一个普通字符,不是同一个东西 9 print(a) 10 11 结果 12 []
//表示重复的字符 '*' '+' '?' '{}' 这4个元字符都是表示重复的元字符 都是差不多 只是有微小的差异 -----这四个表示重复的元字符都是默认贪婪匹配,但如果在这四个元字符的后面加个 ?就是表示惰性匹配
import re // “*” 元字符 a = re.findall('d*','asdqedd')//'*' 元字符会和其前面的一个字符组成一个整体表示(0,+无穷),比如这里的"d*"这一个整体,表示可以有(0,+无穷)个 "d" 默认都是贪婪匹配 b = re.findall('alex*','aksdalejsalexxs')//这里 "*" 和其前面的一个字符也就是’x'组成一个整体,表示可以有(0,+无穷)个‘x'即 “ale” “alex” “alexx” .... print(a) print(b) 结果 ['', '', 'd', '', '', 'dd', '']//0个"d"也就是没有也可以匹配 就是就有了 "" 这些空字符串出现 ['ale', 'alexx']//看上面的讲解 //“+” 元字符 和 “*”一模一样,只不过是“+”和其前面一个字符组成的整体是 (1,+无穷) import re a = re.findall('d+','asdqedd') b = re.findall('alex+','aksdalejsalexxs') print(a) print(b) 结果 ['d', 'dd'] ['alexx'] import re a = re.findall('d+?','asdqedd')//转换成惰性匹配 print(a) 结果 ['d', 'd', 'd']
//“?” 元字符和上面的一模一样 只不过是“?”和其前面一个字符组成的整体是 (0,1)
import re a = re.findall('d?','asdqedd') b = re.findall('alex?','aksdalejsalexxs') print(a) print(b) 结果 ['', '', 'd', '', '', 'd', 'd', ''] ['ale', 'alex']
//“{}” 可以自己限定范围 除此之外和上面的一模一样 只不过是“{}”和其前面一个字符组成的整体是 自己限定的
import re a = re.findall('d{2}','asdqedd')//这样写就意味着仅仅只可以匹配2个的 b = re.findall('alex{2,}','aksdalejsalexxs')//这样写意味着可以匹配 (2,+无穷)个的 print(a) print(b) 结果 ['dd'] ['alexx']
// "[]" 中括号元字符
在[]里面所有的元字符都变得无意义,变成普通字符,除了 "-" "^" "\" 这三个字符是有意义的
import re a = re.findall('q[abc s*]',"qsdq eq*")//执行或的功能,跟 中括号里的某一个字符结合,注意是某一个字符 print(a) 结果 ['qs', 'q ', 'q*'] ---------------------------------------- import re a = re.findall('q[a-c]',"qsdq eq*qa sqc")//[a-c]相当于从a到c 即[abc] print(a) 结果 ['qa', 'qc'] -------------------------------------------------- import re a = re.findall('q[a-z]*',"qsdq eq*qa q8sqc")//相当于 [a-z]* 是一个整体 有(0,+无穷)个 [a-z] print(a) 结果 ['qsdq', 'q', 'qa', 'q', 'qc'] ---------------------------------------------------- import re a = re.findall('q[^a-z]*',"qsdq eq*2qa q8*&^%sqc")// []里面的 ^ 的意思是非的意思 [^a-z]相当于除了 a-z 外的所有字符 print(a) 结果 ['q', 'q ', 'q*2', 'q', 'q8*&^%', 'q']
// ‘\' 元字符
将无意义的变成有意义
\d 匹配任何10进制数 相当于 [0-9] \D 相当于 \d 的相反 也就是 [^0-9] \s 匹配任何空白字符 相当于 [\t\n\r\v\f] \S 相当于 \s 的相反 \w 是所有的字母和数字 相当于[a-zA-z0-9_] 注意:包括字符 "_" import re a = re.findall('\w+',"asd dsf a saf __") print(a) 结果 ['asd', 'dsf', 'a', 'saf', '__'] \W 相当于 \w 的相反
将有意义的元字符变成普通的无意义的字符
import re a = re.findall('asd\.',"asd dsf a saf __") print(a) 结果 [] ----------------------------------------------- import re a = re.findall('asd\.',"asd.dsf a saf __") print(a) 结果 ['asd.'] ----------------------------------------------
// ’\b' 以某些特殊的边界结尾 如空格 # 之类的
import re a = re.findall('I\\b','asdf I asfIsdf')//这里之所以要\\b下面是因为在使用re模块的转义字符 “\” 时,是先经过python这一层转义,转义介绍后的字符串再传到re模块这一层 //因为 \b 在python这一层有意义所以它会先转义,而转义结束后 \b 就没有了 只剩 "I" 了,所以如果我们想在re模块那一层使用 \b 则必须在python这一层用 \\b 这样的话因为 \\ 在python这一层相当于转义 '\' 这个时候与b已经没关系,只是在转义 '\' 然后到re模块那层时就有 '\b' 了 print(a) 结果 ['I']// 这个 "I" 是中间那个独立的 "I" 其实转义字符 "\" 有一些很绕的地方 给你举个例子 import re a = re.findall('I\\\\l','asdffsdfasfI\lsdf') print(a) 结果 ['I\\l'] 这个为啥需要4个 '\' 呢,因为如果只写一个,即'\l' 在python这一层没意义,所以不报错会直接将 '\l' 传个re这一层,但在re这一层 '\l' 没有意义所以会报错,单纯的想法那就再 加一个 '\' 变成 '\\l' 但这个时候 '\\' 在python这一层又有意义了,相当于转义 “\” 这时与 "l" 无关,也就是传进re模块那层时和一个 '\' 即 '\l' 时一样,在re这一层只剩 '\l' ,又报错, 那怎么样才在re这一层不报错呢,就是在re这一层时是 "\\l" 就不报错,相当于转义 “\” 这时与 "l" 无关,既然在re这一层要两个 "\\" 那在python这一层写四个 "\" 就ok啦,即 “\\\\l" 相当于转义 “\” 这时与 "l" 无关 其实这样思考的话要兼顾python和re这两层,其实也又办法之间看re这一层直接跳过python这一层,就是在待匹配的子字符串前加个 r import re a = re.findall(r'I\\l','asdffsdfasfI\lsdf')//r的意思是在python这一层不进行任何转义直接将原生的给re这一层 print(a) 结果 ['I\\l']//之所以会显示两个 "\" 是因为在re那一层确确实实就是只有一个"\" 但回到python这一层显示时自然就得加多一个"\"咯 也就是显示"\\l"
//"|"和"[]"差不多吧,虽然都是或的意思,但 "|" 可能会好一丢丢?? 其实我也不知道
import re a = re.findall(r"ka|kb","khdkbhkax") print(a) 结果 ['kb', 'ka']
//'()"分组符号
//这里这个是将其看成一个整体分组
import re a = re.findall(r'(abc)+','abcabcabcssa') print(a) 结果 ['abc'] 其实 "(abc)+" 是已经把 "abc" 分成一组也就是看成一个整体了,然后去匹配,在此的意思其实就是有(1,+无穷)个"abc" 所以按理说结果应该输出是"abcabcabc"但为啥是"abc"呢 因为在re模块里,如果用()括起来python就会认为你其实更关心的是括号括起来的内容,也就是将python在匹配时用到()里的哪个给输出出来,其实已经把 "abcabcabc" 给匹配出来了, 因为代码是 (abc) 也就python认为这里你更关心的是(abc)这个,所以它不会将真正匹配的 abcabcabc 输出,而是将 abc 输出,加个 "?:" 这个固定搭配的符号的意思是不用优先输 出括号里的内容,直接将原始匹配的输出便好 import re a = re.findall(r'(?:abc)+','abcabcabcssa') print(a) 结果 ['abcabcabc'] 比如下面这个例子 这个例子更好看出问题 import re a = re.findall(r'a(bc|de)fg','abcfg') print(a) 结果 ['bc']//其实已经匹配好 abcfg 了,只是不输出,将匹配时用到的括号括起来的内容优先输出 import re a = re.findall(r'a(?:bc|de)fg','abcfg') print(a) 结果 ['abcfg']
//下面这个是名字分组
import re a = re.search(r'(?P<name>[a-z]+)(?P<age>\d+)','djh18DJh19') print(a.group('name')) print(a.group('age')) 结果 djh 18 对匹配的字符串命名,一般都是和 group()方法和着用,因为这样才显示得出将字符串命名这个东东 上面那个是固定格式 "(?P<...>...)" ?P是固定的了,然后<>里面写名字,接着后 面写要匹配的字符串
re模块中的一些方法
//findall()方法,在字符串中将所有满足匹配要求的子字符串以元组的方式返回
import re a = re.findall(r'ab','ababab') print(a) 结果 ['ab', 'ab', 'ab']
//search()方法,将字符串中第一个满足匹配要求的子字符串以对象的形式返回
import re a = re.search(r'ab','ababab') print(a) 结果 <_sre.SRE_Match object; span=(0, 2), match='ab'> import re a = re.search(r'ab','ababab') print(a.group()) 结果 ab
//match()方法,和search()方法差不多,只不过match()方法必须是在开头开始匹配,同样是返回一个对象
import re a = re.match(r'ab','bbabab') print(a.group()) 结果 AttributeError: 'NoneType' object has no attribute 'group'//报错 import re a = re.match(r'ab','ababab') print(a.group()) 结果 ab
//split()方法,切割字符串,可以有多个切割符,返回一个列表
import re a = re.split(r'[abc]','ascbassa') print(a) 结果 ['', 's', '', '', 'ss', ''] 原理是:因为 abc 是切割符,所以是将abc这三个字母之间的任意两个组合之间的字符串给拿出来并返回一个列表,在此是 "..a..c..b..a..a.." ,对照原字符串可以知道这里的..分别 是 "","s","","","ss",""
//sub()方法,替换字符串里的某些子字符串,返回一个字符串
import re a = re.sub('\d','A','j22ks1d4f12vjk2')//将\d(数字)变成字母 A 并返回这个字符串 print(a) 结果 jAAksAdAfAAvjkA import re a = re.sub('\d','A','j22ks1d4f12vjk2',4)//只将前4个转换 print(a) 结果 jAAksAdAf12vjk2
//subn()方法,其实就是sub(),返回的是元组
import re a = re.subn('\d+','A','j22ks1d4f12vjk2') print(a) 结果 ('jAksAdAfAvjkA', 5)//第一个元素是转换之后的字符串,第二个元素是转换了多少个
//compile()方法,事先编译好需要匹配的子字符串(或者说是匹配的规则)
import re com = re.compile('\d+') a = com.findall('gh12jn22nf11')//以上两步其实是和 re.findall('\d+','gh12jn22nf11')一模一样,只不过分成了两步 print(a) 结果 ['12', '22', '11'] 这样做的好处是:如果匹配的规则会大量重复使用,那用compile()方法会更省时间,因为规则已经编译好了,只编译一次规则
//finditer()方法,和findall()方法一样,只不过返回的是迭代器对象
import re a = re.finditer('\d+','s23dggwg224534sd2534te26543rfe256') print(a) 结果 <callable_iterator object at 0x000001FE7A8CB2E8> import re a = re.finditer('\d+','s23dggwg224534sd2534te26543rfe256') for each in a: print(each.group())//因为迭代出来的是一个对象,例如search()方法一样,使用要显示值需要用group()方法 结果 23 224534 2534 26543 256