Python学习之正则表达式

前面我们学习了Python的基础知识,这时候,你已经在python的编程中有一些思考了,当你面对大量代码和逻辑编写的时候,你会想,还有没一些简单的技巧呢?那么,Python正则表达式的出现,就会解决你上面的问题。

一、什么是正则表达式?

正则表达式(Regular Expression,简称 regex 或 RE)是一种特殊文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”,例如星号、问号),可以用来描述和匹配字符串的特殊语法。

通过使用正则表达式,您可以轻松地实现诸如以下操作:

  • 搜索文本
  • 替换文本
  • 验证文本
  • 提取文本

二、正则表达式语法

正则表达式的语法由以下几部分组成:

  • 字面值字符:例如字母、数字、空格等,可以直接匹配它们自身。
  • 特殊字符:例如点号 .、星号 *、加号 +、问号 ? 等,具有特殊的匹配含义。
  • 限定符:例如 ?+{n} 等,用于指定字符或子表达式的匹配次数。
  • 分组:使用括号 () 将表达式分组,可以提高表达式的可读性和复杂度。 

三、元字符

常用的元字符及其含义如下:

元字符描述
. 匹配任意单个字符,除了换行符
^ 匹配输入字符串的开始位置
$ 匹配输入字符串的结束位置
* 匹配前面的表达式零次或多次
+ 匹配前面的表达式一次或多次
? 匹配前面的表达式零次或一次
` `
[] 匹配字符集中的任意一个字符
[^] 匹配不在字符集中的任意一个字符
() 用于对正则表达式的一部分进行分组
\ 用于转义下一个字符,或引用特殊字符
\b 匹配单词的边界
\B 匹配不是单词边界的位置
\s 匹配任意一个空白字符
\S 匹配任意一个非空白字符
\d 匹配任意一个数字字符
\D 匹配任意一个非数字字符
{} 用于指定前面的表达式匹配的次数

使用这些元字符,您可以创建几乎任何您想要的匹配规则,从简单的搜索到复杂的模式匹配和替换。那么,在实际应用中,就可以根据不同的情况选择合适的元字符。

四、匹配模式

正则表达式可以用来匹配各种文本模式,例如:

  • 匹配所有数字:[0-9]+
  • 匹配所有字母:[a-zA-Z]+
  • 匹配以 http 开头的字符串:^http.*
  • 匹配以 .com 结尾的字符串:.*\.com$
  • 匹配电子邮件地址:^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$

五、re模块

上面我们讲了正则表达式的基础语法、元字符和匹配模式。你看下来也是一头雾水,在想:有什么用,想知道怎么用?那么接下来,就开始讲怎么!

在python中,有专门为正则表达式提供了一个使用模块,叫做“re模块”。对就是正则表达式的英文缩写:正则表达式Regular Expression,简称 regex 或 RE)。

在 Python 中,re 模块提供了一组函数,允许你在字符串中进行模式匹配、搜索和替换操作。re 模块的出现,使得Python语言拥有了完整的正则表达式功能。

1. 引入re模块

要使用re模块,需要先将其导入到程序中:

import re

re模块提供了以下基本功能:

  • 编译正则表达式模式:使用re.compile()函数可以将正则表达式模式编译成一个正则表达式对象。
  • 匹配字符串:使用re.match()re.search()re.findall()等函数可以匹配字符串。
  • 替换字符串:使用re.sub()函数可以替换字符串中的匹配项。

下面具体介绍几个常用的re函数。

2. 常用函数介绍

🌾 re.compile()

re.compile()函数用于编译正则表达式模式,返回一个正则表达式对象。这样做可以提高匹配的效率,因为不需要每次都重新解析模式。

该函数可以接受以下参数:

  • pattern:正则表达式模式字符串。
  • flags:可选参数,用于指定编译模式时的一些标志。

示例代码:

import re
pattern = re.compile('\d+')  # 匹配一个或多个数字
🌾 re.match()

re.match()函数用于从字符串开头匹配正则表达式模式,如果匹配成功,则返回一个匹配对象,否则返回None。

该函数可以接受以下参数:

  • string:要匹配的字符串。
  • pattern:正则表达式模式字符串或正则表达式对象。

例如

使用re.match()函数匹配字符串"1234d"

import re

pattern = re.compile('\d+')  # 匹配一个或多个数字

