【 ②】Python基础(正则表达式)

简介: 【 ②】Python基础(正则表达式)

1 什么是正则表达式

正则表达式(Regular Expression,在代码中常简写为regex、regexp或RE)是一种模式化的字符串,用于搜索、替换、分割和匹配文本数据。其基本思想是使用一些特殊的字符表示一个给定的模式,然后在文本中匹配这个模式。

正则表达式的作用:

  1. 匹配:判断给定的字符串是否符合正则表达式的过滤逻辑;
  2. 获取子串:可以通过正则表达式,从字符串中获取我们想要的特定部分。

正则表达式的特点:

  1. 非常强的灵活性、逻辑性和功能性
  2. 可以迅速地用极简单的方式达到字符串的复杂控制
  3. 对于刚接触的人来说,比较晦涩难懂。

2 正则表达式在Python中的使用

在Python中,主要使用re模块和regex模块来实现正则表达式操作。下面分别进行详细的讲解。

2.1 re模块

Python的re模块(正则表达式)是一种强大和灵活的工具,用来进行对字符串的模式匹配、替换和分割操作。re模块可以用于处理各种字符数据,包括文本文件、日志文件、编程语言代码等。re模块中包含了大量的正则表达式函数,包括搜索、替换、分割、匹配、复制、提取等,可以帮助用户完成高效的文本处理任务。

:Python自带了re模块,不需要额外安装。因此,使用re模块也非常方便,而无需像安装其他第三方库一样在终端中运行pip install命令。

2.1.1 re.search()

re.search()是Python re模块中常用的一个函数,用于在一个字符串中搜索指定的正则表达式模式。在搜索时,该函数会将整个字符串扫描一遍,直到找到第一个与模式匹配的字符串子串,然后返回一个匹配对象(Match Object)。如果没有找到相应的匹配子串,函数会返回None。

re.search()函数的常见用法如下:

match_object = re.search(pattern, string, flags=0)

其中,pattern参数表示要匹配的正则表达式,string参数表示要进行匹配的字符串,flags参数表示匹配选项(如是否忽略大小写等)。当函数返回一个匹配对象时,可以调用match_object.group()方法来获取匹配的子串,而该方法的参数表示要获取的子串的序号(如果正则表达式中有多个括号,每个括号表示一个组,其序号从左向右依次增加)。

例如,下面是一个示例代码,用于在一个字符串中搜索一个正则表达式:

import re
text = "Python is a popular programming language"
pattern = "programming"
match_object = re.search(pattern, text)
if match_object:
    print("Found a match:", match_object.group())
else:
    print("No match found.")

输出结果:

Found a match: programming

2.1.2 re.match()

在Python中,re.match()模块和re.search()模块类似,用于在字符串中搜索正则表达式的匹配项。但是,re.match()只会在字符串的开头进行匹配,如果在开头无法找到匹配项,它会返回一个None对象。因此,re.match()更适用于需要在字符串开头进行匹配的场景。

match的函数用法与search一样,下面来看一下同样的测试字符串,match返回的结果:

text = "Python is a popular programming language"
pattern = "programming"
match_object = re.match(pattern, text)
if match_object:
    print("Found a match:", match_object.group())
else:
    print("No match found.")

输出:

No match found.

由于programming并不是第一个单词,所以无法匹配。

2.1.3 re.findall()

re.findall()是Python中re模块提供的另一种匹配模式的函数,它可以搜索字符串中所有匹配正则表达式的模式,并返回一个列表,列表中的每个元素都是与正则表达式匹配的子字符串

re.search()re.match()不同,re.findall()返回所有的匹配项,而不仅仅是第一个或最后一个匹配项。因此,如果您需要查找一个文本中所有匹配某个正则表达式的模式的情况,re.findall()是非常有用的函数。

以下是该函数的示例代码:

import re
# 定义一个正则表达式,匹配以数字开头的子字符串
pattern = r'\d+'
# 定义一个待匹配的字符串
text = "Today is Oct 15, 2021, and the temperature is 20 degrees Celsius."
# 使用re.findall()函数查找所有匹配项,并将它们存储在一个list对象中
matches = re.findall(pattern, text)
# 输出匹配结果
print(matches)

