python正则表达式

正则表达式对象

1. re.RegexObject
Function nameDescription
re.compile()返回 RegexObject 对象
2. re.MatchObject
Function nameDescription
group()返回被 RE 匹配的字符串。
start()返回匹配开始的位置
end()返回匹配结束的位置
span()返回一个元组包含匹配 (开始,结束) 的位置
匹配模式

比如忽略大小写,多行模式等,具体参数为:

paramDescription
re.I忽略大小写
re.L表示特殊字符集 \w, \W, \b, \B, \s, \S依赖于当前环境
re.M多行模式
re.S即为 . 并且包括换行符在内的任意字符(. 不包括换行符)
re.U表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S依赖于 Unicode 字符属性数据库
re.X为了增加可读性,忽略空格和 # 后面的注释

RegEx 函数

FunctionDescription
compile编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用
match匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败
searchReturns a Match objectif there is a match anywhere in the string. If fail to match, return None
findallReturns a list containing all matches
splitReturns a list where the string has been split at each match
sub替换字符串中的匹配项
re.match与re.search的区别

re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

实例
import re

basic_str = "The rain in China"
basic_findall_res = re.findall("in", basic_str) # match "in", return Match Object
basic_search_res = re.search("\s", basic_str)   # match white space
basic_split_res = re.split("\s", basic_str, 2)  # Split at each white-space character = str.split(" ")
basic_sub_res = re.sub("\s", "9", basic_str)    # Replace every white-space character with the number 9
print(basic_findall_res)
print(basic_search_res, basic_search_res.group(), basic_search_res.start(), basic_search_res.end())
print(basic_split_res)
print(basic_sub_res)

## 结果:
# ['in', 'in', 'in']
# <re.Match object; span=(3, 4), match=' '>   3 4
# ['The', 'rain', 'in China']
# The9rain9in9China

## compiler
pattern = re.compile(r'\d+')                            # 用于匹配至少一个数字
res = pattern.match('one12twothree34four', 3, 10)       # 从'1'的位置(index=3)开始匹配,正好匹配
print(res)                                              # 结果:<re.Match object; span=(3, 5), match='12'> 
## groups
reg = r"(\w+),?"
str = "aabb,xxx,yysin,ienif"
matchResult = re.finditer(reg, str, re.I)
for result in matchResult:
    result.group()#匹配结果
    result.groups()#匹配结果当中,捕获结果依次组成的元组对象
    if len(result.groups()) > 0:
        for g in result.groups():
            print(g)

## 结果:
# aabb
# xxx
# yysin
# ienif

1. 基础匹配

import re

basic_str = "The rain in China"
basic_search_res = re.search("\s", basic_str)   # match white space
print(basic_findall_res) 						# res:['in', 'in', 'in']

2. 元字符

元字符描述
.句号匹配任意单个字符除了换行符。
[ ]字符种类。匹配方括号内的任意字符。
[^ ]否定的字符种类。匹配除了方括号里的任意字符
*匹配>=0个重复的在*号之前的字符。
+匹配>=1个重复的+号前的字符。
?标记?之前的字符为可选.
{n,m}匹配num个大括号之间的字符 (n <= num <= m).
(xyz)字符集,匹配与 xyz 完全相等的字符串.
|或运算符,匹配符号前或后的字符.
\转义字符,用于匹配一些保留的字符 [ ] ( ) { } . * + ? ^ $ \
^从开始行开始匹配.
$从末端开始匹配.
转码特殊字符

反斜线 \在表达式中用于转码紧跟其后的字符。用于指定 { } [ ] / \ + * . $ ^ | ?这些特殊字符。如果想要匹配这些特殊字符则要在其前面加上反斜线\

import re

s = "The rain in China"
s2 = "pi is nearly equal to 3.4159265359"

print(re.findall(r".h", s))             # ['Th', 'Ch']
print(re.findall(r"[rn]a", s))          # ['Th', 'Ch']
print(re.findall(r"h[^in]", s))         # ['he']
print(re.findall(r"[a-zA-Z]*i", s))     # ['rai', 'i', 'Chi']
print(re.findall(r"\s*rain\s*", s))     # [' rain ']
print(re.findall(r"C.+n", s))           # ['Chin']
print(re.findall(r"[a-z]?in", s))       # ['ain', 'in', 'hin']
print(re.findall(r"(H|h)in|The", s))    # ['', 'h'] . 匹配hin,Hin 或者The
# ^ 指定开头,$ 指定结尾
print(re.findall(r"^[Tt]he", s))        # ['The'] . 配置以The或者the开头的字符串
print(re.findall(r"^[Tt]he.*na$", s))   # ['The'] . 配置以The或者the开头,以na结尾的字符串

print(re.findall(r"[0-9]{2,4}", s2))    # ['4159', '2653', '59']
print(re.findall(r"[0-9]{2,}", s2))     # ['4159265359'] . 至少匹配两位0-9的数字
print(re.findall(r"[0-9\.]{2,4}", s2))  # ['3.41', '5926', '5359']

3. 简写字符集

