目录
2.2 re.match() 和 re.search() 匹配和搜索函数
2.3 re.findall() 和 re.finditer() 查找所有符合条件的字符串
4.1 (?iLmsux) 在正则表达式中嵌入一个或者多个特殊"标记"参数
4.3 (?P...) 由名字 name 标示而不是数字 ID 表示的正则分组
4.5 (?#comments) 标示注释,所有内容都被忽略
4.6 (?=...) 正向前行匹配,以 ... 字符串结尾才匹配
4.7 (?!...) 负向前行匹配, 不以 ... 字符串结尾才匹配
4.8 (?<=...) 正向后行匹配,之前有 ... 字符串才进行匹配
4.9 (?<!...) 负向后行匹配,之前没有 ... 字符串才进行匹配
1. 常用正则表达式符号和特殊字符
正则表达式是处理数据时候经常用到的,背景不再赘述。简单说下 Python 中两种模式匹配。
第一种是搜索 searching,第二种是匹配 matching。
搜索即在字符串中任意部分搜索匹配的模式,而匹配是是指判断一个字符串能否从起始处全部或者部分的匹配某个模式。
搜索通过 Re.search() 实现,匹配通过 Re.match() 实现,Re 是 Python 内置的库。
现在来介绍最常用的元字符 ( metacharacter ) --特殊字符和符号,正是它们赋予了正则表达式强大的功能和灵活性。
正则表达式中最常见的符号
记号 | 说明 | 示范 |
普通字符串 | 就匹配该字符串的值 | abc,123,A_B_2 |
re1|re2 | 匹配正则表达式 re1 或者 re2 | abc|123,就是 abc 或者 123 |
. | 匹配除了换行符之外的任何字符 | a.b,意味着可以是abb,acb,a2b 等待 |
^ | 匹配字符串的开始 | ^abc,可以是abcd,abce,但是不能是babc |
$ | 匹配字符串的结尾 | ab$,可以是ab,cab,2ab, 但是不能是abc |
* | 匹配前面出现的正则表达式零次或者多次,该正则表达式可以是单个字符,也可以是()包起来的 | a*,可以是aaaaa,或者空字符 (abc)*,可以是abcabcabc,或者空字符 |
+ | 匹配前面出现的正则表达式1次或者多次,该正则表达式可以是单个字符,也可以是()包起来的 | a+,可以是aaaaa,或者a (abc)+,可以是abcabcabc,或者abc |
? | 匹配前面出现的正则表达式零次或者1次,该正则表达式可以是单个字符,也可以是()包起来的 | a?,可以是a,或者空字符 (abc)?,可以是abc,或者空字符 |
{N} | 匹配前面出现的正则表达式 N 次 | a{4},即aaaa |
{M,N} | 匹配前面出现的正则表达式 M 到 N 次 | a{2,4},即aa,aaa,aaaa |
[....] | 匹配字符组中出现的任何一个字符 | [abc] ,即匹配a、b、c |
[..X-Y....] | 匹配从字符 X 到 Y 的任意字符 | [0-3],即匹配0、1、2、3 |
[^....] | 即不匹配此字符集中出现的任何一个字符 | [^0-3],即匹配除了 0、1、2、3之外的所有字符 |
(*|+|?|{})? | 用于上面出现的任何“非贪婪”。版本重复匹配次数符号。 | 这个需要在讲贪婪模式的时候详细讲。 |
(....) | 匹配封闭括号中正则表达式,并且保存在子组 | 在讲 re.search/match 等具体函数时候可以详细的讲。 |
以上都是正则表达式最最常用的,如果对上面不熟悉,后面的章节可以不看了。
正则表达式-特殊字符
记号 | 示范 | 示范 |
\d | 匹配任何数字,等同于[0-9],( \D 是 \d 的反义:非任何数字) | abc\de,可以是abc0e、abc1e、abc2e 等 |
\w | 匹配任何数字字母字符,等同于[0-9a-zA-Z],( \W 是 \w 的反义:非任何数字字母字符) | _\w,即_0..._9,_a..._z,_A..._Z 都可以 |
\s | 匹配任何空白符号,等同于[\n\r\t\v\f],(\S 是 \s 的反义字符) | ab\sc,可以是"ab c" |
\b | 匹配单词边界,(\B 是 \b 的反义字符) | \bThe\b 意味着The,但是不能是 The 开头的字符串 |
\nn | 匹配已经保存的子组(需要和(.....)一起使用) | 在讲 re.search/match 等具体函数时候可以详细的讲。 |
\c | 逐一匹配特殊字符c(即,取消它的特殊含义,按照字面上匹配) | 如\.,即匹配字符. 如 \\,即匹配字符\ |
\A | 匹配字符串的起始 | \Aabc |
\Z | 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串 | abc\Z |
\
2. Re 模块:核心函数和方法
2.1 re.compile() 编译函数
re.compile 对正则表达式模式 pattern 进行编译,编译后的结果可以用于 re.search、re.match、re.findall 等函数,它并不是必须的,但是它非常有用!
为什么说不是必须的呢,请看下面的一段代码。可以看出,无论是直接用正则表达式还是编译后再使用,都可以得到一样的结果。
a="abc"
patt="^ab"
res=re.match(patt,a)
print(res.group())
patt=re.compile("^ab")
res=re.match(patt,a)
print(res.group())
那为什么还要有 re.compile() 这个函数呢,因为性能!
Python 的代码终究会被编译成字节码,然后才会被解释器执行,如果使用预编译代码会比使用字符串要快,并且我们处理文档时候,往往是一个正则表达式要用很多次,如果直接使用字符串,那么每次都需要重复编译,而直接采用预编译,然后调用预编译结果,则可以快很多。同一个正则表达式被使用得越多越划算。
希望大家养成这个良好的编程习惯!即使模块函数可能对已经编译的对象进行缓存,还是会节省了查询缓存和使用相同字符串反复调用函数的性能开销。
2.1.1 re.compile() 语法格式
re.compile(pattern, flags=0)
pattern 即第一章中提到的正则表达式,flags是可选的,默认为 0。flags 的可选值如下:
- .re.I (re.IGNORECASE): 忽略大小写
- .re.M (MULTILINE): 多行模式,改变'^'和'$'的行为
- .re.S (DOTALL): 点任意匹配模式,改变'.'的行为
- .re.L (LOCALE): 使预定字符类 \w \W \b \B \s \S 取决于当前区域设定
- .re.U (UNICODE): 使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性
- .re.X (VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入
上述 flag 可以单独用,也可以一起使用。
2.1.2 re.compile() 用法
在上一个代码段中就已经显示了,后续也会反复使用,此处不再赘述。
2.2 re.match() 和 re.search() 匹配和搜索函数
2.2.1 re.match() 语法格式
re.match(pattern, string, flags=0)
flags 和 re.compile() 中的 flags 用法一致。
string 即被匹配的字符串。
2.2.2 re.match() 用法
用一小段代码来演示,match 是从字符串的开始进行匹配的。
#匹配全体Python整型的字符串表示形式的集合
patt=re.compile("^[0-9]+$")
words=["1","23","09","www.XXX.net",\
"8.0","a2","_d3234","cd"]
for word in words:
if(re.match(patt,word)):
print(word)
2.2.3 re.search() 语法格式
re.search(pattern, string, flags=0)
flags 和 re.compile() 中的 flags 用法一致。
string 即被匹配的字符串。
2.2.4 re.search() 用法
请注意,search 只找到第一个符合条件字符串的就返回了,所以即使被搜索的字符串中还有其他的符合字符串,也被忽略了。
请注意,此处 match 也好,search 也好,都是返回的一个对象,这个对象有方法 group(),groups(),如果没有找到的时候,返回的是 None 对象。
group() 方法或者返回所有匹配对象,或者是根据特定要求返回某个特定子组。
groups() 方法很简单,它返回一个包含唯一或者所有子组的元组,如果正则表达式中没有子组的话,groups() 将返回一个空元组,而 group() 则仍然返回全部匹配对象。
下面是一个没有子组的代码( 没有使用()来划分子组 )。
# 找出字符串中中包含的 ab 字符串,不区分大小
a="S abc ABC -sfg- dfghdfh abc AB A ABCD ED BA ab Ab aB sgwet"
patt=re.compile("AB",re.I)
res=re.search(patt,a)
print(res.group()) #请注意,此处只找到第一个符合条件的就返回了
print(res.groups())
这是一个有子组的代码,使用了 (),可以看到 group() 返回了全部匹配对象,而 groups() 则返回各自的子组。
# 找出字符串中中包含的 abcde 字符串,不区分大小
a="S abc ABC -sfg- dfghdfh abc AB A ABCDE ED BA ab Ab aB sgwet"
patt=re.compile("(AB)c(DE)",re.I)
res=re.search(patt,a)
print(res.group()) #请注意,此处只找到第一个符合条件的就返回了
print(res.groups())
另外,返回对象中可能有用的 .start() 和 .end() 方法,返回匹配对象起始地址和截止地址。
words=["abcom"]
patt=re.compile("^abcom")
for word in words:
res=re.search(patt,word)
if(res is not None):
print(res) # 返回的是一个 re.Match 对象
print(res.group()) # 返回全部匹配对象
print(res.groups()) # 返回子组,如果有的话,没有则返回空()
print(res.start()) #返回匹配对象起始地址
print(res.end()) #返回匹配对象截至地址
2.3 re.findall() 和 re.finditer() 查找所有符合条件的字符串
2.3.1 re.findall() 语法格式
re.findall(pattern, string, flags=0) flags 和 re.compile() 中的 flags 用法一致。 string 即被匹配的字符串。
2.3.2 re.findall() 用法
re.findall 可以查找出所有的符合要求的字符串,并且返回一个列表。
下面这段代码描述了四种常见的情况。
# 找出字符串中中包含的 abcde 字符串,不区分大小
a="S abc ABC -sfg- dfghdfh abc AB A ABCDE ED BABcdedf ab Ab aB sgwet"
#没有子组
patt=re.compile("ABcDE",re.I)
res=re.findall(patt,a)
# 请注意,此处没有 group()、groups() 对象,因为返回的是列表。
# 因为没有子组,返回的是全部对象的列表
print(res)
#此处有子组
patt=re.compile("(AB)c(DE)",re.I)
res=re.findall(patt,a)
# 请注意,此处没有 group()、groups() 对象,因为返回的是列表。
# 因为有子组,返回的包含子组元组的列表,而不是全部对象的列表
print(res)
#此处有子组,并且把整个字符串用()包括起来了
patt=re.compile("((AB)c(DE))",re.I)
res=re.findall(patt,a)
# 请注意,此处没有 group()、groups() 对象,因为返回的是列表。
# 因为有子组,且把整个字符串用()包括起来了
#所以返回的包含全部匹配对象、各个子组元组的列表,而不是全部对象的列表
print(res)
#此处有子组,子组里面还有子组,并且把整个字符串用()包括起来了
patt=re.compile("((A(B))c(DE))",re.I)
res=re.findall(patt,a)
# 请注意,此处没有 group()、groups() 对象,因为返回的是列表。
# 因为有子组,且把整个字符串用()包括起来了
#所以返回的包含全部匹配对象、各个子组元组的列表,而不是全部对象的列表
print(res)
2.3.3 re.finditer() 语法格式
re.finditer(pattern, string, flags=0)
flags 和 re.compile() 中的 flags 用法一致。
string 即被匹配的字符串。
re.finditer() 和 re.findall() 非常相似,只不过返回的是一个迭代器,而不是列表,对于每个匹配返回一个匹配对象(re.match 匹配对象)。所以读取方式有了变化,这样内存会更加友好。
2.3.4 re.finditer() 用法
将 re.findall 函数的用法处代码稍微改一下。
# 找出字符串中中包含的 abcde 字符串,不区分大小
a="S abc ABC -sfg- dfghdfh abc AB A ABCDE ED BABcdedf ab Ab aB sgwet"
#没有子组
patt=re.compile("ABcDE",re.I)
res=re.finditer(patt,a)
# 请注意,此处没有 group()、groups() 对象,因为返回的是列表。
# 因为没有子组,返回的是全部对象的迭代器
print("*"*28)
for tmp in res:
print(tmp)
print(tmp.group())
print(tmp.groups())
#此处有子组
patt=re.compile("(AB)c(DE)",re.I)
res=re.finditer(patt,a)
# 请注意,此处没有 group()、groups() 对象,因为返回的是迭代器。
# 因为有子组,返回的包含子组元组的迭代器,而不是全部对象的迭代器
print("*"*28)
for tmp in res:
print(tmp)
print(tmp.group())
print(tmp.groups())
#此处有子组,并且把整个字符串用()包括起来了
patt=re.compile("((AB)c(DE))",re.I)
res=re.finditer(patt,a)
# 请注意,此处没有 group()、groups() 对象,因为返回的是迭代器。
# 因为有子组,且把整个字符串用()包括起来了
#所以返回的包含全部匹配对象、各个子组元组的迭代器,而不是全部对象的迭代器
print("*"*28)
for tmp in res:
print(tmp)
print(tmp.group())
print(tmp.groups())
#此处有子组,子组里面还有子组,并且把整个字符串用()包括起来了
patt=re.compile("((A(B))c(DE))",re.I)
res=re.finditer(patt,a)
# 请注意,此处没有 group()、groups() 对象,因为返回的是迭代器。
# 因为有子组,且把整个字符串用()包括起来了
#所以返回的包含全部匹配对象、各个子组元组的迭代器,而不是全部对象的迭代器
print("*"*28)
for tmp in res:
print(tmp)
print(tmp.group())
print(tmp.groups())
2.4 re.split() 分割函数
大家肯定用过 str.split(), re.split()功能更加强大,因为它被设计出来时候就是可以使用正则表达式的。
2.4.1 re.split() 语法结构
re.split(pattern, string, maxsplit=0, flags=0)
flags 和 re.compile() 中的 flags 用法一致。
string 即被匹配的字符串。
maxsplit 是最多能被分割的次数,从左而右计算。
re.split() 返回一个列表。
2.4.2 re.split() 用法
可以看到下面这段代码,虽然剩下的代码还可以分割,但是因为限制了分割的次数,也不再分割。
a="das:fgfd:dfdgf:dadf"
res=re.split(":",a,2)
res
2.5 re.sub() 和 re.subn() 替换函数
2.5.1 re.sub() 语法结构
re.sub(pattern, repl, string, count=0, flags=0)
flags 和 re.compile() 中的 flags 用法一致。
repl 即用来替换的字符串
string 即被替换的字符串。
count 即被替换的次数,为 0 时候意味着全部替换。
re.sub()返回被替换后的字符串
2.5.2 re.sub() 用法
# 找出字符串中中包含的 ab 字符串,不区分大小,并且用 XY 来替换
a="S abc ABC -sfg- dfghdfh abc AB A ABCDE ED BABcdedf ab Ab aB sgwet"
#没有子组
patt=re.compile("AB",re.I)
res=re.sub(patt,"XY",a)
# 请注意,此处返回的是字符串
print(res)
2.5.3 re.subn() 语法结构
re.subn(pattern, repl, string, count=0, flags=0)
flags 和 re.compile() 中的 flags 用法一致。
repl 即用来替换的字符串
string 即被替换的字符串。
count 即被替换的次数,为 0 时候意味着全部替换。
re.sub()返回一个元组,第一个元素是被替换后的字符串,第二个元素是被替换次数的数值。
2.5.4 re.subn() 用法
# 找出字符串中中包含的 ab 字符串,不区分大小,并且用 XY 来替换
a="S abc ABC -sfg- dfghdfh abc AB A ABCDE ED BABcdedf ab Ab aB sgwet"
#没有子组
patt=re.compile("AB",re.I)
res=re.subn(patt,"XY",a)
# 请注意,此处返回的是字符串和替换次数的列表
print(res)
3. 贪婪模式和非贪婪模式
给大家看下面一小段代码,想在一串字符串中找出连续的数字字符,1 个或者多个都可以。结果是找出了最长的那个符合要求的。这就是贪心匹配,也叫贪婪模式,Python 中默认的模式,在从左到右的顺序中求值时候,尽可能的抓取满足匹配的最长的字符。
在使用 "*","+",或者"?" 取长度不确定的字符串时候,都会遇到这种情况。
# 找出连续的数字字符,1 个或者多个
a="0123456789"
patt=re.compile("\d+")
res=re.search(patt,a)
# 请注意,此处返回的是匹配对象
print(res)
print(res.group())
print(res.groups())
如果不想要贪婪模式,只要最短的满足条件的字符串,可以在"*","+",或者"?" 后面使用 “?” 来使用非贪婪模式。
4. 扩展表示法
4.1 (?iLmsux) 在正则表达式中嵌入一个或者多个特殊"标记"参数
之前在说re.compile 、re.match、re.search 等函数,提到了模型 pattern 里面配套使用的 flags。其实,除了re.compile 、re.match、re.search 函数中使用 flags 选项,还可以直接在正则表达式中使用类似的功能,即扩展表达式。常见的有(?iLmsux),下面会一一慢慢说。
首先请注意, (?iLmsux) 只能在正则表达式的开头使用;并且 (?imx) 正则表达式包含三种可选标志:i, m, 或 x ,只影响括号中的区域;(?-imx) 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。
4.1.1 (?i)
首先,(?i) 表示 re.I/IGNORECASE,忽略大小写;
words=["abcom","abco","abCoMD","ABCOM","BDAbcOM"]
#patt=re.compile("(?iabcom)")
for word in words:
res=re.search(r"(?i)abcom",word)
if(res is not None):
print("*"*20)
print(word)
print(res.group())
print(res.groups())
4.1.2 (?L)
等同re.l,re.LOCALE, 表示字符集本地化。即使预定字符类 \w \W \b \B \s \S 取决于当前区域设定 。
这个功能是为了支持多语言版本的字符集使用环境的,比如在转义符\w,在英文环境下,它代表[a-zA-Z0-9_],即所以英文字符和数字。如果在一个法语环境下使用,缺省设置下,不能匹配"é" 或 “ç”。加上这L选项和就可以匹配了。不过这个不适用于中文环境,将会在第五小节讲如何匹配中文字符。
4.1.3 (?m)
等同于 re.M ,即多行模式;
word='''
This line is the first abcom,
another line ABCOM,
that line, it's the bestABcomm
'''
res=re.findall(r"(?im)(^th[\w\s]+)",word)
if(res is not None):
print(res)
4.1.4 (?s)
表示符号 . 可以代表任意符号(正常情况符号 . 表示除 \n 之外的任意符号);等同于.re.S (DOTALL)。用下面的代码,就可以清晰的看出使用 (?s) 和不使用直接的差异了。
word='''
This line is the first abcom,
another line ABCOM,
that line, it's the bestABcomm
'''
res=re.findall(r"(?is)(th.*)",word)
if(res is not None):
print(res)
res=re.findall(r"(?i)(th.*)",word)
if(res is not None):
print(res)
4.1.5 (?u)
等同re.U,re.UNICODE,使用 \w, \W, \b, \B 这些元字符时将按照 UNICODE 定义的属性.
4.1.6 (?x)
等同于 re.X ,冗长模式,允许编写更友好的正则表达式。
4.2 (?:...) 表示一个匹配不用保存的分组
用下面这段代码来看,当使用 re.search 函数时候,如果使用了 (?:...) 语法,那么返回的对象使用 .groups() 方法时候,则不会体现那个子组。
word="I am happy now, let's have a happy day! If you are not happy, please do not tell me!"
res = re.search(r'(?:happy)\s\w{3,4}', word)
if(res is not None):
print("*"*40)
print(res)
print(res.group())
print(res.groups())
print(res.groups(1))
res = re.search(r'(happy)\s\w{3,4}', word)
if(res is not None):
print("*"*40)
print(res)
print(res.group())
print(res.groups())
print(res.groups(1))
再看一个复杂一点的例子
当不使用 (?:) 时候
word="I am happy now! let's have a happy day! "
res = re.search(r'((?:happy)\s(\w+))!', word)
if(res is not None):
print("-"*40)
print(res)
print("res.group()",res.group())
print("res.groups()",res.groups()) #显示所有子组
print("res.group(0)",res.group(0)) #显示全部匹配对象
print("res.group(1)",res.group(1)) #显示第一个()包括的对象
#显示第二个()包括的对象,越过了(?:)分组,直接到(\w+)
print("res.group(2)",res.group(2))
#虽然有第三个(),但是因为(?:)分组不保存,所以实际只有两个分组
print("res.group(3)",res.group(3))
print("res.group(4)",res.group(4)) #这一行其实执行不到
但是使用了(?:)后,子组少了一个
word="I am happy now! let's have a happy day! "
res = re.search(r'((?:happy)\s(\w+))!', word)
if(res is not None):
print("-"*40)
print(res)
print("res.group()",res.group())
print("res.groups()",res.groups()) #显示所有子组
print("res.group(0)",res.group(0)) #显示全部匹配对象
print("res.group(1)",res.group(1)) #显示第一个()包括的对象
#显示第二个()包括的对象,越过了(?:)分组,直接到(\w+)
print("res.group(2)",res.group(2))
#虽然有第三个(),但是因为(?:)分组不保存,所以实际只有两个分组
print("res.group(3)",res.group(3))
print("res.group(4)",res.group(4)) #这一行其实执行不到
4.3 (?P<name>...) 由名字 name 标示而不是数字 ID 表示的正则分组
4.3.1 使用数字 ID 表示分组
一般我们可以用数字来表示各种分组。如下:
word="I am happy now! let's have a happy day! "
res = re.search(r'((happy)\s(\w+))!', word)
if(res is not None):
print("-"*40)
print(res)
print("res.group()",res.group())
print("res.groups()",res.groups()) #显示所有子组
print("res.group(0)",res.group(0)) #显示全部匹配对象
print("res.group(1)",res.group(1)) #显示第一个()包括的对象
print("res.group(2)",res.group(2)) #显示第二个()包括的对象
print("res.group(3)",res.group(3)) #显示第三个()包括的对象
print("res.group(4)",res.group(4)) #因为没有第四个(),所以会报错
使用数字 ID 表示分组的规律如下:
- 分组组从 0 开始编号
- 分组 0 始终存在,表示整个正则表达式的匹配对象
- 匹配对象方法.groups() 以 0 为默认参数,等同于. group()
- 分组从左到右 从 1 向上编号,分组也可以嵌套,第 n 个 ( 对应的就是第 n 个分组。
4.3.2 使用名字 name 表示分组
我们还可以用(?P<name>...) 来取代数字ID。
word="I am happy now! let's have a happy day! "
res = re.search(r'((?P<NAME>happy)\s(\w+)!)', word)
if(res is not None):
print("-"*40)
print(res)
print("res.group()",res.group())
print("res.groups()",res.groups()) #显示所有子组
print("res.group(0)",res.group(0)) #显示全部匹配对象
print("res.group(1)",res.group(1)) #显示第一个()包括的对象
print("res.group(2)",res.group(2)) #显示第二个()包括的对象
print("res.group(3)",res.group(3)) #显示第三个()包括的对象
#显示 NAME 表示的对象,即和 res.group(2) 的结果为同一个
print("res.group('NAME')",res.group('NAME'))
print("res.group(4)",res.group(4)) #因为没有第四个(),所以会报错
4.4 (?P=name) 在正则后面引用前面的组
在同一个字符串中匹配由(?P<name)分组的之前文本,可以使用 (?P=name)。
请注意,此处 pattern 中的 (?P=name) 是为了匹配前面的分组内容,而不是继续分组,所以不视同有效的 (),不会列入数字 ID 的计数中。
用 (?P<name)、 (?P=name)的好处很明显,相对于数字 ID 方式,它不容易出错。
下面用代码来表示:
word="I am happy now! let's have a happy day! "
res = re.search(r'((?P<NAME>happy)\s(\w+)!.*(?P=NAME))', word)
if(res is not None):
print("-"*40)
print(res)
print("res.group()",res.group())
print("res.groups()",res.groups()) #显示所有子组
print("res.group(0)",res.group(0)) #显示全部匹配对象
print("res.group(1)",res.group(1)) #显示第一个()包括的对象
print("res.group(2)",res.group(2)) #显示第二个()包括的对象
print("res.group(3)",res.group(3)) #显示第三个()包括的对象
#显示 NAME 表示的对象,即和 res.group(2) 的结果为同一个
print("res.group('NAME')",res.group('NAME'))
#请注意,此处pattern 虽然有第四个(),但是第四个() 是为了匹配前面的分组内容,
#而不是继续分组,所以不视同有效的()。因为没有有效的第四个(),所以会报错
print("res.group(4)",res.group(4))
4.5 (?#comments) 标示注释,所有内容都被忽略
请注意,此处 pattern 中的 (?#comments) 是为了标示注释,所有内容都被忽略,所以不视同有效的 (),不会列入数字 ID 的计数中。
word="I am happy now! let's have a happy day! "
res = re.search(r'((?#comments-happy)\s(\w+)!)', word)
if(res is not None):
print("-"*40)
print(res)
print("res.group()",res.group())
print("res.groups()",res.groups()) #显示所有子组
print("res.group(0)",res.group(0)) #显示全部匹配对象
print("res.group(1)",res.group(1)) #显示第一个()包括的对象
print("res.group(2)",res.group(2)) #显示第二个()包括的对象
#请注意,此处pattern 虽然有第三个(),但是(?#comments-happy) 是为了注释,
#而不是实际分组,所以不视同有效的()。因为总共只有两个有效的(),所以提取res.groups(3)会报错
print("res.group(3)",res.group(3))
4.6 (?=...) 正向前行匹配,以 ... 字符串结尾才匹配
匹配条件是如果...出现在之后的位置,才进行匹配输入字符串,(?=...) 中的 ... 不参与分组 ;又称作正向前视断言。
举例说明:
word="I am happy now! let's have a happy day! "
res = re.search(r'(\s(\w+)!(?=\s\w{1,3}))', word)
if(res is not None):
print("-"*40)
print(res)
print("res.group()",res.group())
print("res.groups()",res.groups()) #显示所有子组
print("res.group(0)",res.group(0)) #显示全部匹配对象
print("res.group(1)",res.group(1)) #显示第一个()包括的对象
print("res.group(2)",res.group(2)) #显示第二个()包括的对象
#请注意,此处pattern 虽然有第三个(),但是(?=\s\w{1,3})) 是为了正向前行匹配,
#而不是实际分组,所以不视同有效的()。因为总共只有两个有效的(),所以提取res.groups(3)会报错
print("res.group(3)",res.group(3))
4.7 (?!...) 负向前行匹配, 不以 ... 字符串结尾才匹配
匹配条件是如果 ... 不出现在之后的位置,才进行匹配输入字符串,(?!...) 中的 ... 不参与分组 ;又称作负向前视断言
word="I am happy now! let's have a happy day! "
res = re.search(r'(\s(\w+)!(?!\s\w{1,3}))', word)
if(res is not None):
print("-"*40)
print(res)
print("res.group()",res.group())
print("res.groups()",res.groups()) #显示所有子组
print("res.group(0)",res.group(0)) #显示全部匹配对象
print("res.group(1)",res.group(1)) #显示第一个()包括的对象
print("res.group(2)",res.group(2)) #显示第二个()包括的对象
#请注意,此处pattern 虽然有第三个(),但是(?!\s\w{1,3}) 是为了负向前行匹配,
#而不是实际分组,所以不视同有效的()。因为总共只有两个有效的(),所以提取res.groups(3)会报错
print("res.group(3)",res.group(3))
4.8 (?<=...) 正向后行匹配,之前有 ... 字符串才进行匹配
匹配条件是如果 ... 出现在之前的位置,才进行匹配;称作正向后视断言。(?<...) 中的 ... 不参与分组。请注意,Python 不支持可变长度的正向后视断言,会报错:“error: look-behind requires fixed-width pattern”。
word="I am happy now! let's have a happy day! "
res = re.search(r'((?<=\w{5})\s(\w+)!)', word)
if(res is not None):
print("-"*40)
print(res)
print("res.group()",res.group())
print("res.groups()",res.groups()) #显示所有子组
print("res.group(0)",res.group(0)) #显示全部匹配对象
print("res.group(1)",res.group(1)) #显示第一个()包括的对象
print("res.group(2)",res.group(2)) #显示第二个()包括的对象
#请注意,此处 pattern 虽然有第三个(),但是(?<=\w{5}) 是为了正向后视断言,
#而不是实际分组,所以不视同有效的()。因为总共只有两个有效的(),所以提取res.groups(3)会报错
print("res.group(3)",res.group(3))
4.9 (?<!...) 负向后行匹配,之前没有 ... 字符串才进行匹配
匹配条件是如果 ... 不出现在之前的位置,而不使用输入字符串;称作负向后视断言(?<!192\.168\.)
匹配条件是如果 ... 不出现在之前的位置,才进行匹配;又称作负向后视断言。(?<!...) 中的 ... 不参与分组。请注意,Python 不支持可变长度的负向后视断言,会报错:“error: look-behind requires fixed-width pattern”。
word="I am happy now! let's have a happy day! "
res = re.search(r'((?<!\w{5})\s(happy day))', word)
if(res is not None):
print("-"*40)
print(res)
print("res.group()",res.group())
print("res.groups()",res.groups()) #显示所有子组
print("res.group(0)",res.group(0)) #显示全部匹配对象
print("res.group(1)",res.group(1)) #显示第一个()包括的对象
print("res.group(2)",res.group(2)) #显示第二个()包括的对象
#请注意,此处 pattern 虽然有第三个(),但是(?<!\w{5})是为了负向后视断言,
#而不是实际分组,所以不视同有效的()。因为总共只有两个有效的(),所以提取res.groups(3)会报错
print("res.group(3)",res.group(3))
4.10 (?(id/name)Y|N) 匹配不同的字符串
如果分组所提供的 id 或者 name 存在,对于给出的 id
或者 name
,先尝试去匹配 Y
部分的内容;如果 id
或 name
条件不满足,则去匹配 N 部分的内容;|N是可选项。
word="I am happy now! let's have a happy day! "
res = re.search(r'((?P<NAME>Happy|happy)\s(\w+)!)', word)
if(res is not None):
print("-"*40)
print(res)
print("res.group()",res.group())
print("res.groups()",res.groups()) #显示所有子组
print("res.group(0)",res.group(0)) #显示全部匹配对象
print("res.group(1)",res.group(1)) #显示第一个()包括的对象
print("res.group(2)",res.group(2)) #显示第二个()包括的对象
print("res.group(3)",res.group(3)) #显示第三个()包括的对象
#显示 NAME 表示的对象,即和 res.group(2) 的结果为同一个
print("res.group('NAME')",res.group('NAME'))
print("res.group(4)",res.group(4)) #因为没有第四个(),所以会报错
'''
要是大家觉得写得还行,麻烦点个赞或者收藏吧,最好加关注我,想个博客涨涨人气,非常感谢!
'''