在这个示例中,我们首先定义一个正则表达式模式,r'\d+',用于匹配以数字开头的子字符串。然后,我们定义了一个待匹配的字符串,text。接下来,我们使用re.findall()函数查找所有匹配项,并将它们存储在一个list对象中,matches。最后,我们将匹配结果输出到屏幕上。

输出结果:

['15', '2021', '20']

这是因为,在示例中的文本中,有三个以数字开头的子字符串,分别是15、2021和20度。re.findall()函数找到它们,并将它们存储在一个list对象中。

2.1.4 re.sub()

re.sub()是Python re模块中提供的另一种匹配模式的函数,用于在字符串中对以某种模式匹配的子字符串进行替换。re.sub()函数返回一个新的字符串,其中所有匹配指定模式的子字符串都被替换为指定的内容。

下面是一个简单的代码示例,说明如何使用re.sub()进行字符串替换:

import re
# 定义一个正则表达式,匹配所有'is'字符
pattern = 'is'
# 定义一个待匹配的字符串
text = "The pattern of the book is not easy to find."
# 使用re.sub()函数将匹配项替换为指定字符串
new_text = re.sub(pattern, "was", text)
# 输出结果
print(new_text)

在这个示例中,我们首先定义了一个正则表达式模式,'is',用于匹配所有的is字符。然后,我们定义了一个待匹配的字符串,text。接下来,我们使用re.sub()函数将所有匹配项替换为"was"。最后,我们输出被替换后的新字符串。

输出:

The pattern of the book was not easy to find.

2.2 regex模块

除了标准库中的re模块,还有一些第三方正则表达式模块,例如regex模块,它提供了比re模块更加全面、高级和兼容Perl正则表达式语法的功能。

regex模块与re模块类似,提供了re模块中的大多数函数,但它支持更多的正则表达式语法和功能,例如复杂的断言、Unicode属性和匹配嵌套结构等。此外,regex模块的性能也比re模块更好,可以处理更大的正则表达式和更长的文本数据。

总之,Python中的正则表达式模块有很多,其中标准库中的re模块是最常用的。如果需要在处理正则表达式时需要更复杂的语法和功能,可以尝试使用regex模块。


3 正则表达式的分类

正则表达式由一些普通字符和一些元字符(metacharacters)组成。普通字符包括大小写的字母和数字,而元字符则具有特殊的含义,也就是我们要用来做匹配的标记。

3.1 简单的元字符

元字符 作用
\ 将下一个字符标记符、或一个向后引用、或一个八进制转义符。
^ 匹配输入字行首。
$ 匹配输入行尾。
(星号)* 匹配前面的子表达式任意次。
(加号)+ 匹配前面的子表达式一次或多次(大于等于1次)。
? 匹配前面的子表达式零次或一次。

示例代码:

import re
# 匹配开头字符
res1 = re.match('^a', 'abandon')
print(res1)     # <re.Match object; span=(0, 1), match='a'>
print(res1.group()) # a
# 匹配结尾字符
res2 = re.match('.*d$', 'wood')
print(res2)     # <re.Match object; span=(0, 4), match='wood'>
print(res2.group()) # wood
# 匹配至少出现一次的字符
res3 = re.match('a+', 'aabcd')
print(res3)     # <re.Match object; span=(0, 2), match='aa'>
print(res3.group()) # aa
# 匹配一次或零次的字符
res4 = re.match('a?', 'aaabandon')
print(res4)     # <re.Match object; span=(0, 1), match='a'>
print(res4.group()) # a

3.2 用于单字符匹配的元字符

元字符 作用
. (点) 匹配除“\n”和"\r"之外的任何单个字符。
\d 匹配一个数字字符。
\D 匹配一个非数字字符。
\f 匹配一个换页符。
\n 匹配一个换行符。
\r 匹配一个回车符。
\s 匹配任何不可见字符,包括空格、制表符、换页符等等。
\S 匹配任何可见字符。
\t 匹配一个制表符。
\w 匹配包括下划线的任何单词字符。
\W 匹配任何非单词字符。

:在元字符后面加一个加号(+)表示匹配一个或多个该类型字符

代码示例一(.):

# 指定要匹配的模式
pattern = "py."
# 测试字符串1
test_str1 = "python"
result1 = re.match(pattern, test_str1)
print(result1)  # 输出 <re.Match object; span=(0, 3), match='pyt'>

代码示例二(\d、\D):

# 指定要匹配的模式
pattern = "\d"
# 测试字符串2
test_str2 = "The price is 199.99 dollars"
result2 = re.findall(pattern, test_str2)
print(result2)  # 输出['1', '9', '9', '9', '9']
# 指定要匹配的模式
pattern = "\D"
# 测试字符串3
test_str3 = "My phone number is 555-1234"
result3 = re.findall(pattern, test_str3)
print(result3)  # 输出 ['M', 'y', ' ', 'p', 'h', 'o', 'n', 'e', ' ', 'n', 'u', 'm', 'b', 'e', 'r', ' ', 'i', 's', ' ', '-']

代码示例四(\s、\S):

# 指定要匹配的模式
pattern1 = r"\s+"  # 匹配一个或多个空白字符
pattern2 = r"\S+"  # 匹配一个或多个非空白字符
# 测试字符串1
test_str1 = "Hello\tworld\n"
result1 = re.findall(pattern1, test_str1)
print(result1)  # 输出 ['\t', '\n']
result2 = re.findall(pattern2, test_str1)
print(result2)  # 输出 ['Hello', 'world']
# 测试字符串2
test_str2 = " This is a demo. "
result3 = re.findall(pattern1, test_str2)
print(result3)  # 输出 [' ', ' ', ' ']
result4 = re.findall(pattern2, test_str2)
print(result4)  # 输出 ['This', 'is', 'a', 'demo.']

代码示例四(\w、\W):

# 指定要匹配的模式
pattern1 = r"\w+"  # 匹配一个或多个单词字符
pattern2 = r"\W+"  # 匹配一个或多个非单词字符
# 测试字符串1
test_str1 = "Hello, world!"
result1 = re.findall(pattern1, test_str1)
print(result1)  # 输出 ['Hello', 'world']
result2 = re.findall(pattern2, test_str1)
print(result2)  # 输出 [', ', '!']
# 测试字符串2
test_str2 = "This is a demo."
result3 = re.findall(pattern1, test_str2)
print(result3)  # 输出 ['This', 'is', 'a', 'demo']
result4 = re.findall(pattern2, test_str2)
print(result4)  # 输出 [' ', ' ', ' ', '.']

3.3 用于字符集匹配的元字符

元字符 作用
[xyz] 字符集合。匹配所包含的任意一个字符。

代码示例

import re
# 指定要匹配的模式
pattern1 = r"[aeiou]"  # 匹配任何元音字母
pattern2 = r"[A-Z]"   # 匹配任何大写字母
# 测试字符串1
test_str1 = "Hello, world!"
result1 = re.findall(pattern1, test_str1)
print(result1)  # 输出 ['e', 'o', 'o']
# 测试字符串2
test_str2 = "This is a Demo."
result2 = re.findall(pattern2, test_str2)
print(result2)  # 输出 ['T', 'D']

3.4 用于量词匹配的元字符

元字符 作用
{n} n是一个非负整数。匹配确定的n次。
{n,} n是一个非负整数。至少匹配n次。
{n,m} m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。

代码示例

import re
# 指定要匹配的模式
pattern1 = r"[a-z]{3}"  # 匹配任何由三个小写字母组成的连续子串
pattern2 = r"\d{2,3}"   # 匹配任何由两个或三个数字组成的连续子串
# 测试字符串1
test_str1 = "apple banana cherry"
result1 = re.match(pattern1, test_str1)
print(result1)  # 输出 <re.Match object; span=(0, 3), match='app'>
# 测试字符串2
test_str2 = "1234567890 12 123 1234"
result2 = re.findall(pattern2, test_str2)
print(result2)  # 输出 ['12', '123', '234']

3.5 用于分组匹配的元字符

元字符 作用
() 将( 和 ) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个元组中

