我的Python学习日记(十):正则表达式

我的Python学习日记


DateLog
2023.12.09完成编写

10. 正则表达式

前段时间在码字的时候,遇到一种情况需要把一段字符串中的括号及其中的内容去掉,以这两章目录为例:

Test_text = """* [2. Python,启动!](#启动)
  * [2.1 标识符](#2.1)
  * [2.2 输入输出](#2.2)
  * [2.3 缩进](#2.3)
  * [2.4 多行输入](#2.4)
  * [2.5 注释](#2.5)
  * [2.6 import](#2.6)
  * [2.7 一些散碎关键字](#2.7)
* [3. 基本语句](#基本语句)
  * [3.1 条件语句](#3.1)
  * [3.2 循环语句](#3.2)
    * [3.2.1 for循环](#3.2.1)
    * [3.2.2 while循环](#3.2.2)
    * [3.2.3 break](#3.2.3)
    * [3.2.4 continue](#3.2.4)
  * [3.3 pass语句](#3.3)
"""

如果在C语言的环境下,我肯定想去写一个算法解决这个问题,但是这就太麻烦了,Python就白学了,我决定问问ChatGPT,它说我们可以用正则表达式来解决这个问题,所以就有了本章的内容。(点我直接看答案)

什么是正则表达式?

正则表达式(Regular Expression),是一种用于匹配字符串模式的表达式。它是一种强大的工具,广泛用于文本处理、搜索和匹配操作。正则表达式提供了一种灵活的方式,使我们能够在文本中查找替换提取满足特定模式的字符串。
可以看到它的基本功能就是查找替换提取这三种了。

!!!在使用前要先import re

正则表达式的基本思想就是用一种特殊的语法来描述字符串的模式,比如我们上面的字符串Test_text,我们想要去掉括号及其中内容,那么我们想要匹配到的模式就是:先匹配到左括号(,接着匹配任意不是右括号)的字符零次或多次,直到匹配到右括号,这样我们就描述了括号及其内容的模式。

基本思想了解了,那我们怎么用编程语言去表达这个模式呢?
答案就是通过使用元字符+普通字符辅佐以一些量词或边界符号来组合表达这个模式。

10.1 元字符

