最近选修了学校的Python课,课程内容比我想象的还要基础。最近讲到了正则表达式,上课闲来无事我就刚好来整理一下吧,找回我遗失的记忆
首先给出一个很好用的网站:https://regex101.com/
这篇博客主要包括两个部分:
- 正则表达式
- Python.re模块
正则表达式
正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。
匹配字符
代码 | 功能 |
---|---|
. |
通配符,匹配任意1个字符(除了\n 换行) |
[] |
匹配[ ] 中列举的字符,里面的元素之间存在“或”的关系,[abc] 只能匹配a或b或c |
\d |
匹配数字,等价于[0,9] |
\D |
匹配非数字,等价于[^0-9] |
\s |
匹配空白,常见的空白符的形式有空格、制表符 (\t )、换行 (\n ) 和回车 (\r ) |
\S |
匹配非空白 |
\w |
匹配非特殊字符,即a-z、A-Z、0-9、_、中文 |
\W |
匹配特殊字符 |
\b |
匹配单词字符(字母、数字、下划线 _)和非单词字符(例如空格、标点符号、字符串的开头或结尾)之间的位置。简单来说,它标记了一个“单词”的开始或结束。 |
\B |
匹配任何不是单词边界的位置。也就是说,它匹配两个单词字符之间,或者两个非单词字符之间的位置。 |
^ |
匹配字符串开头 |
$ |
匹配字符串结尾 |
\d
\D
匹配数字
import re
str0 = """北向资金尾盘回流,全天净买入18.54亿元,连续4日加仓"""
result = re.findall(r'\d', str0) # 加上r模式是为了防止Python中的转义字符与正则表达式中的元字符
print(result)
结果为:
['1', '8', '5', '4', '4']
匹配非数字
import re
str0 = """北向资金尾盘回流,全天净买入18.54亿元,连续4日加仓"""
result = re.findall(r'\D', str0) # 加上r模式是为了防止Python中的转义字符与正则表达式中的元字符
print(result)
结果为:
['北', '向', '资', '金', '尾', '盘', '回', '流', ',', '全', '天', '净', '买', '入', '
\s
\S
text = '''please don't
leave me
alone'''
result = re.findall(r'\s',text)
print(result)
结果为:
[' ', ' ', '\n', ' ', ' ', '\n']
\b
\B
下面我们通过 Python 代码示例来具体说明它们的区别:
import re
text = "This is a test string. word another_word 123"
print("原始文本:", text)
print("-" * 30)
# 使用 \b 匹配以 "word" 开头的单词
pattern_b_start = r"\bword"
matches_b_start = re.finditer(pattern_b_start, text)
print(r"使用 '\bword' 匹配:")
for match in matches_b_start:
print(f" - 在位置 {match.start()} 找到: '{match.group()}'")
# 使用 \b 匹配以 "word" 结尾的单词
pattern_b_end = r"word\b"
matches_b_end = re.finditer(pattern_b_end, text)
print(r"使用 'word\b' 匹配:")
for match in matches_b_end:
print(f" - 在位置 {match.start()} 找到: '{match.group()}'")
# 使用 \B 匹配 "word" 中间的 "or"
pattern_B_middle = r"w\Bor"
matches_B_middle = re.finditer(pattern_B_middle, text)
print(r"使用 'w\Bor' 匹配:")
for match in matches_B_middle:
print(f" - 在位置 {match.start()} 找到: '{match.group()}'")
# 使用 \B 匹配 "another_word" 中的 "other"
pattern_B_inside_word = r"other\B"
matches_B_inside_word = re.finditer(pattern_B_inside_word, text)
print(r"使用 'other\B' 匹配:")
for match in matches_B_inside_word:
print(f" - 在位置 {match.start()} 找到: '{match.group()}'")
结果为:
原始文本: This is a test string. word another_word 123
------------------------------
使用 '\bword' 匹配:
- 在位置 22 找到: 'word'
使用 'word\b' 匹配:
- 在位置 22 找到: 'word'
使用 'w\Bor' 匹配:
- 在位置 23 找到: 'or'
使用 'other\B' 匹配:
- 在位置 29 找到: 'other'
text
: 这是我们用来进行匹配的示例字符串。pattern_b_start = r"\bword"
: 这个正则表达式尝试匹配以 "word" 开头的单词。\b 确保了 "word" 前面是一个单词边界(例如空格或字符串的开头)。在我们的文本中,它会匹配到 "word"。pattern_b_end = r"word\b"
: 这个正则表达式尝试匹配以 "word" 结尾的单词。\b 确保了 "word" 后面是一个单词边界(例如空格或字符串的结尾)。在我们的文本中,它会匹配到 "word"。pattern_B_middle = r"w\Bor"
: 这个正则表达式尝试匹配 "word" 中间的 "or"。\B 确保了 "w" 和 "or" 之间不是一个单词边界,这意味着它们必须在同一个“单词”内部。它会匹配到 "word" 中的 "or"。pattern_B_inside_word = r"other\B"
: 这个正则表达式尝试匹配 "another_word" 中的 "other"。\B 确保了 "other" 后面不是一个单词边界,这意味着它必须在同一个“单词”内部。它会匹配到 "another_word" 中的 "other"。
^
$
import re
a = re.findall(r'^s','son')
print(a)
b = re.findall(r'^s','abs')
print(b)
c = re.findall(r's$','son')
print(c)
d = re.findall(r's$','abs')
print(d)
结果为:
['s']
[]
[]
['s']
量词:匹配多个字符
某个字符后的数量限定符(*
、 ?
、 +
)用来限定前面这个字符允许出现的个数。最常见的数量限定符包括 *
、 ?
和 +
(不加数量限定则代表出现一次且仅出现一次)
符号 | 匹配规则 | 示例 |
---|---|---|
* |
匹配前一个字符出现0到多次,即可有可无,等价于{0,} |
ab*c 匹配 ac, abc, abbc, abbbc |
+ |
匹配前一个字符出现1到多次,即至少有1次,等价于{1,} |
colou?r 匹配 color 和 colour |
? |
匹配前一个字符出现1次或者0次,即要么有1次,要么没有,等价于{0,1} | ab+c 匹配 abc, abbc, abbbc 等等,但不匹配 ac |
{n} |
匹配前一个字符出现n次 | |
{n,} |
匹配前一个字符至少出现n次 | |
{n,m} |
匹配前一个字符出现从n到m次 |
import re
str0 = """北向资金尾盘回流,全天净买入18.54亿元,连续4日加仓;其中沪股通净买入26.41亿元,深股通净卖出7.87亿元。"""
result = re.findall(r'\d+', str0)
print(result)
“+
”在正则表达式里代表的是“匹配前面的子表达式一次或多次”。
在“\d+
”中,子表达式就是“\d
”,一个“\d
”是匹配一个数字,“\d+
”则就是匹配一个或者多个数字。
结果为:
['18', '54', '4', '26', '41', '7', '87']
在以上结果中,数字被小数点分开了,如18.54被分成了18和54。那么我们可以再做这样的改进。
result = re.findall(r'[\d.]+', str0)
print(result)
[]方括号代表字符集合。匹配所包含的任意一个字符。“[\d.]
”即匹配一个“\d
”或者一个“.
”字符,再带上加“+
”,就可以匹配一个或者多个了。
结果为:
['18.54', '4', '26.41', '7.87']
星号 *
u*
:字符u可以出现0次或1次或者多次
a = re.findall(r'colou*r', 'color')
print(a)
b = re.findall(r'colou*r', 'colour')
print(b)
c = re.findall(r'colou*r', 'colouuur')
print(c)
结果为:
['color']
['colour']
['colouuur']
加号+
u+
:字符u可以出现 1 次或多次
a = re.findall(r'colou+r', 'color')
print(a)
b = re.findall(r'colou+r', 'colour')
print(b)
c = re.findall(r'colou+r', 'colouuur')
print(c)
结果为:
[]
['colour']
['colouuur']
问号?
u?
:字符u可以出现 0 次或 1 次
a = re.findall(r'colou?r', 'color')
print(a)
b = re.findall(r'colou?r', 'colour')
print(b)
c = re.findall(r'colou?r', 'colouuur')
print(c)
结果为:
['color']
['colour']
[]
大括号 {}
有的时候我们非常明确要匹配的字符出现几次,比如
- 中国的手机号位数是 13 位,n = 13
- 密码需要 8 位以上,n ≥ 8
- 用户名需要在 8 到 16 位之间,8 ≤ n ≤ 16
这时我们可以设定具体的上界或(和)下界,使得代码更加有效也更好读懂,规则如下:
- {n} 左边的字符串是否出现 n 次
- {n, } 左边的字符串是否出现大于等于 n 次
- {, n} 左边的字符串是否出现小于等于 n 次
- {n, m} 左边的字符串是否出现在 n 次和 m 次之间
import re
a = re.findall(r'[a-z]{1}','a11bbb2222ccccc')
print(a)
b = re.findall(r'[0-9]{2,}','a11bbb2222ccccc')
print(b)
c = re.findall(r'[a-z]{1,4}','a11bbb2222ccccc')
print(c)
d = re.findall(r'[0-9]{2,3}','a11bbb2222ccccc')
print(d)
结果为:
['a', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'c']
['11', '2222']
['a', 'bbb', 'cccc', 'c']
['11', '222']
集合字符 []
中括号表示一个字符集,即创建的模式匹配中括号里指定字符集中的任意一个字符,字符集有三种方式来表现:
- 明确字符:
[abc]
会匹配字符 a,b 或者 c - 范围字符:
[a-z]
会匹配字符 a 到 z - 补集字符:
[^6]
会匹配除了 6 以外的字符
明确字符
import re
result = re.findall(r'[abc]','abandon')
print(result)
结果为:
['a', 'b', 'a']
范围字符
在 [ ] 中加入 - 即可设定范围,比如:
[a-e]
=[abcde]
[1-4]
=[1234]
[a-ep]
=[abcdep]
[0-38]
=[01238]
[A-Za-z0-9_]
= 大小写字母、数字、下划线
补集字符
在 [ ] 中加入 ^ 即可除去后面的字符集,比如
[^abc]
就是非 a, b, c 的字符[^123]
就是非 1, 2, 3 的字符
import re
result = re.findall(r'[^abc]','baba')
print(result)
result = re.findall(r'[^abc]','steven')
print(result)
result = re.findall(r'[^123]','456')
print(result)
result = re.findall(r'[^123]','1+2=3')
print(result)
结果为:
[]
['s', 't', 'e', 'v', 'e', 'n']
['4', '5', '6']
['+', '=']
常见字符集
常见的字符集有:
[A-Za-z0-9]
表示匹配任意大小写字母和单个数字[a-z]
表示匹配a至z之间的一个小写英文字母[A-Z]
表示匹配A至Z之间的一个大写英文字母[0-9]
表示匹配0至9之间的一个数字[\u4e00-\u9fa5]
表示匹配中文字符
当然,也可以进行组合,比如说:
[a-zA-Z0-9_\u4e00-\u9fa5]+
表示匹配至少一个汉字、数字、字母、下划线
import re
str0 = """北向资金尾盘回流,全天净买入18.54亿元,连续4日加仓;其中沪股通净买入26.41亿元,深股通净卖出7.87亿元。"""
result = re.findall(r"[\u4e00-\u9fa5]+", str0) # 匹配中文字符
print(result)
结果为:
['北向资金尾盘回流', '全天净买入', '亿元', '连续', '日加仓', '其中沪股通净买入', '亿元', '深股通净卖出', '亿元']
贪婪模式和非贪婪模式
在Python的正则表达式中,默认是贪婪模式,尽可能多的匹配;在量词后面直接加上一个问号"?",可以将贪婪模式转换成为非贪婪模式,尽可能少的匹配,即一旦匹配到结果就结束。
量词包括如下:
{m,n}
*
:{0,}
+
:{1,}
?
:{0,1}
【例1】
import re
# '.*' 表示匹配 0 个或多个任意字符
a = re.search('11.*11', '11-22-11-22-11')
print('贪婪模式:',a.group())
# 加上 '?' 设置为非贪婪模式
b = re.search('11.*?11', '11-22-11-22-11')
print('非贪婪模式:',b.group())
结果为:
贪婪模式: 11-22-11-22-11
非贪婪模式: 11-22-11
【例2】
# '.*' 表示匹配 0 个或多个任意字符
a = re.search(r'a.*c', 'sljad38c32ic')
print('贪婪模式:',a.group())
# 变为非贪婪模式,尽可能少的匹配
b = re.search(r'a.*?c', 'sljad38c32ic')
print('非贪婪模式:',b.group())
结果为:
贪婪模式: ad38c32ic
非贪婪模式: ad38c
Python re模块
re
模块使 Python 语言拥有全部的正则表达式功能。
接下来主要介绍Python中常用的正则表达式处理函数。
span()
、start()
、end()
方法
在 Python 中,span()
方法通常与正则表达式的匹配对象(match object)一起使用。当你使用 re 模块中的函数(如 re.search()
, re.match()
, re.finditer()
)成功找到匹配项时,它们会返回一个匹配对象。这个匹配对象就拥有 span()
方法。
同理,start()
、end()
方法与之类似,返回起始位置、终止位置
span()
方法的作用:
span()
方法用于返回匹配对象的起始位置和结束位置的索引。它返回一个包含两个整数的元组(start, end)
,其中:
start
: 是匹配到的子字符串在原始字符串中的起始索引(包含)。
end
: 是匹配到的子字符串在原始字符串中的结束索引(不包含)。
语法:
match_object.span(group=0)
match_object
: 是通过re.search()
,re.match()
, 或re.finditer()
等函数返回的匹配对象。group
: 是可选参数,用于指定要获取其起始和结束索引的捕获组。- 如果
group=0
(默认值),则返回整个匹配的起始和结束索引。 - 如果
group
是一个正整数,则返回对应编号的捕获组的起始和结束索引。 - 如果
group
是一个组名(如果正则表达式中使用了命名捕获组),则返回该命名捕获组的起始和结束索引。
- 如果
举例:
import re
string = "Hello World 123 Python"
pattern = r"\d+" # 匹配一个或多个数字
match = re.search(pattern, string)
if match:
print(f"找到匹配项:{match.group()}")
print(f"匹配项的起始和结束位置:{match.span()}")
# 输出:
# 找到匹配项:123
# 匹配项的起始和结束位置:(12, 15)
group()
方法
在 Python 的 re
模块中,group()
方法是正则表达式匹配对象(match object)的一个非常常用的方法。当你使用 re.search()
, re.match()
, re.finditer()
等函数成功找到匹配项时,它们会返回一个匹配对象,而这个匹配对象就拥有 group()
方法。
group()
方法的作用:
group()
方法用于返回匹配对象中匹配到的一个或多个子字符串。它可以接受不同的参数来返回不同的内容:
group()
或group(0)
: 返回整个匹配的字符串。group(n)
: 返回编号为 n 的捕获组匹配到的字符串,其中 n 是从 1 开始的整数。捕获组是在正则表达式中使用括号 () 包裹的部分。group(name)
: 返回匹配到的具有指定名称的捕获组的字符串。这需要在正则表达式中使用(?P<name>...)
语法来命名捕获组。group(n1, n2, ...)
或group(name1, name2, ...)
: 返回包含指定编号或名称的捕获组匹配到的字符串的元组。
语法:
match_object.group(group1=0, group2=0, ...)
match_object
: 是通过 re.search(), re.match(), 或 re.finditer() 等函数返回的匹配对象。
group1, group2, ...
: 是可选参数,可以是整数(表示捕获组的编号,从 1 开始)、字符串(表示捕获组的名称),或者省略(默认为 0,表示返回整个匹配)。
返回整个匹配的字符串 (group()
或 group(0)
):
import re
string = "Hello World 123 Python"
pattern = r"\d+" # 匹配一个或多个数字
match = re.search(pattern, string)
if match:
print(f"整个匹配项:{match.group()}")
print(f"整个匹配项(使用 group(0)):{match.group(0)}")
# 输出:
# 整个匹配项:123
# 整个匹配项(使用 group(0)):123
返回指定编号的捕获组 (group(n)
):
import re
string = "Name: Alice, Age: 30"
pattern = r"Name: (\w+), Age: (\d+)" # 匹配姓名和年龄,并使用括号创建捕获组
match = re.search(pattern, string)
if match:
print(f"整个匹配项:{match.group()}")
print(f"第一个捕获组(姓名):{match.group(1)}")
print(f"第二个捕获组(年龄):{match.group(2)}")
# 输出:
# 整个匹配项:Name: Alice, Age: 30
# 第一个捕获组(姓名):Alice
# 第二个捕获组(年龄):30
返回指定名称的捕获组 (group(name)
):
import re
string = "Email: user@example.com"
pattern = r"Email: (?P<username>\w+)@(?P<domain>[\w.]+)" # 使用 (?P<name>...) 命名捕获组
match = re.search(pattern, string)
if match:
print(f"整个匹配项:{match.group()}")
print(f"用户名:{match.group('username')}")
print(f"域名:{match.group('domain')}")
# 输出:
# 整个匹配项:Email: user@example.com
# 用户名:user
# 域名:example.com
返回多个捕获组的元组 group(n1, n2, ...
或 group(name1, name2, ...)
:
import re
string = "Coordinates: 10, 20"
pattern = r"Coordinates: (\d+), (\d+)"
match = re.search(pattern, string)
if match:
print(f"匹配到的坐标:{match.group(1, 2)}")
string_named = "Info: Name=Bob; Age=25"
pattern_named = r"Info: Name=(?P<name>\w+); Age=(?P<age>\d+)"
match_named = re.search(pattern_named, string_named)
if match_named:
print(f"匹配到的信息:{match_named.group('name', 'age')}")
# 输出:
# 匹配到的坐标:('10', '20')
# 匹配到的信息:('Bob', '25')
re.match()
函数
re.match
尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,就返回 none
。
即使模式在字符串的其他位置可以找到匹配,但如果不是从字符串的开头开始,re.match()
也不会匹配成功。
函数语法:
re.match(pattern,string,flags=0)
函数参数说明:
参数 | 描述 |
---|---|
pattern | 匹配的正则表达式 |
string | 要匹配的字符串。 |
flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志 |
匹配成功 re.match
方法返回一个匹配的对象,否则返回 None
。
我们可以使用 group(num)
或 groups()
匹配对象函数来获取匹配表达式。
举例:
import re
# 示例 1: 匹配成功
string1 = "hello world"
pattern1 = r"hello"
match1 = re.match(pattern1, string1)
if match1:
print(f"示例 1: 匹配成功!匹配对象: {match1.group()}")
else:
print("示例 1: 匹配失败!")
# 示例 2: 匹配失败 (模式不在字符串开头)
string2 = "world hello"
pattern2 = r"hello"
match2 = re.match(pattern2, string2)
if match2:
print(f"示例 2: 匹配成功!匹配对象: {match2.group()}")
else:
print("示例 2: 匹配失败!")
# 示例 3: 匹配成功 (使用分组)
string3 = "Python 3.9"
pattern3 = r"Python (\d+\.\d+)"
match3 = re.match(pattern3, string3)
if match3:
print(f"示例 3: 匹配成功!完整匹配: {match3.group(0)}, 第一个分组: {match3.group(1)}")
else:
print("示例 3: 匹配失败!")
# 示例 4: 匹配成功 (空字符串)
string4 = ""
pattern4 = r"" # 匹配空字符串
match4 = re.match(pattern4, string4)
if match4:
print(f"示例 4: 匹配成功!匹配对象: {match4.group()}")
else:
print("示例 4: 匹配失败!")
# 示例 5: 匹配失败 (模式不完全匹配开头)
string5 = "hello world"
pattern5 = r"hell" # 注意这里少了一个 'o'
match5 = re.match(pattern5, string5)
if match5:
print(f"示例 5: 匹配成功!匹配对象: {match5.group()}")
else:
print("示例 5: 匹配失败!")
运行结果:
示例 1: 匹配成功!匹配对象: hello
示例 2: 匹配失败!
示例 3: 匹配成功!完整匹配: Python 3.9, 第一个分组: 3.9
示例 4: 匹配成功!匹配对象:
示例 5: 匹配失败!
各个示例的解释:
- 示例 1: 模式
r"hello"
完全匹配了字符串string1
的开头,因此匹配成功。 - 示例 2: 模式
r"hello"
存在于string2
中,但它不是从字符串的开头开始的,所以re.match()
返回None
,匹配失败。 - 示例 3: 模式
r"Python (\d+\.\d+)"
成功匹配了string3
的开头。"Python "
被直接匹配,(\d+\.\d+)
使用分组捕获了版本号"3.9"
。 - 示例 4: 模式
r""
(空字符串) 可以匹配任何字符串的开头(包括空字符串本身),因此匹配成功。 - 示例 5: 模式
r"hell"
只是string5
开头的一部分,但re.match()
要求模式从字符串的开头完全匹配,因此匹配失败。
re.search()
函数
re.search
扫描整个字符串并返回第一个成功的匹配。
与 re.match()
不同,re.search()
不要求模式必须从字符串的开头开始匹配,它可以在字符串的任何位置找到匹配。
函数语法:
re.search(pattern, string, flags=0)
参数说明同上
举例:
import re
# 示例 1: 在字符串中间找到匹配
string1 = "This is a test string with the word 'example' in it."
pattern1 = r"example"
match1 = re.search(pattern1, string1)
if match1:
print(f"示例 1: 找到匹配!匹配对象: {match1.group()}, 起始位置: {match1.start()}, 结束位置: {match1.end()}")
else:
print("示例 1: 未找到匹配!")
# 示例 2: 在字符串开头找到匹配
string2 = "Start with this example string."
pattern2 = r"example"
match2 = re.search(pattern2, string2)
if match2:
print(f"示例 2: 找到匹配!匹配对象: {match2.group()}, 起始位置: {match2.start()}, 结束位置: {match2.end()}")
else:
print("示例 2: 未找到匹配!")
# 示例 3: 没有找到匹配
string3 = "No matching word here."
pattern3 = r"python"
match3 = re.search(pattern3, string3)
if match3:
print(f"示例 3: 找到匹配!匹配对象: {match3.group()}")
else:
print("示例 3: 未找到匹配!")
# 示例 4: 使用分组捕获信息
string4 = "The price is $123.45."
pattern4 = r"[$](\d+\.\d+)"
match4 = re.search(pattern4, string4)
if match4:
print(f"示例 4: 找到匹配!完整匹配: {match4.group(0)}, 价格: {match4.group(1)}")
else:
print("示例 4: 未找到匹配!")
# 示例 5: 对比 re.match() 和 re.search()
string5 = "The word is here."
pattern5 = r"word"
match_match = re.match(pattern5, string5)
if match_match:
print(f"示例 5 (re.match): 找到匹配!匹配对象: {match_match.group()}")
else:
print("示例 5 (re.match): 未找到匹配!")
match_search = re.search(pattern5, string5)
if match_search:
print(f"示例 5 (re.search): 找到匹配!匹配对象: {match_search.group()}")
else:
print("示例 5 (re.search): 未找到匹配!")
结果:
示例 1: 找到匹配!匹配对象: example, 起始位置: 37, 结束位置: 44
示例 2: 找到匹配!匹配对象: example, 起始位置: 16, 结束位置: 23
示例 3: 未找到匹配!
示例 4: 找到匹配!完整匹配: $123.45, 价格: 123.45
示例 5 (re.match): 未找到匹配!
示例 5 (re.search): 找到匹配!匹配对象: word
re.compile()
函数
compile
函数用于编译正则表达式,生成一个正则表达式( Pattern
)对象,可以给 match()
、 search()
以及 findall
等函数使用。
语法格式为:
re.compile(pattern[, flags])
举例
import re
text = "The quick brown fox jumps over the lazy dog. The fox is brown."
# 方式 1: 直接使用 re.search() (每次都编译)
pattern_str_direct = r"fox"
match_direct1 = re.search(pattern_str_direct, text)
if match_direct1:
print(f"直接使用 re.search() - 第一次匹配: {match_direct1.group()}")
match_direct2 = re.search(pattern_str_direct, text)
if match_direct2:
print(f"直接使用 re.search() - 第二次匹配: {match_direct2.group()}")
print("-" * 30)
# 方式 2: 使用 re.compile() (预编译一次)
pattern_compiled = re.compile(r"fox")
match_compiled1 = pattern_compiled.search(text)
if match_compiled1:
print(f"使用 re.compile() - 第一次匹配: {match_compiled1.group()}")
match_compiled2 = pattern_compiled.search(text)
if match_compiled2:
print(f"使用 re.compile() - 第二次匹配: {match_compiled2.group()}")
print("-" * 30)
# 示例 2: 更复杂的模式和多次使用
log_data = """
[ERROR] 2023-10-26 10:00:00 - File not found
[INFO] 2023-10-26 10:00:05 - User logged in
[ERROR] 2023-10-26 10:00:10 - Connection timeout
"""
error_pattern = re.compile(r"\[ERROR\] (.*)")
error_matches = error_pattern.findall(log_data)
print("使用 compile() 查找所有错误信息:")
for error in error_matches:
print(f"- {error}")
print("-" * 30)
# 示例 3: 使用 compile() 进行匹配和分组
date_pattern = re.compile(r"(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})")
match_date = date_pattern.search(log_data)
if match_date:
print(f"匹配到的日期: {match_date.group(1)}")
print(f"匹配到的时间: {match_date.group(2)}")
运行结果:
直接使用 re.search() - 第一次匹配: fox
直接使用 re.search() - 第二次匹配: fox
------------------------------
使用 re.compile() - 第一次匹配: fox
使用 re.compile() - 第二次匹配: fox
------------------------------
使用 compile() 查找所有错误信息:
- 2023-10-26 10:00:00 - File not found
- 2023-10-26 10:00:10 - Connection timeout
------------------------------
匹配到的日期: 2023-10-26
匹配到的时间: 10:00:00
使用 re.compile()
的优点:
- 提高效率: 如果同一个正则表达式需要在代码中多次使用,预编译可以避免重复编译,从而提高程序的执行效率。
- 代码可读性: 将正则表达式模式定义在一个变量中,可以使代码更易读和维护。模式的意图更加清晰。
- 方便复用: 编译后的模式对象可以在不同的地方被复用,减少了代码的冗余。
re.findall()
函数
re.findall()
函数是 Python 的 re
模块中用于在字符串中查找所有与正则表达式模式匹配的 非重叠 子字符串,并将它们以 列表 的形式返回。
语法
re.findall(pattern, string, flags=0)
举例:
import re
text = "The rain in Spain falls mainly on the plain."
print(f"原始文本: {text}")
print("-" * 40)
# 示例 1: 查找所有 "ain"
pattern1 = r"ain"
matches1 = re.findall(pattern1, text)
print(f"使用模式 '{pattern1}': {matches1}")
# 返回所有匹配到的 "ain" 子字符串
print("-" * 40)
# 示例 2: 查找所有单词
pattern2 = r"\b\w+\b" # \b 是单词边界,\w+ 匹配一个或多个单词字符
matches2 = re.findall(pattern2, text)
print(f"使用模式 '{pattern2}': {matches2}")
# 返回所有独立的单词
print("-" * 40)
# 示例 3: 使用捕获组查找所有以 "a" 开头的单词
pattern3 = r"\b(a\w*)\b" # 括号创建了一个捕获组
matches3 = re.findall(pattern3, text)
print(f"使用模式 '{pattern3}' (带一个捕获组): {matches3}")
# 返回所有以 "a" 开头的单词,但只返回捕获组中的内容 (即整个单词)
print("-" * 40)
# 示例 4: 查找所有数字
text_numbers = "123 abc 45 def 6789 ghi"
pattern4 = r"\d+" # \d+ 匹配一个或多个数字
matches4 = re.findall(pattern4, text_numbers)
print(f"文本: '{text_numbers}', 使用模式 '{pattern4}': {matches4}")
# 返回所有连续的数字
print("-" * 40)
# 示例 5: 使用多个捕获组查找日期和月份
text_dates = "Today is 2023-10-26, and tomorrow will be 2023-10-27."
pattern5 = r"(\d{4})-(\d{2})-(\d{2})" # 三个捕获组分别匹配年、月、日
matches5 = re.findall(pattern5, text_dates)
print(f"文本: '{text_dates}', 使用模式 '{pattern5}' (带多个捕获组): {matches5}")
# 返回一个列表,其中每个元素是一个元组,包含每个捕获组匹配到的内容
print("-" * 40)
# 示例 6: 查找所有以 "t" 开头,后跟一个字母的组合
pattern6 = r"t(\w)"
matches6 = re.findall(pattern6, text)
print(f"使用模式 '{pattern6}' (查找 't' 后跟一个字母): {matches6}")
# 返回一个列表,只包含捕获组中的内容,即 "t" 后面的那个字母
运行结果:
原始文本: The rain in Spain falls mainly on the plain.
----------------------------------------
使用模式 'ain': ['ain', 'ain', 'ain']
----------------------------------------
使用模式 '\b\w+\b': ['The', 'rain', 'in', 'Spain', 'falls', 'mainly', 'on', 'the', 'plain']
----------------------------------------
使用模式 '\b(a\w*)\b' (带一个捕获组): ['ain', 'ain', 'a']
----------------------------------------
文本: '123 abc 45 def 6789 ghi', 使用模式 '\d+': ['123', '45', '6789']
----------------------------------------
文本: 'Today is 2023-10-26, and tomorrow will be 2023-10-27.', 使用模式 '(\d{4})-(\d{2})-(\d{2})' (带多个捕获组): [('2023', '10', '26'), ('2023', '10', '27')]
----------------------------------------
使用模式 't(\w)' (查找 't' 后跟一个字母): ['h', 'e', 'o']
re.finditer()
函数
和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
re.finditer(pattern, string, flags=0)
当处理大量匹配项或者需要获取每个匹配项的详细信息(例如位置、捕获组)时,re.finditer()
比 re.findall()
更为高效和灵活,因为它不会一次性将所有匹配项加载到内存中。
举例
import re
text = "The quick brown fox jumps over the lazy dog. The fox is brown."
print(f"原始文本: {text}")
print("-" * 60)
# 示例 1: 查找所有 "fox" 并打印其位置
pattern1 = r"fox"
matches1 = re.finditer(pattern1, text)
print(f"使用模式 '{pattern1}' 查找所有匹配:")
for match in matches1:
print(f" - 找到 '{match.group()}' 在位置 {match.start()}:{match.end()}")
print("-" * 60)
# 示例 2: 查找所有以 "th" 开头的单词,并打印完整单词和 "th" 后面的部分
pattern2 = r"\b(th)(\w+)\b" # 使用捕获组
matches2 = re.finditer(pattern2, text)
print(f"使用模式 '{pattern2}' 查找以 'th' 开头的单词:")
for match in matches2:
full_word = match.group(0) # 完整匹配的字符串
prefix = match.group(1) # 第一个捕获组 ('th')
suffix = match.group(2) # 第二个捕获组 ('e', 'is')
print(f" - 完整单词: '{full_word}', 前缀: '{prefix}', 后缀: '{suffix}',位置: {match.start()}:{match.end()}")
print("-" * 60)
# 示例 3: 查找所有数字并进行简单的处理
text_numbers = "123 abc 45 def 6789 ghi"
pattern3 = r"\d+"
matches3 = re.finditer(pattern3, text_numbers)
print(f"文本: '{text_numbers}', 使用模式 '{pattern3}' 查找所有数字:")
for match in matches3:
number_str = match.group()
number_int = int(number_str)
print(f" - 找到数字 '{number_str}' (整数形式: {number_int}) 在位置 {match.start()}:{match.end()}")
print("-" * 60)
# 示例 4: 处理日志数据,提取日期和错误信息
log_data = """
[ERROR] 2023-10-26 10:00:00 - File not found
[INFO] 2023-10-26 10:00:05 - User logged in
[ERROR] 2023-10-26 10:00:10 - Connection timeout
"""
pattern4 = r"\[(ERROR|INFO)\]\s+(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})\s+-\s+(.*)"
matches4 = re.finditer(pattern4, log_data)
print("处理日志数据:")
for match in matches4:
log_level = match.group(1)
date = match.group(2)
time = match.group(3)
message = match.group(4)
print(f" - Level: {log_level}, Date: {date}, Time: {time}, Message: {message}")
运行结果:
原始文本: The quick brown fox jumps over the lazy dog. The fox is brown.
------------------------------------------------------------
使用模式 'fox' 查找所有匹配:
- 找到 'fox' 在位置 16:19
- 找到 'fox' 在位置 44:47
------------------------------------------------------------
使用模式 '\b(th)(\w+)\b' 查找以 'th' 开头的单词:
- 完整单词: 'The', 前缀: 'th', 后缀: 'e',位置: 0:3
- 完整单词: 'the', 前缀: 'th', 后缀: 'e',位置: 39:42
------------------------------------------------------------
文本: '123 abc 45 def 6789 ghi', 使用模式 '\d+' 查找所有数字:
- 找到数字 '123' (整数形式: 123) 在位置 0:3
- 找到数字 '45' (整数形式: 45) 在位置 8:10
- 找到数字 '6789' (整数形式: 6789) 在位置 15:19
------------------------------------------------------------
处理日志数据:
- Level: ERROR, Date: 2023-10-26, Time: 10:00:00, Message: File not found
- Level: INFO, Date: 2023-10-26, Time: 10:00:05, Message: User logged in
- Level: ERROR, Date: 2023-10-26, Time: 10:00:10, Message: Connection timeout
注意:re.finditer()
函数返回一个迭代器,在这里我不会赘述迭代器的使用方法。或许你只需要知道:for
循环可以遍历他。
re.split()
函数
split
方法按照能够匹配的子串将字符串分割后返回列表,它的使用形式如下:
re.split(pattern, string[, maxsplit=0, flags=0])
maxsplit
参数可以限制分割的次数。
举例
import re
text = "apple,banana;orange|grape"
print(f"原始文本: {text}")
print("-" * 40)
# 示例 1: 使用单个分隔符分割字符串 (逗号)
pattern1 = r","
result1 = re.split(pattern1, text)
print(f"使用模式 '{pattern1}' 分割: {result1}")
print("-" * 40)
# 示例 2: 使用多个分隔符分割字符串 (逗号, 分号, 竖线)
pattern2 = r"[,;|]"
result2 = re.split(pattern2, text)
print(f"使用模式 '{pattern2}' 分割: {result2}")
print("-" * 40)
# 示例 3: 使用空格分割字符串
text3 = "This is a test string with spaces."
pattern3 = r"\s+" # 匹配一个或多个空白字符
result3 = re.split(pattern3, text3)
print(f"文本: '{text3}', 使用模式 '{pattern3}' 分割: {result3}")
print("-" * 40)
# 示例 4: 使用捕获组保留分隔符 (逗号)
text4 = "apple,banana,orange"
pattern4 = r"(,)" # 捕获逗号
result4 = re.split(pattern4, text4)
print(f"文本: '{text4}', 使用模式 '{pattern4}' 分割 (保留分隔符): {result4}")
print("-" * 40)
# 示例 5: 使用捕获组保留多个分隔符 (逗号, 分号, 竖线)
pattern5 = r"([,;|])" # 捕获逗号、分号或竖线
result5 = re.split(pattern5, text)
print(f"使用模式 '{pattern5}' 分割 (保留多个分隔符): {result5}")
print("-" * 40)
# 示例 6: 指定最大分割次数
text6 = "apple,banana,orange,grape"
pattern6 = r","
result6 = re.split(pattern6, text6, maxsplit=2)
print(f"文本: '{text6}', 使用模式 '{pattern6}' 分割 (maxsplit=2): {result6}")
print("-" * 40)
# 示例 7: 空模式
text7 = "abc"
pattern7 = r""
result7 = re.split(pattern7, text7)
print(f"文本: '{text7}', 使用空模式 '{pattern7}' 分割: {result7}")
# 注意:空模式会分割字符串中的每个字符
print("-" * 40)
# 示例 8: 以数字作为分隔符
text8 = "part1 123 part2 456 part3"
pattern8 = r"\d+"
result8 = re.split(pattern8, text8)
print(f"文本: '{text8}', 使用模式 '{pattern8}' 分割: {result8}")
re.sub()
函数
语法
re.sub(pattern, repl, string, count=0, flags=0)
参数说明:
pattern
: 正则表达式模式字符串。repl
: 可以是一个字符串,也可以是一个函数。- 如果是一个字符串,则用它来替换所有匹配到的子字符串。你可以在这个字符串中使用反向引用(例如 \1, \2)来引用模式中捕获组的内容。
- 如果是一个函数,则对每个匹配到的子字符串都会调用这个函数。函数接收一个匹配对象作为参数,并且必须返回一个字符串作为替换内容。
string
: 要进行替换操作的原始字符串。count
: 可选参数,指定最多替换的次数。默认值为 0,表示替换所有匹配项。flags
: 可选参数,用于修改正则表达式的行为(例如忽略大小写)。
举例
import re
text = "The quick brown fox jumps over the lazy dog."
print(f"原始文本: {text}")
print("-" * 50)
# 示例 1: 简单替换
pattern1 = r"fox"
replacement1 = "cat"
new_text1 = re.sub(pattern1, replacement1, text)
print(f"替换 '{pattern1}' 为 '{replacement1}': {new_text1}")
print("-" * 50)
# 示例 2: 使用捕获组进行替换
text2 = "User: John, Age: 30"
pattern2 = r"User: (\w+), Age: (\d+)"
replacement2 = r"Name: \1, Years: \2"
new_text2 = re.sub(pattern2, replacement2, text2)
print(f"使用捕获组替换 '{pattern2}' 为 '{replacement2}': {new_text2}")
print("-" * 50)
# 示例 3: 使用函数进行替换
def replace_with_upper(match):
return match.group(0).upper()
text3 = "hello world"
pattern3 = r"\b\w+\b" # 匹配所有单词
new_text3 = re.sub(pattern3, replace_with_upper, text3)
print(f"使用函数替换 '{pattern3}': {new_text3}")
print("-" * 50)
# 示例 4: 使用 count 参数限制替换次数
text4 = "apple banana apple orange apple"
pattern4 = r"apple"
replacement4 = "pear"
new_text4 = re.sub(pattern4, replacement4, text4, count=2)
print(f"限制替换次数 (count=2): {new_text4}")
print("-" * 50)
# 示例 5: 使用 flags 参数 (忽略大小写)
text5 = "Python is fun, python is powerful."
pattern5 = r"python"
replacement5 = "Java"
new_text5_case_sensitive = re.sub(pattern5, replacement5, text5)
print(f"区分大小写替换: {new_text5_case_sensitive}")
new_text5_ignore_case = re.sub(pattern5, replacement5, text5, flags=re.IGNORECASE)
print(f"忽略大小写替换 (flags=re.IGNORECASE): {new_text5_ignore_case}")
print("-" * 50)
# 示例 6: 更复杂的替换函数
def censor_word(match):
word = match.group(0)
if len(word) > 4:
return "*" * len(word)
return word
text6 = "This is a long word and a short one."
pattern6 = r"\b\w+\b"
new_text6 = re.sub(pattern6, censor_word, text6)
print(f"使用复杂替换函数: {new_text6}")
运行结果:
原始文本: The quick brown fox jumps over the lazy dog.
--------------------------------------------------
替换 'fox' 为 'cat': The quick brown cat jumps over the lazy dog.
--------------------------------------------------
使用捕获组替换 'User: (\w+), Age: (\d+)' 为 'Name: \1, Years: \2': Name: John, Years: 30
--------------------------------------------------
使用函数替换 '\b\w+\b': HELLO WORLD
--------------------------------------------------
限制替换次数 (count=2): pear banana pear orange apple
--------------------------------------------------
区分大小写替换: Java is fun, python is powerful.
忽略大小写替换 (flags=re.IGNORECASE): Java is fun, Java is powerful.
--------------------------------------------------
使用复杂替换函数: This is a ***** word and a ***** one.
正则表达式修饰符 - 可选标志
正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|
) 它们来指定。如 re.I | re.M
被设置成 I 和 M 标志:
修饰符 | 描述 |
---|---|
re.I 或re.IGNORECASE |
使匹配对大小写不敏感 |
re.M 或re.MULTILINE |
多行匹配,影响 ^ 和 $ |
re.S 或re.DOTALL |
使 . 匹配包括换行在内的所有字符 |
re.X 或re.VERBOSE |
该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 |
re.A 或re.ASCII |
此修饰符使 \w, \W, \b, \B, \d, \D, \s 和 \S 等特殊序列执行仅限 ASCII 的匹配,而不是完整的 Unicode 匹配。 |
re.L |
做本地化识别(locale-aware)匹配 |
re.U |
根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |