💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] 参考文章: * https://www.jianshu.com/p/5295c5988b7f ## 正则表达式基础 re正则表达式模块就是字符串的匹配规则 ### 常用的表达式规则 | 元字符 | 说明 | | --- | --- | | . | 代表任意字符 | | | | 逻辑或操作符 | | ^ | 匹配字符串的开头 | | $ | 匹配字符串结束 | | \[ \] | 匹配内部的任一字符或子表达式 | | \[^\] | 对字符集和取非 | | \- | 定义一个区间 | | \\ | 对下一字符取非,通常是普通变特殊 | | \* | 匹配前面的字符或子表达式0次或多次 | | \*? | 惰性匹配上一个 | | + | 匹配前一个字符或子表达式一次或多次 | | +? | 惰性匹配上一个 | | ? | 匹配前一个字符或子表达式0次或1次重复 | | {n} | 匹配前一个字符或子表达式 | | {m,n} | 匹配前一个字符或子表达式至少m次至多n次 | | {n,} | 匹配前一个字符或者子表达式至少n次 | | {n,}? | 前一个的惰性匹配 | |\A | 只从字符开头匹配,同^| |\Z | 匹配字符结尾,同$ | |\d | 匹配数字0-9| |\D | 匹配非数字| |\w | 匹配[A-Za-z0-9]| |\W | 匹配非[A-Za-z0-9]| |s | 匹配空白字符、\t、\n、\r| ### 贪婪匹配和非贪婪匹配 * **贪婪匹配** 当正则表达式中包含能接受重复的限定符时,通常的行为是匹配尽可能多的字符,这被称为贪婪匹配。python的正则匹配默认情况是贪婪匹配: ``` >>> re.findall('a.*b','aabab') ['aabab'] ``` * **非贪婪匹配** 非贪婪匹配就是匹配尽可能少的字符,使用?来表示非贪婪匹配,比如.\*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。 ``` *? 重复任意次,但尽可能少重复 +? 重复1次或更多次,但尽可能少重复 ?? 重复0次或1次,但尽可能少重复 {n,m}? 重复n到m次,但尽可能少重复 {n,}? 重复n次以上,但尽可能少重复 >>> re.findall('a.*?b','aabab') ['aab', 'ab'] >>> ``` ### 分组匹配 1. 分组匹配的语法 ()被括起来的表达式将作为分组。 ```python (?P<name>) 分组,除了原有的编号外再指定一个额外的别名name (?P=name) 引用别名为<name>的分组匹配到字符串 (?:...) 分组的不捕获模式,计算索引时会跳过这个分组,取消优先级,相当于取消括号内分组(去掉括号) \number 匹配和前面索引为number的分组捕获到的内容一样的字符串 (?=...) 顺序肯定环视,表示所在位置右侧能够匹配括号内正则 (?!...) 顺序否定环视,表示所在位置右侧不能匹配括号内正则 (?<=...) 逆序肯定环视,表示所在位置左侧能够匹配括号内正则 (?<!...) 逆序否定环视,表示所在位置左侧不能匹配括号内正则 (?(id/name)yes|no) 若前面指定id或name的分区匹配成功则执行yes处的正则,否则执行no处的正则 ``` 1. 前瞻后顾与环视 有些地方叫前瞻后顾,有些地方叫环视,说的都是一个意思. ``` 前 瞻: exp1(?=exp2) exp1后面的内容要匹配exp2 负前瞻: exp1(?!exp2) exp1后面的内容不能匹配exp2 后 顾: (?<=exp2)exp1 exp1前面的内容要匹配exp2 负后顾: (?<!exp2)exp1 exp1前面的内容不能匹配exp2 例如:我们要查找hello,但是hello后面必须是world,正则表达式可以这样写: "(hello)\s+(?=world)",用来匹配"hello wangxing"和"hello world"只能匹配到后者的hello ``` 2. 分组匹配案例 ``` num="371481199306143242" s=re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})",num) print(s) print(s.groupdict()) 结果: <_sre.SRE_Match object; span=(0, 10), match='3714811993'> {'province': '3714', 'city': '81', 'birthday': '1993'} ``` >此处用到了re的内置对象`groupdict`,详情见高级内置对象部分 ## re正则模块语法 ### re的常用方法语法 1. compile(pattern,flags=0): 编译正则表达式pattern进行并返回一个regex对象供其他方法使用 1. re.match(pattern,string,flags=0): 返回一个匹配对象,从字符串的开头开始匹配的 1. re.search(pattern,string,flags=0): 返回一个匹配对象,查找匹配正则表达式模式pattern的第一次 1. re.findall(pattern,string[,flags]): 返回一个列表,把所有匹配到的字符放到以列表中的元素返回 1. re.finditer(pattern,string[,flags]): 返回一个可迭代对象,和findall()相同,但返回的不是列表而是迭代器 1. re.split(pattern,string,max=0): 以匹配到的字符当做列表分隔符,最多分割max次。 1. re.sub(pattern,rep1,string,max=0): 返回一个字符串。把string中所有匹配正则表达式pattern的地方换成字符串rep1 1. re.fullmatch(pattern, string, flags=0) 整个字符串匹配成功就返回re object, 否则返回None 1. re.group(num=0): 返回全部匹配对象,或指定编号是num的子组。 1. re.groups(): 返回一个包含全部匹配的子组的元组,如果没有成功匹配,就返回一个空元组。 ### 语法参数解析 * pattern 正则表达式 * string 要匹配的字符串 * flags 标志位,用于控制正则表达式的匹配方式 ### 常用flag标志 | 标志 | 含义 | | --- | --- | | re.S (DOTALL) | 使.(点)匹配包括换行在内的所有字符 | | re.I(IGNORECASE) | 使匹配对大小写不敏感 | | re.L(LOCALE) | 做本地化识别(locale-aware)匹配 | | re.M(MULTILINE) | 多行匹配,影响^和$ | | re.X(VERBOSE) | 能够使用 REs 的 verbose 状态,能被组织得更清晰易懂 | | re.U | 根据Unicode字符集解析字符,这个标志影响\w,\W,\b,\B | ### 匹配对象的属性与方法 search、match等方法会返回一个匹配对象,其他方法还可能返回列表,可迭代对象等,这些返回的对象有一些内置方法可以使用,如下 * m.group(g, ...) 返回编号或者组名匹配到的内容,默认或者0表示整个表达式匹配到的内容,如果指定多个,就返回一个元组 * m.groupdict(default) 返回一个字典。字典的键是所有命名的组的组名,值为命名组捕获到的内容 如果有default参数,则将其作为那些没有参与匹配的组的默认值。 * m.groups(default) 返回一个元组。包含所有捕获到内容的子分组,从1开始,如果指定了default值,则这个值作为那些没有捕获到内容的组的值 * m.lastgroup() 匹配到内容的编号最高的捕获组的名称,如果没有或者没有使用名称则返回None(不常用) * m.lastindex() 匹配到内容的编号最高的捕获组的编号,如果没有就返回None。 * m.start(g) 当前匹配对象的子分组是从字符串的那个位置开始匹配的,如果当前组没有参与匹配就返回-1 * m.end(g) 当前匹配对象的子分组是从字符串的那个位置匹配结束的,如果当前组没有参与匹配就返回-1 * m.span() 返回一个二元组,内容分别是m.start(g)和m.end(g)的返回值 * m.re() 产生这一匹配对象的正则表达式 * m.string() 传递给match或者search用于匹配的字符串 * m.pos() 搜索的起始位置。即字符串的开头,或者start指定的位置(不常用) * m.endpos() 搜索的结束位置。即字符串的末尾位置,或者end指定的位置(不常用) ## 语法简单案例 **re.compile(pattern, flags=0)** ``` prog = re.compile(pattern) result = prog.match(string) ``` **re.match(pattern, string, flags=0)** 从起始位置开始根据模型去字符串中匹配指定内容,匹配单个 ``` import re obj = re.match('\d+', '123uuasf') if obj: print obj.group() ``` **re.search(pattern, string, flags=0)** 根据模型去字符串中匹配指定内容,匹配单个 ``` import re obj = re.search('\d+', 'u123uu888asf') if obj: print obj.group()` ``` **re.findall(pattern, string, flags=0)** match and search均用于匹配单值,即:只能匹配字符串中的一个,如果想要匹配到字符串中所有符合条件的元素,则需要使用 findall。 ``` import re obj = re.findall('\d+', 'fa123uu888asf') print obj ``` **re.sub(pattern, repl, string, count=0, flags=0)** 用于替换匹配的字符串 ``` >>>re.sub('[a-z]+','sb','武配齐是abc123',) >>> re.sub('\d+','|', 'alex22wupeiqi33oldboy55',count=2) 'alex|wupeiqi|oldboy55' ``` 相比于str.replace功能更加强大 **re.split(pattern, string, maxsplit=0, flags=0)** ``` >>>s='9-2*5/3+7/3*99/4*2998+10*568/14' >>>re.split('[\*\-\/\+]',s) ['9', '2', '5', '3', '7', '3', '99', '4', '2998', '10', '568', '14'] >>> re.split('[\*\-\/\+]',s,3) ['9', '2', '5', '3+7/3*99/4*2998+10*568/14'] ``` **re.fullmatch(pattern, string, flags=0)** 整个字符串匹配成功就返回re object, 否则返回None ``` re.fullmatch('\w+@\w+\.(com|cn|edu)',"alex@oldboyedu.cn") ``` ## 常用正则表达式 ``` 1、匹配email地址: [\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])? 2、匹配网址URL: [a-zA-z]+://[^\s]* 3、匹配18位身份证号: ^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$ 4、匹配年月日格式: ([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))) 5、匹配整数: ^-?[1-9]\d*$ 6、匹配正整数: ^[1-9]\d*$ 7、匹配负整数: ^-[1-9]\d*$ 8、匹配空白行: \n\s*\r ``` ## 总结 * 对于正则表达式的匹配功能,Python没有返回true和false的方法,但可以通过对match或者search方法的返回值是否是None来判断 * 对于正则表达式的搜索功能,如果只搜索一次可以使用search或者match方法返回的匹配对象得到,对于搜索多次可以使用finditer方法返回的可迭代对象来迭代访问 * 对于正则表达式的替换功能,可以使用正则表达式对象的sub或者subn方法来实现,也可以通过re模块方法sub或者subn来实现,区别在于模块的sub方法的替换文本可以使用一个函数来生成 * 对于正则表达式的分割功能,可以使用正则表达式对象的split方法,需要注意如果正则表达式对象有分组的话,分组捕获的内容也会放到返回的列表中