Python正则表达式---re模块

正则表达式(Regular Expression)是一种强大的文本处理工具,用于匹配、查找、替换或提取符合特定模式的字符串。在Python中,我们使用内置的re模块来实现正则表达式的功能:

目录

一、正则表达式的匹配过程

二、Python中使用正则表达式的基本步骤

1、导入re模块

2、编译正则模式

3、应用模式

三、正则表达式基础及进阶

1. 基本字符匹配

2. 特殊字符(元字符)

3. 量词

4.边界匹配与分组

5.选择、连接与断言

四、使用re模块修改字符串

五、使用标志(Flags)


一、正则表达式的匹配过程

正则表达式的匹配过程可以简化为以下几个关键步骤:

  1. 编译:将用户编写的正则模式字符串转换为内部可执行结构(如自动机)。
  2. 初始化:在目标字符串上设定初始匹配位置。
  3. 状态转移:遍历目标字符串的字符。根据当前内部状态和字符,按自动机规则移动到下一个状态。
  4. 处理量词:对于重复次数不确定的子表达式(如*、+、?、{m,n}),尝试不同次数的匹配。
  5. 捕获分组:记录括号内子表达式匹配到的子串。
  6. 断言:检查特定位置是否满足额外条件,不影响匹配结果但影响匹配过程。
  7. 判定结果:若能完整遍历目标字符串且到达接受状态,匹配成功。否则,匹配失败。
  8. 全局搜索(可选):寻找目标字符串中所有匹配项,重复上述过程。

简单来说,正则表达式匹配就是将模式字符串编译为内部结构,然后在目标字符串上按照特定规则逐字符移动并处理量词、捕获分组、断言等,直至找到符合模式的子串或判定为不匹配。对于全局搜索,会在目标字符串中重复此过程以查找所有匹配项。

二、Python中使用正则表达式的基本步骤

1、导入re模块

import re

2、编译正则模式

使用re.compile()函数将一个字符串形式的正则表达式编译成一个可重用的模式对象。这一步不是必需的,但有助于提高效率,尤其是在多次使用同一模式时。

pattern = re.compile(r'定义需要匹配的字符串')

3、应用模式

使用模式对象的方法来执行匹配操作,常见的方法有:

mach()从字符串起始位置尝试匹配
search()在整个字符串中搜索首次出现的匹配项
findall()返回所有非重叠的匹配项组成的列表
finditer()返回一个迭代器,生成所有非重叠匹配项的对象(每个对象包含匹配信息)
fullmach()只有整个字符串完全匹配时才返回成功
split()根据模式分割字符串
sub()替换字符串中所有(或指定次数)的匹配项

如需了解更多请参阅re文档:re --- 正则表达式操作 --- Python

匹配对象实例也有几个方法和属性,最重要的是:

方法 / 属性

目的

group()

返回正则匹配的字符串

start()

返回匹配的开始位置

end()

返回匹配的结束位置

span()

返回包含匹配 (start, end) 位置的元组

三、正则表达式基础及进阶

1. 基本字符匹配

  • 字母、数字、符号等直接匹配自身。

示例1:匹配字符串 "'hello,123456,text,text' 中的 "hello":

import re
str1='hello,123456,text,text'
ma=re.match('hello',str1)
print(ma)
#输出<re.Match object; span=(0, 5), match='hello'>

示例2:匹配字符串 "'hello,123456,text,text' 中的 "123456":

search1=re.search('123456',str1)
print(search1)
#输出<re.Match object; span=(6, 12), match='123456'>

2. 特殊字符(元字符)

有些字符是特殊的 元字符(metacharacters),并不匹配自身。事实上,它们表示匹配一些非常规的内容,或者通过重复它们或改变它们的含义来影响正则的其他部分

元字符的完整列表:.  ^  $  *  +  ?  { }  [ ]  \  |  ( )  在本文章中会分别讲到这些元字符的作用

  • .(点):匹配除换行符外的任何单个字符。
  • ^:匹配字符串的开始。
  • $:匹配字符串的结束。
  • \ :反斜杠用来转义,常见的如下:
    • \d 匹配任何十进制数字,等价于字符类 [0-9] 。
    • \D 匹配任何非数字字符,等价于字符类 [^0-9] 。
    • \s 匹配任何空白字符,等价于字符类 [ \t\n\r\f\v] 。
    • \S 匹配任何非空白字符,等价于字符类 [^ \t\n\r\f\v] 。
    • \w 匹配任何字母与数字字符,等价于字符类 [a-zA-Z0-9_] 。
    • \W 匹配任何非字母与数字字符,等价于字符类 [^a-zA-Z0-9_] 。