字符描述
^匹配输入字符串的开始位置
$匹配输入字符串的结束位置
\将下一个字符标记为一个特殊字符
如我们上面要匹配左括号,那么就使用\(将左括号标记
*匹配前面的子表达式零次或多次
+匹配前面的子表达式一次或多次
?匹配前面的子表达式零次或一次
{n}匹配确定的n次
{n,}匹配至少n次
{n,m}n <= m,最少匹配n次最多匹配m次
x?其中x表示任何一个其他限制符(*, +, ?, {n}, {n,}, {n,m})
此时匹配是非贪婪的,详见注1
.匹配除换行符之外的任何单个字符
(pattern)按pattern模式匹配并获取这一匹配,并将该匹配存放在类re.Match中
存放内容有索引范围span和匹配字符串match[注2]
(?:pattern)按pattern模式匹配但不获取匹配结果[注3]
(?=pattern)正向肯定预查,相当于是检查括号前的字符串是否能够正确匹配,
括号前的字符串匹配后,如果后面跟的字符串与pattern匹配,
则括号前内容属于正确匹配
(?!pattern)正向否定预查,也是检查能否正确匹配,
与上面的区别是后面字符串必须不与pattern匹配
(?<=pattern)反向肯定预查,与正向肯定预查类似,只是反向
(?<!pattern)反向否定预查,同上,例程见[注4]
x|y匹配x或y
[xyz]匹配所包含的任意字符
[^xyz]匹配未包含的任意字符
[a-z]匹配a-z中任意小写字符
[^a-z]匹配不在a-z中的任意字符
\b匹配一个单词边界,也就是单词和空格间的位置,见[注5]
\B匹配非单词边界,结合上面例程中搜索ar在单词中间的例子,
第四项还可以用\b\w*\Bar\B\w*这个表达式
\w匹配字母、数字、下划线,等价于[a-zA-Z0-9_]
\W匹配非字母、数字、下划线
\d匹配一个数字字符,等价于[0-9]
\D匹配一个非数字字符
\n换行符
\r回车符
\s空白字符,等价于[\f\n\r\t\v]
\S非空白字符
\p{Han}匹配中文字符,但需要安装并导入库regex

表注

  • 注1: 非贪婪,也就是尽可能少的匹配搜索的字符串,对于字符串"AHHHHHHHHHHHH!!!",H+?将匹配单个H,,而H+将匹配所有H
  • 注2:
    import re
    
    text = "Hello, my email is john.doe@example.com"
    
    # 使用捕获组获取邮箱中的用户名和域名
    match_result = re.search(r'(\w+)\.(\w+)@(\w+\.\w+)', text)
    
    print(match_result)  # <re.Match object; span=(19, 39), match='john.doe@example.com'>
    print(type(match_result))  # <class 're.Match'>
    
  • 注3:
    import re
    
    text = "apple banana cherry"
    
    # 使用非捕获组匹配水果名称,但不获取匹配
    match_result = re.findall(r'\b(?:apple|banana|cherry)\b', text)
    
    print(match_result)
    # ['apple', 'banana', 'cherry']  
    # 用re.findall返回所有匹配结果,是一个列表
    
  • 注4:
    import re
    
    text = "Windows10"
    
    # 1. 正向肯定预查(Positive Lookahead)
    positive_lookahead_pattern = re.compile(r'Windows(?=10)')
    result_positive_lookahead = positive_lookahead_pattern.findall(text)
    print("正向肯定预查:", result_positive_lookahead)
    # 正向肯定预查: ['Windows']  在搜索到Windows后,检查发现后面的字符串也是10,正确匹配
    
    # 2. 正向否定预查(Negative Lookahead)
    negative_lookahead_pattern = re.compile(r'Windows(?!10)')
    result_negative_lookahead = negative_lookahead_pattern.findall(text)
    print("正向否定预查:", result_negative_lookahead)
    # 正向否定预查: []  在搜索到Windows后,检查发现后面的字符串是10,被否定掉
    
    # 3. 反向肯定预查(Positive Lookbehind)
    positive_lookbehind_pattern = re.compile(r'(?<=Windows)10')
    result_positive_lookbehind = positive_lookbehind_pattern.findall(text)
    print("反向肯定预查:", result_positive_lookbehind)
    # 反向肯定预查: ['10']  在搜索到10后,检查发现前面的字符串是Windows,正确匹配
    
    # 4. 反向否定预查(Negative Lookbehind)
    negative_lookbehind_pattern = re.compile(r'(?<!Windows)10')
    result_negative_lookbehind = negative_lookbehind_pattern.findall(text)
    print("反向否定预查:", result_negative_lookbehind)
    # 反向否定预查: []  在搜索到10后,检查发现前面的字符串是Windows,被否定掉
    
  • 注5:
    import re
    
    text = "pillar regular arrange arch article march"
    
    word_beginwith_ar = re.compile(r'\bar\w*\b')
    word_endwith_ar = re.compile(r'\b\w*ar\b')
    word_endorbeginwith_ar = re.compile(r'\bar\w*\b|\b\w*ar\b')
    word_contain_ar = re.compile(r'\b\w+ar\w+\b')
    
    word_beginwith_ar_result = word_beginwith_ar.findall(text)
    word_endwith_ar_result = word_endwith_ar.findall(text)
    word_endorbeginwith_ar_result = word_endorbeginwith_ar.findall(text)
    word_contain_ar_result = word_contain_ar.findall(text)
    
    print(f"以'ar'开头的单词有:{word_beginwith_ar_result}")
    print(f"以'ar'结尾的单词有:{word_endwith_ar_result}")
    print(f"以'ar'开头或结尾的单词有:{word_endorbeginwith_ar_result}")
    print(f"中间包含'ar'但边缘没有的单词有:{word_contain_ar_result}")
    # 以'ar'开头的单词有:['arrange', 'arch', 'article']
    # 以'ar'结尾的单词有:['pillar', 'regular']
    # 以'ar'开头或结尾的单词有:['pillar', 'regular', 'arrange', 'arch', 'article']
    # 中间包含'ar'但边缘没有的单词有:['march']
    

例:匹配邮箱的正则表达式:

import re

text = "Hello, my email is john.doe@example.com"

address_pattern = re.compile(r'\b[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,6}\b')

address = address_pattern.findall(text)

print(address)  # ['john.doe@example.com']

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


10.2 查找

10.2.1 re.match

基本语法:re.match(pattern, string, flags=0)

  • pattern 为匹配的正则表达式
  • string 为要匹配的字符串
  • flags 为标志位,可选,用于控制正则表达式的匹配方式,如是否区分大小写,多行匹配等,见10.5 修饰符
  • re.match从字符串的起始位置开始匹配,如果起始位置就不匹配,直接返回None,如果匹配,就输出匹配到的内容,当然这个内容还是以re.Match存储

10.2.2 re.search

相比于re.match只能匹配字符串开头,re.search会扫描整个字符串并返回第一个匹配到的字符串

基本语法:re.search(pattern, string, flags=0)

  • 参数意义与re.match相同

这里介绍一个函数group(index),可以将捕获的内容以字符串形式返回,

但在此之前我们要先明确捕获组的概念:当我们在规定正则表达式时,可以使用()在正则表达式中划分捕获组,例如(\b\w*ar\b)(\s*)(\bar\w*\b)划分了连续的三个捕获组,以 ar 结尾的单词、若干空白字符与以 ar 开头的单词,这样我们就可以使用group()函数来访问这些捕获组了

import re

text = "pillar regular arrange arch article march"

s = re.search(r'(\b\w*ar\b)(\s*)(\bar\w*\b)',text)
if s:
  for i in range(1,s.lastindex+1):
    print(s.group(i))
  print(s.group()) # regular arrange
else:
  print("No match!")

比如这个例子,group()会将全部匹配项列出,而group(i)会将第i个捕获组输出

10.2.3 re.compile

re.compile函数用于编译正则表达式,生成一个正则表达式对象,提高模式的重用性

基本语法:re.compile(pattern, flag)

  • 参数意义与上同

比如上面的例子可以改为:

import re

text = "pillar regular arrange arch article march"

s_pattern = re.compile(r'(\b\w*ar\b)(\s*)(\bar\w*\b)')

s = re.search(s_pattern,text)
if s:
  for i in range(1,s.lastindex+1):
    print(s.group(i))
  print(s.group()) # regular arrange
else:
  print("No match!")

10.3 替换

10.3.1 re.sub

re.sub可以替换字符串中的匹配项

基本语法:re.sub(pattern, replace, string, max, flags=0)

  • pattern 为待替换的字符串模式
  • replace 为用于替换的字符串
  • max 为最大替换次数,可选,默认为最大

如果想要删除某些内容只需要用于替换的字符串为''即可
所以我们现在已经可以解决开头时的问题,删除掉括号及其中内容了:

import re

Test_text = """* [2. Python,启动!](#启动)
  * [2.1 标识符](#2.1)
  * [2.2 输入输出](#2.2)
  * [2.3 缩进](#2.3)
  * [2.4 多行输入](#2.4)
  * [2.5 注释](#2.5)
  * [2.6 import](#2.6)
  * [2.7 一些散碎关键字](#2.7)
* [3. 基本语句](#基本语句)
  * [3.1 条件语句](#3.1)
  * [3.2 循环语句](#3.2)
    * [3.2.1 for循环](#3.2.1)
    * [3.2.2 while循环](#3.2.2)
    * [3.2.3 break](#3.2.3)
    * [3.2.4 continue](#3.2.4)
  * [3.3 pass语句](#3.3)
"""

del_bracket_pattern = re.compile(r'\(.*\)') # 删除所有括号及其中内容
del_enter_pattern = re.compile(r'\s') # 删除所有空白符

result_del_bracket = del_bracket_pattern.sub('',Test_text)
result_del_enter = del_enter_pattern.sub('',result_del_bracket)

print(result_del_enter)
# *[2.Python,启动!]*[2.1标识符]*[2.2输入输出]*[2.3缩进]*[2.4多行输入]*[2.5注释]*[2.6import]*[2.7一些散碎关键字]*[3.基本语句]*[3.1条件语句]*[3.2循环语句]*[3.2.1for循环]*[3.2.2while循环]*[3.2.3break]*[3.2.4continue]*[3.3pass语句]

10.4 提取

10.4.1 re.findall

基本语法:re.findall(pattern, string, flags=0, end)

  • pattern 、 string 、flags 与上同
  • end 为指定字符串结束位置,可选,默认为字符串长度

re.findall可以找到正则表达式所匹配的所有子字符串,并返回一个列表,如果有多个匹配模式,则返回元组列表

import re

text = "pillar regular arrange arch article march"

s_pattern = re.compile(r'(\b\w*ar\b)(\s*)(\bar\w*\b)')

s = s_pattern.findall(text)

if s:
  print(s) # [('regular', ' ', 'arrange')]
else:
  print("No match!")

10.4.2 re.finditer

和findall类似,但是finditer以一个迭代器返回

import re

text = "pillar regular arrange arch article march"

s_pattern = re.compile(r'(\b\w*ar\w*\b)')

s = s_pattern.finditer(text)
"""输出所有带'ar'的单词"""
if s:
  for matchs in s:
    print(matchs.group())
else:
  print("No match!")

10.4.3 re.split

split 方法以匹配的字符串为分割符将字符串分割后返回列表

基本语法:re.split(pattern, string, max, flags=0)

  • max 为最大分割次数,可选,默认为不限次数
import re

text = "pillar regular arrange arch article march"

s_pattern = re.compile(r'\s')

s = s_pattern.split(text)
"""以空白字符为分隔符将字符串分割"""
if s:
  print(s) # ['pillar', 'regular', 'arrange', 'arch', 'article', 'march']
else:
  print("No match!")

10.5 修饰符

修饰符就是我们上面一些函数中的flag位,用来控制匹配的模式

修饰符描述
re.INGORECASE 或 re.I忽略大小写
re.MULTILINE 或 re.M多行匹配
此时 ^ 和 $ 匹配每一行的开头结尾
re.DOTALL 或 re.S使.匹配任意字符
re.VERBOSE 或 re.X忽略空白和注释

修饰符可以组合使用,用|隔开,如re.X|re.I


  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HIT-Zxy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值