简写描述
.除换行符外的所有字符
\w匹配所有字母数字,等同于[a-zA-Z0-9_]
\W匹配所有非字母数字,即符号,等同于: [^\w]
\d匹配数字: [0-9]
\D匹配非数字: [^\d]
\s匹配所有空格字符,等同于:[\t\n\f\r\p{Z}]
\S匹配所有非空格字符:[^\s]
\f匹配一个换页符
\n匹配一个换行符
\r匹配一个回车符
\t匹配一个制表符
\v匹配一个垂直制表符
\p匹配CR/LF(等同于\r\n),用来匹配 DOS 行终止符

4.零宽度断言(前后预查)

先行断言和后发断言都属于非捕获簇(不捕获文本 ,也不针对组合计进行计数)。 先行断言用于判断所匹配的格式是否在另一个确定的格式之前,匹配结果不包含该确定格式(仅作为约束)。

例如,我们想要获得所有跟在 $ 符号后的数字,我们可以使用正后发断言 (?<=\$)[0-9\.]*。 这个表达式匹配 $ 开头,之后跟着 0,1,2,3,4,5,6,7,8,9,. 这些字符可以出现大于等于 0 次。

零宽度断言如下:

符号描述
?=正先行断言-存在
?!负先行断言-排除
?<=正后发断言-存在
?<!负后发断言-排除
4.1 ?=… 正先行断言

?=… 正先行断言,表示第一部分表达式之后必须跟着 ?=…定义的表达式。

返回结果只包含满足匹配条件的第一部分表达式。 定义一个正先行断言要使用 ()。在括号内部使用一个问号和等号: (?=…)。

正先行断言的内容写在括号中的等号后面。 例如,表达式 (T|t)he(?=\sfat) 匹配 The 和 the,在括号中我们又定义了正先行断言 (?=\sfat) ,即 The 和 the 后面紧跟着 (空格)fat。

“(T|t)he(?=\sfat)” => Thefat cat sat on the mat.
在线练习

4.2 ?!.. 负先行断言

负先行断言 ?! 用于筛选所有匹配结果,筛选条件为 其后不跟随着断言中定义的格式。 正先行断言 定义和 负先行断言 一样,区别就是 = 替换成 ! 也就是 (?!..)。

表达式 (T|t)he(?!\sfat) 匹配 The 和 the,且其后不跟着 (空格)fat。

“(T|t)he(?!\sfat)” => The fat cat sat on themat.
在线练习

4.3 ?<= … 正后发断言

正后发断言 记作(?<=…) 用于筛选所有匹配结果,筛选条件为 其前跟随着断言中定义的格式。 例如,表达式 (?<=(T|t)he\s)(fat|mat) 匹配 fat 和 mat,且其前跟着 The 或 the。

“(?<=(T|t)he\s)(fat|mat)” => The fatcat sat on the mat.
在线练习

4.4 ?<!.. 负后发断言

负后发断言 记作 (?<!..) 用于筛选所有匹配结果,筛选条件为 其前不跟随着断言中定义的格式。 例如,表达式 (?<!(T|t)he\s)(cat) 匹配 cat,且其前不跟着 The 或 the。

“(?<!(T|t)he\s)(cat)” => The cat sat on cat.

5. 标志

标志也叫模式修正符,因为它可以用来修改表达式的搜索结果。这些标志可以任意的组合使用,它也是整个正则表达式的一部分。

标志描述
i忽略大小写(等同于python中的re.I)。
g全局搜索
m多行修饰符:锚点元字符^ $工作范围在每行的起始 (等同于python中的re.M)
s = "The fat cat sat on the mat."
print(re.findall(r"the", s, re.I))  # ['The', 'the']

6. 贪婪匹配与惰性匹配(Greedy vs lazy matching)

正则表达式默认采用贪婪匹配模式,在该模式下意味着会匹配尽可能长的子串。我们可以使用 ? 将贪婪匹配模式转化为惰性匹配模式

有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复

代码/语法说明
*?重复任意次,但尽可能少重复
+?重复1次或更多次,但尽可能少重复
??重复0次或1次,但尽可能少重复
{n,m}?重复n到m次,但尽可能少重复
{n,}?重复n次以上,但尽可能少重复

“/(.at)/" => The fat cat sat on the mat.
"/(.
?at)/” => The fatcat sat on the mat.

高级技巧

1. 分支条件

正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。看例子:

0\d{2}-\d{8}|0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)

n = "010-12345678"
n2 = "0376-2233445"

print(re.findall("0\d{2}-\d{8}|0\d{3}-\d{7}", n))
print(re.findall("0\d{2}-\d{8}|0\d{3}-\d{7}", n2))

## 结果:
# ['010-12345678']
# ['0376-2233445']

\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。

\d{5}-\d{4}|\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了

2. 分组

我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(后面会有介绍)。

(\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?)

理解这个表达式的关键是理解2[0-4]\d | 25[0-5] | [01]?\d\d?,这里我就不细说了,你自己应该能分析得出来它的意义。

References

  1. https://www.w3schools.com/python/python_regex.asp
  2. https://github.com/ziishaned/learn-regex/blob/master/README.md
  3. https://deerchao.cn/tutorials/regex/regex.htm#greedyandlazy
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值