mystring = '1234d!'
result = re.match(pattern, mystring)

if result:
    print("匹配成功")
else:
    print("匹配失败")

输出结果:

匹配成功
🌾 re.search()

re.search()函数用于在整个字符串中搜索正则表达式模式。如果匹配成功,则返回一个匹配对象,否则返回None。

该函数可以接受以下参数:

  • string:要搜索的字符串。
  • pattern:正则表达式模式字符串或正则表达式对象。

例如

使用re.search()函数在字符串"TiYong"中搜索模式`r'\d+':

import re

pattern = re.compile('\d+')  # 匹配一个或多个数字

mystring = 'TiYong'
result = re.search(pattern, mystring)

if result:
    print("匹配成功")
else:
    print("匹配失败")

输出结果:

匹配失败
🌾 re.findall()

re.findall()函数用于查找所有匹配正则表达式模式的字符串。返回一个列表,其中包含所有匹配的字符串。

该函数可以接受以下参数:

  • string:要搜索的字符串。
  • pattern:正则表达式模式字符串或正则表达式对象。

例如

使用re.findall()函数查找字符串"34TiYong12"中所有数字:

import re

pattern = re.compile('\d+')  # 匹配一个或多个数字

mystring = '34TiYong12'
results = re.findall(pattern, mystring)

print(results)

输入:

['34', '12']
🌾 re.split()

re.split()函数用于根据正则表达式模式分割字符串。返回一个列表,其中包含分割后的字符串。

该函数可以接受以下参数:

  • string:要分割的字符串。
  • pattern:正则表达式模式字符串或正则表达式对象。

例如

使用re.split()函数分割字符串"TiYong"

import re

pattern = re.compile("")  # 匹配一个或多个字符

mystring = "TiYong"
results = re.split(pattern, mystring)

print(results)

输出结果:

['', 'T', 'i', 'Y', 'o', 'n', 'g', '']
🌾 re.sub()

re.sub()函数用于替换字符串中的匹配项,返回一个替换后的字符串。该函数可以接受以下参数:

  • pattern:正则表达式模式字符串或正则表达式对象。
  • repl:替换字符串。
  • string:要替换的字符串。

使用re.sub()函数将字符串"TiYong123"中的所有数字替换为" welcome you":

import re

pattern = re.compile('\d+')  # 匹配一个或多个数字

mystring = "TiYong123"
repl = " welcome you"
results = re.sub(pattern, repl, mystring)

print(results)

输出结果:

TiYong welcome you 

3. 正则表达式高级用法

🌾 分组

分组可以将正则表达式分成多个部分,提高表达式的可读性和复杂度。分组可以使用括号 () 来实现。

例如,以下表达式可以匹配一个日期,其中日期格式为 YYYY-MM-DD

^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})$

这个表达式中有三个分组:

  • ([0-9]{4}):匹配年份
  • ([0-9]{1,2}):匹配月份
  • ([0-9]{1,2}):匹配日期

分组还可以用于捕获匹配到的文本。例如,以下表达式可以匹配一个电子邮件地址,并捕获用户的姓名和邮箱地址:

^(.+) <(.+)>$

这个表达式中有两个分组:

  • (.+):匹配用户姓名
  • <(.+)>:匹配邮箱地址

捕获到的文本可以通过 \n 来引用,其中 n 是分组的编号。

例如,以下代码可以输出用户姓名和邮箱地址:

import re

text = "Ti Yong <TiYong@example.com>"

match = re.match(r'^(.+) <(.+)>$', text)

print("姓名:", match.group(1))
print("邮箱地址:", match.group(2))

输出如下:

姓名: Ti Yong
邮箱地址: TiYong@example.com
🌾 贪婪模式与非贪婪模式

贪婪模式是正则表达式的默认模式。它会尽可能匹配更长的字符串。换句话说,它会匹配尽可能多的字符,直到不满足条件为止。

例如,在这个表达式中,. 表示匹配任意字符,* 表示匹配前面的字符 0 次或多次,这里会使用贪婪模式:

import re

text = "This is a test string with some numbers 12345 and some more 6789"

pattern = ".*\d+"

result = re.match(pattern, text)

print(result.group())

输出结果会是整个字符串,因为 .* 会尽可能地匹配更多的字符,直到遇到数字 \d+

非贪婪模式可以让匹配操作尽可能少地匹配文本。在正则表达式中,可以使用 ? 来表示非贪婪模式。

例如,在上面的例子中,我们只想匹配第一个数字到第一个空格之间的部分。这时候可以使用非贪婪模式:

import re
text = "This is a test string with some numbers 12345 and some more 6789"
pattern = ".*?\d+"
result = re.match(pattern, text)
print(result.group())

这次输出结果会是 "This is a test string with some numbers 123",因为 .*? 会尽可能地匹配更少的字符,直到满足 \d+ 的条件。

🌾 回溯

回溯是指在尝试匹配的过程中,由于贪婪模式的存在,可能会进行多次尝试。这种情况在正则表达式引擎需要回退到之前的位置并尝试另一种匹配方式时发生。这可能会导致性能下降,尤其是在处理大量数据时。

例如,考虑这个例子:

import re
text = "aaaaaa"
pattern = "a*aa"
result = re.match(pattern, text)
print(result.group())

这里的 a*aa 在贪婪模式下会尝试匹配尽可能多的 a,然后回溯以满足 aa 的条件。结果会匹配整个字符串 "aaaaaa"。这时候可能会发生回溯,尝试不同的组合,直到找到一个匹配。

为了避免回溯,可以使用非贪婪模式:

import re
text = "aaaaaa"
pattern = "a*?aa"
result = re.match(pattern, text)
print(result.group())

输出:

 aa

这次的输出会是 "aa",因为 a*? 会尽可能地匹配更少的 a,满足 aa 的条件,避免了不必要的回溯。

六、正则表达式断言

正则表达式断言可以用于测试文本是否满足某个条件,但不会实际匹配任何文本。断言有两种类型:

  • 先行断言:测试文本是否在当前位置之前满足某个条件
  • 后行断言:测试文本是否在当前位置之后满足某个条件

常用的断言如下:

断言含义
(?=...) 仅当在当前位置的右侧匹配 ... 时才继续匹配
(?!...) 仅当在当前位置的右侧不匹配 ... 时才继续匹配
(?<=...) 仅当在当前位置的左侧匹配 ... 时才继续匹配
(?<!...) 仅当在当前位置的左侧不匹配 ... 时才继续匹配

例如,以下表达式可以匹配一个有效的电子邮件地址:

^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$

这个表达式可以确保电子邮件地址包含以下部分:

  • 用户名
  • @ 符号
  • 域名
  • 顶级域名

但是,这个表达式无法确保电子邮件地址中的用户名和域名是有效的。

为了解决这个问题,可以使用断言:

^[a-zA-Z0-9_.+-]+(?=@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$)@.*$

这个表达式中的 (?=@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$) 是一个先行断言,它确保在 @ 符号之前有一个有效的域名。

七、正则表达式修饰符

正则表达式修饰符可以改变正则表达式的匹配行为,常用的修饰符如下:

修饰符含义
i 忽略大小写
g 全局匹配
m 多行模式
s 点号匹配换行符
x 允许空格

例如,以下表达式可以匹配所有以 a 开头的单词:

^a.*$

这个表达式在默认情况下只会匹配第一个以 a 开头的单词。为了匹配所有以 a 开头的单词,可以使用 g 修饰符:

^a.*$g

这个表达式会匹配文本中的所有以 a 开头的单词:

>>> re.findall(r'^a.*$g', 'This is a sentence with a lot of words.')
['a', 'all', 'a', 'and']

八、正则表达式实战

🌾 提取文本中的所有电子邮件地址
import re

text = """
This is an email address: TiYong@example.com.
This is another email address: TiYong2@example.org.
"""

email_addresses = re.findall(r'[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+', text)

print(email_addresses)

输出:

['TiYong@example.com.', 'TiYong2@example.org.']
🌾 分析文本中的单词频率
import re

text = """
This is an email address: TiYong@example.com.
This is another email address: TiYong2@example.org.
"""

word_counts = {}
for word in re.findall(r'\w+', text):
  word_counts[word] = word_counts.get(word, 0) + 1

print(word_counts)

输出:

{'This': 2, 'is': 2, 'an': 1, 'email': 2, 'address': 2, 'TiYong': 1, 'example': 2, 'com': 1, 'another': 1, 'TiYong2': 1, 'org': 1} 

posted on 2024-12-04 19:30  梁飞宇  阅读(53)  评论(0)    收藏  举报