[ ] :  用于指定一个字符类,希望匹配的字符的一个集合。这些字符可以单独地列出,也可以用字符范围来表示(给出两个字符并用 '-' 分隔)。例如,[abc] 将匹配 abc 之中的任意一个字符;这与 [a-c] 相同,后者使用一个范围来表达相同的字符集合。如果只想匹配小写字母,则正则表达式将是 [a-z] ,只匹配大写字母则是[A-Z]。

元字符 (除了 \) 在字符类中是不起作用的。 例如,[ak47$] 将会匹配以下任一字符 'a''k''4','7'或 '$';而'$' 通常是一个元字符,但在一个字符类中它的特殊性被消除了。

我们也可以通过对集合 取反 来匹配字符类中未列出的字符。方法是把 '^' 放在字符类的最开头。 例如,[^5] 将匹配除 '5' 之外的任何字符。 如果插入符出现在字符类的其他位置,则它没有特殊含义。 例如:[5^] 将匹配 '5' 或 '^'

示例

import re
str2='m416,ak47$,我有5个5,abcde' #匹配[a-c]
search2=re.search('[a-c]',str2)
print(search2) #<re.Match object; span=(5, 6), match='a'>

findall1=re.findall('[ak47$]',str2) #匹配[ak47$]
print(findall1) #['a', 'k', '4', '7', '$']

findall2=re.findall('[^5]',str2) #匹配除了5的字符
print(findall2)
#['m', '4', '1', '6', ',', 'a', 'k', '4', '7', '$', ',', '我', '有', '个', ',', 'a', 'b', 'c', 'd', 'e']

str3='hello python!'
match1=re.match('^h.*!$',str3) #匹配h开头和!结尾的字符串
print(match1) #<re.Match object; span=(0, 13), match='hello python!'>

3. 量词

  • ?:前一个字符或子表达式可出现0次或1次(可选)。
  • *:前一个字符或子表达式可出现0次或多次(重复任意次)。(*贪婪  *?非贪婪)
  • +:前一个字符或子表达式至少出现1次(重复1次或多次)。
  • {m,n}:前一个字符或子表达式至少出现m次,至多出现n次。

示例

import re

test_string = 'banaanaaa'

# 匹配连续零个或一个'a'
pattern=re.compile('a?')
result = pattern.findall(test_string)
print(result) #['', 'a', '', 'a', 'a', '', 'a', 'a', 'a', ''] 'a'可以被空字符串、单个 'a'匹配

# 匹配连续零个或多个'a'
pattern1 = re.compile('a*')
result1 = pattern1.findall(test_string)
print(result1) #['', 'a', '', 'aa', '', 'aaa', ''] 'a'可以被空字符串、单个 'a'、两个 'a' 和三个 'a' 匹配

# 匹配至少一个'a'
pattern2 = re.compile('a+')
result2 = pattern2.findall(test_string)
print(result2) #['a', 'aa', 'aaa'] 'a'至少出现一次

# 匹配至少2次,至多3次'a'
pattern3 = re.compile('a{2,3}')
result3 = pattern3.findall(test_string)
print(result3) #['aa', 'aaa'] 'a'至少出现2次,至多出现3次

非贪婪模式和贪婪模式示例:

import re
html_text = "<div>This is some content</div><div>More content here</div>"
greedy_pattern = r"<div>(.*)</div>"  # 注意此处的 .*
greedy_matches = re.findall(greedy_pattern, html_text)
print(greedy_matches) #['This is some content</div><div>More content here']

no_greedy_pattern = r"<div>(.*?)</div>" # 注意此处的 .*?
no_greedy_matches = re.findall(no_greedy_pattern, html_text)
print(no_greedy_matches) #['This is some content', 'More content here']

在这个例子中,非贪婪量词 .*? 在贪婪模式下会尽可能少地匹配内容,因此会分别匹配到每个 <div> 标签内的内容。而如果使用贪婪量词 .*,则只会匹配到整个字符串中第一个 <div> 和最后一个 </div> 之间的所有内容。

4.边界匹配与分组

  • \b:匹配单词边界。
  • ():用于创建捕获组,捕获匹配的内容,后续可通过\数字引用。

示例

import re

#提取URL中的域名部分:
url = 'http://www.example.com/path/to/file.html'
pattern = re.compile(r'http://(.*?)/')
match = pattern.search(url)
print(match.group(1)) #获取第一个括号内的捕获组的内容,也就是我们想要提取的域名部分
#输出:www.example.com


#下面是一个使用\b和捕获组 的正则表达式示例,用于匹配电子邮件地址:
pattern=re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b')
test_string = 'Please contact me at my.email@example.com or another_email@sub.example.co.uk.'
emails = pattern.findall(test_string)
print(emails) #['my.email@example.com', 'another_email@sub.example.co.uk']