示例代码

# 指定要匹配的模式
pattern1 = r"(\d{3})-(\d{4})-(\d{4})"  # 匹配格式为 3-4-4 的电话号码
pattern2 = r"<(\w+)>.*</\1>"   # 匹配任何形如 <tag>value</tag> 的 XML 节点
# 测试字符串1
test_str1 = "My phone number is 123-4567-8901."
result1 = re.search(pattern1, test_str1)
if result1:
    area_code, prefix, line_number = result1.groups()
    print("Area code: {}, Prefix: {}, Line number: {}".format(area_code, prefix, line_number))
else:
    print("No match.")
# 测试字符串2
test_str2 = "<title>This is a title</title>"
result2 = re.match(pattern2, test_str2)
if result2:
    tag = result2.group(1)
    print("Tag name: {}".format(tag))
else:
    print("No match.")

输出:

Area code: 123, Prefix: 4567, Line number: 8901
Tag name: title

在上面的示例中,我们定义了两个模式 (\d{3})-(\d{4})-(\d{4})(其中 () 表示将其包含的字符集合作为一个捕获组,可以在后续的处理中通过 groups() 方法获取该组的内容)和 <(\w+)>.*</\1>(其中 \1 表示引用第一个捕获组所匹配到的内容)。然后我们使用 re.search 函数搜索两个不同的测试字符串,看看它们是否匹配这些模式。第一个测试字符串中包含格式为 3-4-4 的电话号码,我们将其匹配到了,并进一步提取出了该号码的区号、前缀和线路号信息。而第二个测试字符串是一个 XML 节点,我们将其匹配到了,并进一步提取出了节点的名称。

大家可以根据需要修改这个示例,以实现更复杂的文本处理功能。

  • 最后,还有一个元字符" | ",用作将两个匹配条件进行逻辑“或”(or)运算

示例代码如下:

# 指定要匹配的模式
pattern = r"(cat|dog|bird)\d*"  # 匹配任何形如 cat\d* 或 dog\d* 或 bird\d* 的字符串
# 测试字符串
test_str = "I have a cat3 and a dog4 but no bird."
results = re.findall(pattern, test_str)
if results:
    print("Matching results: ", results)
else:
    print("No match.")

输出:

Matching results:  ['cat', 'dog', 'bird']

4 正则表达式的等价

正则难理解因为里面有一个等价的概念,这个概念大大增加了理解难度,让很多初学者看起来会懵,如果把等价都恢复成原始写法,自己书写正则就超级简单了,就像说话一样去写你的正则了

?,*,+,\d,\w 都是等价字符

  • ? 等价于 匹配长度{0,1}
  • *等价于 匹配长度{0,}
  • +等价于 匹配长度{1,}
  • \d 等价于 [0-9]
  • \D 等价于 [^0-9]
  • \w 等价于 [A-Za-z_0-9]
  • \W 等价于 [^A-Za-z_0-9]。

5 贪婪匹配

正则表达式的贪婪匹配和非贪婪匹配是指当正则表达式中有多个可能匹配文本时,它们所选择的匹配方式不同。

贪婪匹配指的是在匹配时尽可能多地匹配所有符合条件的字符串,即优先匹配更长的文本。比如 a.*b 表示以 a 开始,以 b 结束,并且中间任意字符(包括空格)至少出现一次,正则表达式引擎在匹配符合该规则的字符串时会从最左边开始选取满足条件的尽可能多的字符。例如对于字符串 abbbcbbbd,该正则表达式的贪婪匹配结果为 abbbcbbb

相对应的,非贪婪匹配也称为惰性匹配或最小匹配,指的是在匹配时只匹配符合条件的最短的字符串默认情况下,正则表达式引擎采用贪婪匹配模式,在量词的后面添加 ? 前缀可以将其转化为非贪婪匹配模式。例如 a.*?b 表示以 a 开始,以 b 结束,并且中间任意字符(包括空格)至少出现一次,而加上 ? 后,表示在尽可能短的条件下满足这个规则。例如对于字符串 abbbcbbbd,该正则表达式的非贪婪匹配结果为 abb

  • 正则表达式的贪婪或非贪婪匹配取决于量词后是否有一个问号,应根据实际需求来决定。需要注意的是,虽然非贪婪通常更加安全和可靠,但也会导致性能损失,因为引擎需要不断回溯以查找符合条件的文本。

