一、正则表达式引入
1. 正则表达式简介
正则表达式(regular expression)就是用一个“字符串”来描述一个特征,然后去验证另一个或一批“字符串”是否符合这个特征。比如正则表达式“ab+” 描述的特征是“一个’a’ 和 任意个 ‘b’”,那么’ab’, ‘abb’,'abbbbbbbbbb’都符合这个特征。
正则表达式可以用来:
- 验证字符串是否符合指定特征,比如验证是否是合法的邮件地址;
- 用来查找字符串,从一个长的文本中查找符合指定特征的字符串,比查找固定字符串更加灵活方便;
- 用来替换,比普通的替换更强大。
需要注意的是,正则表达式并非某一个编程语言所独有的,在不同的编程语言中都是适用的,只是不同语言会有各自额外的个性化部分,本文所涉及的正则表达式都是基于Python语言,包括通用的,也包括Python语言独有的。
2. Python的re模块
在Python中,如果需要使用正则表达式对字符串进行各种操作时(如:查找、替换、判断),可以使用re(regular expression)模块。
二、正则表达式详解
1. re模块常用函数和对象
为了方便后续通过Python来演示基本的正则表达式操作,下面先介绍在Python的re模块中涉及的match()函数和match object对象及其基本的对象方法group()。
match()函数
- 函数原型:re.match(pattern, string, flags=0)
- pattern:正则表达式
- string:待匹配字符串
- 函数功能:按照pattern指定的规则,从待匹配字符串的开头开始匹配,如果成功匹配,则返回一个match object,否则返回None。
匹配成功对象
Python中,使用re模块中的函数通过正则表达式成功匹配后,多数函数的返回值都是match object,而不是直接以字符串的形式返回匹配得到的结果(如本文后面将介绍的search()函数等)。
进一步地,可通过match object的一系列方法,对匹配得到的结果进行操作,如获取匹配出的字符串等。
匹配成功对象方法group()
- 方法原型:group([group1, …]) (groupN为非负整数)
- 方法功能:
- 在正则表达式中不使用分组()的情况下,不指定参数或仅能指定参数为0,此时返回匹配得到的字符串整体;
- 在正则表达式中使用分组()的情况下(相关举例请见后续分组匹配操作部分):
- 如果仅指定一个非0正整数,则仅返回一个对应分组位置匹配得到的字符串;
- 如果指定多个参数,则以元组形式返回对应参数数量的字符串;
- 如果指定多个参数,且其中有0,则返回字符串组成的一个元组,且在0对应位置为匹配得到的字符串整体。
2. 常用匹配规则
下面的正则表达式在几乎所有语言中都通用。
2.1 匹配单个字符
符号 | 功能 |
---|---|
\d | 匹配数字,即0-9 |
[ ] | 匹配[ ]中列举出的字符 |
. | 匹配任意一个字符(’\n’除外) |
\D | 匹配非数字 |
\s | 匹配空白,即空格或tab键 |
\S | 匹配非空白 |
\w | 匹配字母、数字、下划线,即a-z、A-Z、0-9、_ |
\W | 匹配非字母、数字、下划线 |
- \d:re.match(r"复仇者联盟\d", “待匹配字符串”),可成功匹配出"复仇者联盟0"、“复仇者联盟1” ...... "复仇者联盟9"这样的字符串。
- []:re.match(r"复仇者联盟[123]", “待匹配字符串”)或者re.match(r"复仇者联盟[1-3]", “待匹配字符串”)可限制只能成功匹配出"复仇者联盟1"、“复仇者联盟2”、“复仇者联盟3”。
2.2 匹配多个字符
符号 | 功能 |
---|---|
{m} | 匹配{m}前一个字符出现m次 |
{m,n} | 匹配{m, n}前一个字符出现m次到n次(注意数字m和n之间不能有空格) |
? | 匹配?前一个正则表示的字符(串)出现0次或1次(同样可以表示正则表达式为非贪婪模式) |
* | 匹配*前一个正则表示的字符(串)出现0次或无限次 |
+ | 匹配+前一个正则表示的字符(串)出现1次或无限次 |
- {m}:re.match(r"\d{11}", “待匹配字符串”),仅当待匹配字符串的至少前11位为数字时,匹配成功;
- {m,n}:re.match(r"\w{1,3}", “待匹配字符串”),仅当待匹配字符串的前1到3个字符是数字、字母、下划线时,匹配成功。
2.3 匹配开头结尾
符号 | 功能 |
---|---|
^ | 匹配字符串开头(需要主要的是,当^不出现在正则表达式的开头时, 表示取反操作) |
$ | 匹配字符串结尾 |
re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", var_name),可判断var_name是否满足变量命名规范:
- 由于match函数默认从字符串开头进行匹配,故符号^可省略;
- [a-zA-Z_]表示:var_name必须以其中任意一个字符开始;
- [a-zA-Z0-9_]*$表示:var_name在以字母或下划线开头后,直到结束,可以跟上0个或任意个数字、字母、下划线。
2.4 分组匹配操作
假设有这样一个需求:请匹配出Gmail格式的邮箱地址,且@符号之前必须有4到20位。
根据前面的知识,可以简单得出对应Python语句为re.match(r"[a-zA-Z0-9_]{4,20}@gmail.com$", email_addr)。
如果基于上述需求,更进一步地,要求写出一个正则表达式,使之除了能够匹配出gmail格式的邮箱,还可以匹配出QQ、Foxmail等格式邮箱,此时则需要利用正则表达式中分组的概念。
字符 | 功能 |
---|---|
| | 匹配|左右任意一个表达式 |
(qwerty) | 将括号中的所有字符作为一个分组 |
\num | 引用分组num匹配到的字符串 |
(?P<name>) | 为分组起别名 |
(?P=name) | 引用别名为name分组匹配到的字符串 |
分组匹配((qwerty)和|结合使用):
- 语法:(pattern1|pattern2|…|patternN)
- 举例:
对于正则表达式:re.match(r"[a-zA-Z0-9_]{4,20}@(gmail|qq|foxmail).com$", email_addr).group(),如果email_addr是符合要求的Gmail、QQ、Foxmail其中任意一种,该语句即返回邮箱全称。
group()方法参数:
对于re.match(r"([a-zA-Z0-9_]{4,20})@(gmail|qq|foxmail).com$", email_addr).group(),假设email_addr为larry_lage@gmail.com:
- 如果在group()方法的参数位置填写1,则该正则的返回值为’larry_page’;
- 如果在group()方法的参数位置填写2,则返回值为’gmail’;
- 如果在group()方法的参数位置填写0, 1, 2,则返回值为(‘larry_page@gmail.com’, ‘larry_page’, ‘gmail’)
引用分组((qwerty)和\num结合使用):
如果现在的需求为:匹配出由一对HTML标签及其包裹内容在内的字符串,由于标签成对出现的特性,方便起见,可用到引用分组。
如:
- 对于正则表达式:re.match(r"<(\w*)>.*</\1>", html_str),在html_str格式满足<tag_name>content</tag_name>时匹配成功;
- 对于正则表达式:re.match(r"<(\w*)><(\w*)>.*</\2></\1>", html_str),在html_str格式满足<tag_name1><tag_name2>content</tag_name2></tag_name1>时匹配成功。
分组起别名:
上述通过引用分组实现的方式,还可以通过为分组起别名的方式来实现,即:
对于正则表达式:re.match(r"<(?P<p1>\w*)><(?P<p2>\w*)>.*</(?P=p2)></(?P=p1)>", html_str),在html_str格式满足<tag_name1><tag_name2>content</tag_name2></tag_name1>时也可以匹配成功。
3. re模块高级函数和对象
3.1 search
- 函数原型:re.search(pattern, string, flags=0)
- pattern:同函数match()
- string:同函数match()
- 函数功能:扫描待匹配字符串,尝试匹配第一个符合pattern所指定规则的字符串,如果成功匹配,则返回一个match object,否则返回None。
- 使用案例:
对于正则:re.search(r"\d+", “当前Python在编程榜上排名为3”).group(),其返回值为3。
3.2 findall
- 函数原型:re.findall(pattern, string, flags=0)
- pattern:同上
- string:同上
- 函数功能:
- 以列表形式返回通过pattern匹配string成功后的所有字符串;
- pattern以从左往右的顺序扫描string,返回的字符串按照匹配的顺序排列;
- 如果在pattern中存在分组,则以列表形式返回一系列元组。
- 使用案例:
对于正则:re.findall(r"\d+", “当前Python在编程榜上排名为3,Java为1”),其返回值为[‘3’, ‘1’]。
3.3 sub
- 函数原型:re.sub(pattern, repl, string, count=0, flags=0)
- pattern:同上
- string:同上
- repl:希望实现替换的对象,可以是字符串或者函数(repl是函数的情形请见Python正则表达式模块中函数sub的高级用法)。
- count:该参数用于指定最大的匹配替换次数,必须为非负整数,该参数如果省略或指定为0,则所有得到的匹配都将被替换。
- 函数功能:返回一个被repl替换后的字符串,该字符串的生成方式为:将string中所有符合规则pattern的子字符串全部替换成repl,且在替换时进行非重叠处理(关于非重叠的含义请见Python正则表达式模块中函数sub的高级用法。
- 使用案例:
对于正则:re.sub(r"\d+", “1”, “当前Python在编程榜上排名为3”),其返回值为’当前Python在编程榜上排名为1’。
3.4 split
- 函数原型:re.split(pattern, string, maxsplit=0, flags=0)
- pattern:同上
- string:同上
- maxsplit:非负整数,如果不指定或指定为0,则按照匹配出string中有符合pattern规则的次数来分割待匹配字符串,如指定非零正整数,则最多分割maxsplit次。
- 函数功能:
- 通过_pattern成功匹配出的字符串对string进行分割并以列表形式返回分割结果;
- 如果在pattern中使用分组符号(),则通过pattern匹配出的用于的分割字符串也会在列表中被返回。
- 使用案例:
- 对于正则1:re.split(r’\W+’, ‘Words, words, words.’),其结果为:[‘Words’, ‘words’, ‘words’, ‘’]
- 对于正则2:re.split(r’(\W+)’, ‘Words, words, words.’):,其结果为:[‘Words’, ', ', ‘words’, ', ‘, ‘words’, ‘.’, ‘’](用于分割的字符串’, '也被包含在了返回的列表中)。
4. 贪婪和非贪婪模式
- 前面介绍的正则表达式修饰符*、+、?都会结合其前后的正则表达式,尽可能匹配出符合要求的最长字符串,这就是所谓的贪婪模式。
- 修饰符?可用于指定其之前的正则表达式仅匹配出符合要求的最短字符串,这就是所谓的非贪婪模式。
举例来说:
- re.match(r"<.*>", “<a>b<c>”).group()的结果为’<a>b<c>’,这就是所谓贪婪匹配;
- re.match(r"<.*?>", “<a>b<c>”).group()(即加上非贪婪模式修饰符?)的结果为’<a>’,这就是所谓非贪婪匹配。
5. 原始字符串符号r
四、参考材料
转载:https://blog.csdn.net/weixin_37780776/article/details/105624008