'''
在此正则表达式中:
\b 用于匹配单词边界,确保我们不会在单词内部匹配到邮箱地址。
([A-Za-z0-9._%+-]+) 是第一个捕获组,用于匹配用户名部分,其中可以包含字母、数字、点、下划线、百分号、加号和减号。
@ 是固定邮箱地址中的符号。
[A-Za-z0-9.-]+ 匹配域名部分的标签,允许字母、数字、点和短横线。
\. 匹配点号,它是域名的一部分。
[A-Z|a-z]{2,} 匹配顶级域名(TLD),至少由两个字母组成。
'''

5.选择、连接与断言

  • |:选择符,匹配左右任一表达式。
  • x y:连接两个表达式,匹配连续的 x 和 y。
  • (?=p):正向前瞻断言,要求 p 在当前位置之后但不计入匹配。
  • (?!p):负向前瞻断言,要求 p 不在当前位置之后。

示例:

#选择符 | (匹配左右任一表达式):
text = "I prefer Python or Java"
pattern = 'Python|Java' # "Python|Java" 这里 | 用来匹配 "Python" 或 "Java"。
matches = re.findall(pattern, text)
print(matches)  # ['Python', 'Java']

#连接两个表达式 x y(匹配连续的 x 和 y)
text = "The quick brown fox jumps over the lazy dog"
pattern = "quick brown"
matches = re.findall(pattern, text) # 匹配连续的 "quick" 和 "brown"
print(matches)  # ['quick brown']

#正向前瞻断言 (?=p)(要求 p 在当前位置之后但不计入匹配):
text = "123abcABCabc123"
pattern = r"\d+(?=abc)"  # 匹配数字,后面紧跟 "abc",但不包括 "abc"
matches = re.findall(pattern, text) #这里 (?=abc) 表示匹配的数字后面必须是 "abc",但匹配结果只包含数字。
print(matches)  # ['123', '123']

#负向前瞻断言 (?!p)(要求 p 不在当前位置之后):
text = "end123start456end"
pattern = r"\d+(?!end)"  # 匹配数字,后面不能紧跟 "end"
matches = re.findall(pattern, text) #这里 (?!end) 表示匹配的数字后面不能是 "end",所以只会匹配到 "start456" 中的 "456"
print(matches)  # ['456']

四、使用re模块修改字符串

  • re.sub(replacementstring[, count=0]):返回通过替换 replacement 替换 string 中正则的最左边非重叠出现而获得的字符串。 如果未找到模式,则 string 将保持不变。可选参数 count 是要替换的模式最大的出现次数;count 必须是非负整数。 默认值 0 表示替换所有。
  • re.split(string[, maxsplit=0]) :通过正则表达式的匹配拆分 字符串。 如果在正则中使用捕获括号,则它们的内容也将作为结果列表的一部分返回。 如果 maxsplit 非零,则最多执行 maxsplit 次拆分。

re.split示例:

import re
# 分割含有多种分隔符的字符串
text = "Apple, Banana; Cherry: Date"
# 使用 | 作为选择符,匹配逗号或分号或冒号
pattern = r"[,:;]"

# 使用正则表达式分割字符串
split_result = re.split(pattern, text)
print(split_result)  # 输出:['Apple', ' Banana', ' Cherry', ' Date']

# 若需去除空白字符,可以进一步处理
split_result = [part.strip() for part in split_result if part]
print(split_result)  # 输出:['Apple', 'Banana', 'Cherry', 'Date']

re.sub示例:

# 替换连续重复的字母为单个字母
text = "bananaaa cccat doggo eeeefish"
# 使用正则表达式匹配连续重复两次及以上的字母
pattern = r"(\w)\1+"

# 使用 lambda 函数将匹配到的重复字母替换为其本身的一个实例
replace= lambda m: m.group(1)

# 使用 re.sub() 方法执行替换操作
modified_text = re.sub(pattern, replace, text)
print(modified_text)  # 输出:banana cat dogo efish

五、使用标志(Flags)

通过在调用正则函数时添加标志参数(如 re.IGNORECASEre.MULTILINE 等),可以改变正则表达式的匹配行为。

示例:忽略大小写进行匹配:

import re
text = 'The Quick Brown Fox Jumps Over The Lazy Dog.'
pattern = re.compile(r'the', re.IGNORECASE)
matches = pattern.findall(text)
print(matches)  # 输出:['The', 'The']

希望该篇文章能帮助大家对Python正则表达式有初步的认识。实际使用时,可以根据具体需求组合运用这些元素,编写出复杂且精确的匹配规则。随着实践的深入,我们就会发现正则表达式在处理字符串数据时的强大之处。但是也不要纠着正则不放哟,总的来说,正则表达式在处理文本模式匹配、验证和提取等任务时具有显著优势,但在面对简单字符串操作、已有专用工具覆盖的场景、结构化数据处理以及复杂的文本分析时,可能并非最佳选择。在实际开发中,应根据具体需求权衡使用正则表达式与其他工具的利弊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值