示例代码

# 原始字符串
str1 = "hello-world-and-hi"
# 贪婪匹配,获取第一个连字符到最后一个连字符之间的所有字符
result1_greedy = re.findall(r"-.*-", str1)
print(result1_greedy)    # 输出 ['-world-and-']
# 非贪婪匹配,只获取第一个连字符到第二个连字符之间的所有字符
result1_non_greedy = re.findall(r"-\w+?-", str1) 
print(result1_non_greedy)   # 输出 ['-world-']
# 原始字符串
s = "hello world, this is a test string."
# 贪婪匹配,获取以 h 开头、以空格结尾的所有字符
result_greedy = re.findall(r"h.* ", s)
print(result_greedy)  # ['hello world, this is a test ']
# 非贪婪匹配,获取以 h 开头、以空格结尾的最短字符
result_non_greedy = re.findall(r"h.*? ", s)
print(result_non_greedy)  # ['hello ', 'his ']
相关文章
|
4月前
|
Python
"揭秘!Python如何运用神秘的正则表达式,轻松穿梭于网页迷宫,一键抓取隐藏链接?"
【8月更文挑战第21天】Python凭借其强大的编程能力,在数据抓取和网页解析领域表现出高效与灵活。通过结合requests库进行网页请求及正则表达式进行复杂文本模式匹配,可轻松提取网页信息。本示例展示如何使用Python和正则表达式解析网页链接。首先确保已安装requests库,可通过`pip install requests`安装。接着,利用requests获取网页内容,并使用正则表达式提取所有`&lt;a&gt;`标签的`href`属性。
51 0
|
1月前
|
Python
在Python中,可以使用内置的`re`模块来处理正则表达式
在Python中,可以使用内置的`re`模块来处理正则表达式
41 5
|
1月前
|
数据采集 Web App开发 iOS开发
如何使用 Python 语言的正则表达式进行网页数据的爬取?
使用 Python 进行网页数据爬取的步骤包括:1. 安装必要库(requests、re、bs4);2. 发送 HTTP 请求获取网页内容;3. 使用正则表达式提取数据;4. 数据清洗和处理;5. 循环遍历多个页面。通过这些步骤,可以高效地从网页中提取所需信息。
|
2月前
|
Python
【收藏备用】Python正则表达式的7个实用技巧
【收藏备用】Python正则表达式的7个实用技巧
26 1
|
2月前
|
数据安全/隐私保护 Python
Python实用正则表达式归纳
Python实用正则表达式归纳
20 3
|
2月前
|
Python
Python 正则表达式高级应用指南
正则表达式是文本模式匹配的强大工具,Python 的 `re` 模块支持其操作。本文介绍正则表达式的高级应用,包括复杂模式匹配(如邮箱、电话号码)、分组与提取、替换操作、多行匹配以及贪婪与非贪婪模式的区别。通过示例代码展示了如何灵活运用这些技巧解决实际问题。
31 7
|
2月前
|
JavaScript 前端开发 Scala
Python学习十:正则表达式
这篇文章是关于Python中正则表达式的使用,包括re模块的函数、特殊字符、匹配模式以及贪婪与非贪婪模式的详细介绍。
19 0
|
2月前
|
数据采集 开发者 Python
Python正则表达式之re.compile函数
`re.compile`是Python正则表达式处理中一个强大的工具,它通过预先编译正则表达式,不仅提升了执行效率,还增强了代码的组织性和可读性。掌握其使用,对于涉及文本分析、数据清洗、日志处理等领域的Python开发者来说,是非常必要的技能。正确并高效地应用这一功能,可以显著提升程序的性能和维护性。
123 0
|
3月前
|
索引 Python
30天拿下Python之正则表达式
30天拿下Python之正则表达式
19 0
|
3月前
|
数据采集 Python
Python正则表达式提取车牌号
Python正则表达式提取车牌号
47 0