正则表达式学习

一、正则表达式算法

正则表达式(或 RE)指定了一组与之匹配的字符串;模块内的函数可以检查某个字符串是否与给定的正则表达式匹配(或者正则表达式是否匹配到字符串,这两种说法含义相同)。

二、正则表达式语法

正则表达式默认贪婪匹配;要求更严格的匹配要求放在'|'左边

 1、普通字符

正则表达式可以包含普通或者特殊字符。绝大部分普通字符,比如 'A''a', 或者 '0',都是最简单的正则表达式。它们就匹配自身。你可以拼接普通字符,所以 last 匹配字符串 'last'

  • 字母:如 a-zA-Z
  • 数字:如 0-9
  • 特殊字符:如 !@#$%^&*() 当它们不被用作元字符时。

字符集

普通字符可以与字符集配合使用:字符集中除预定义的字符外,默认是普通字符含义。

字符集仅表示可以匹配1个在字符集的字符,但要多个要用量词匹配。

字符集的字符没有先后顺序要求,[1-9-]+ 可匹配‘-123456-’

  • 字符可以单独列出,比如 [amk] 匹配 'a', 'm', 或者 'k'

  • 可以表示字符范围,通过用 '-' 将两个字符连起来。比如 [a-z] 将匹配任何小写ASCII字符, [0-5][0-9] 将匹配从 00 到 59 的两位数字, [0-9A-Fa-f] 将匹配任何十六进制数位。 如果 - 进行了转义 (比如 [a\-z])或者它的位置在首位或者末尾(如 [-a] 或 [a-]),它就只表示普通字符 '-'

  • 特殊字符在集合中会失去其特殊意义。比如 [(+*)] 只会匹配这几个字面字符之一 '(''+''*', or ')'

  • 字符类如 \w 或者 \S (定义如下) 也在集合内被接受,不过它们可匹配的字符则依赖于所使用的 flags

  • 不在集合范围内的字符可以通过 取反 来进行匹配。如果集合首字符是 '^' ,所有  在集合内的字符将会被匹配,比如 [^5] 将匹配所有字符,除了 '5', [^^] 将匹配所有字符,除了 '^'^ 如果不在集合首位,就没有特殊含义。

  • 要在集合内匹配一个 ']' 字面值,可以在它前面加上反斜杠,或是将它放到集合的开头。 例如,[()[\]{}] 和 []()[{}] 都可以匹配右方括号,以及左方括号,花括号和圆括号。

转普通字符

如果要匹配 '|' 字符,使用 \|, 或者把它包含在字符集里,比如 [|].

2、元字符

元字符是指在正则表达式中有特殊含义的字符。它们通常用于描述匹配模式的行为:

预定义字符类

预定义字符类提供了一些常用的字符集:

  • \d:任何十进制数字 [0-9]
  • \D:非数字字符 [^0-9]
  • \w:任何字母数字字符 [a-zA-Z0-9_]
  • \W:非字母数字字符 [^a-zA-Z0-9_]
  • \s:空白字符(空格、制表符、换页符等)。
  • \S:非空白字符。

按功能区分的特殊字符

  • 定位符:用于指明匹配的位置。
    • ^:匹配输入字符串的开始位置。
    • $:匹配输入字符串的结束位置。
  • 量词:控制前面的元素重复的次数。
    • *:前面的元素可以出现零次或多次。
    • +:前面的元素至少出现一次。
    • ?:前面的元素可以出现零次或一次。
    • {n}:前面的元素恰好出现 n 次。
    • {n,}:前面的元素至少出现 n 次。
    • {m,n}:前面的元素至少出现 m次,但不超过 n 次。_在 m 和 n 之间取尽量多,默认优先匹配n次
  • 非贪婪量词:非贪婪量词会尽可能少地匹配字符。

        *?: 非贪婪的 `*`。

         +?: 非贪婪的 `+`。

         ??: 非贪婪的 `?`。

         {m,n}?: 非贪婪的 `{m,n}`  ——尝试匹配尽可能 少的 重复次数,默认优先匹配m次

  • 字符集:定义一个字符集。
    • []:定义一个字符集合。
    • [^] 或 [:^:]:定义一个否定的字符集合。
  • 选择:表示多个可能的匹配。
    • |:选择操作符,表示“或”关系。
    • 扫描目标字符串时, '|' 分隔开的正则样式从左到右进行匹配。当一个样式完全匹配时,这个分支就被接受。意思就是,一旦 A 匹配成功, B 就不再进行匹配,即便它能产生一个更好的匹配。或者说,'|' 操作符绝不贪婪。
  • 分组与引用:用于组合子表达式,并允许对子表达式的引用。
    • ():创建一个捕获组。
    • \1\2, ...:引用第 n 个捕获组的内容。
  • 其他特殊字符: . 匹配任何单个字符(除了换行符)。
  • 转义字符:用于表示特殊字符或预定义字符类。
    • \:转义字符,后跟一个特殊字符或预定义字符类。
  • 断言:测试一个位置而非匹配字符。
    • (?=...):正向先行断言,检查后面是否跟着 ...
    • (?!...):负向先行断言,检查后面是否不是 ...
    • (?<=...):正向后行断言,检查前面是否是 ...
    • (?<!...):负向后行断言,检查前面是否不是 ...
  • 边界符:用于匹配单词边界。
    • \b:单词边界。
    • \B:非单词边界。

特殊用途字符

  • 控制字符:如 \t(制表符),\n(换行符)等。

三、re函数

Python 基于正则表达式提供了不同的原始操作:

  • re.match() 只在字符串的开头位置检测匹配。

  • re.search() 在字符串中的任何位置检测匹配(这也是 Perl 在默认情况下所做的)

  • re.fullmatch() 检测整个字符串是否匹配

re.findall(pattern, string, flags=0)

  • 用途:在字符串中查找所有与正则表达式匹配的子串。
  • 语法re.findall(pattern, string, flags=0)
  • 参数
    • pattern:要匹配的正则表达式字符串。
    • string:要搜索的目标字符串。
    • flags:可选参数,用于设置正则表达式的标志位。
  • 返回值:返回一个包含所有匹配项的列表。
  • 返回值类型list

re.sub(pattern, repl, string, count=0, flags=0)

  • 用途:替换字符串中所有与正则表达式匹配的子串。
  • 语法re.sub(pattern, repl, string, count=0, flags=0)
  • 参数
    • pattern:要匹配的正则表达式字符串。
    • repl:替换字符串或一个回调函数。
    • string:要搜索的目标字符串。
    • count:可选参数,指定最多替换的匹配次数。
    • flags:可选参数,用于设置正则表达式的标志位。
  • 返回值:返回替换后的字符串。
  • 返回值类型str

re.split(pattern, string, maxsplit=0, flags=0)

  • 用途:根据正则表达式分割字符串。
  • 语法re.split(pattern, string, maxsplit=0, flags=0)
  • 参数
    • pattern:要匹配的正则表达式字符串。
    • string:要分割的目标字符串。
    • maxsplit:可选参数,指定最多分割的次数。默认为 0,表示分割所有匹配项。
    • flags:可选参数,用于设置正则表达式的标志位。
  • 返回值:返回一个由分割后的子字符串组成的列表。
  • 返回值类型list

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

  • 用途:搜索字符串中是否存在与正则表达式匹配的子串。
  • 语法re.search(pattern, string, flags=0)
  • 参数
    • pattern:要匹配的正则表达式字符串。
    • string:要搜索的目标字符串。
    • flags:可选参数,用于设置正则表达式的标志位,例如 re.IGNORECASE 表示忽略大小写。
  • 返回值:如果找到匹配项,则返回第一个匹配的对象;否则返回 None
  • 返回值类型re.Match 类型的对象或者 None

re.match(pattern, string, flags=0)

  • 用途:从字符串的起始位置匹配正则表达式。
  • 语法re.match(pattern, string, flags=0)
  • 参数
    • pattern:要匹配的正则表达式字符串。
    • string:要搜索的目标字符串。
    • flags:可选参数,用于设置正则表达式的标志位。
  • 返回值:如果匹配成功,则返回一个匹配对象;否则返回 None
  • 返回值类型re.Match 类型的对象或者 None
import re
p = '[a-z]+'
s = '123abc1a'
print(re.split(p,s))  # ['123', '1', '']
print(re.findall(p, s)) # ['abc', 'a']
print(re.match(p,s)) # None,起始位置
print(re.search(p,s)) # <re.Match object; span=(3, 6), match='abc'>
print(re.search(p,s).group()) # abc 要获取Match对象的值,需要用group
print(re.sub(p,'*',s)) # 123*1*

r = re.compile(p)
print(r.findall(s))  # ['abc', 'a'] 

re.finditer(pattern, string, flags=0)

  • 用途:返回一个迭代器,该迭代器会产生每个匹配项的匹配对象。
  • 语法re.finditer(pattern, string, flags=0)
  • 参数
    • pattern:要匹配的正则表达式字符串。
    • string:要搜索的目标字符串。
    • flags:可选参数,用于设置正则表达式的标志位。
  • 返回值:返回一个迭代器,可用于遍历所有匹配项。
  • 返回值类型iterator

re.compile(pattern, flags=0)

  • 用途:编译正则表达式为模式对象,可以用于多次匹配。
  • 语法re.compile(pattern, flags=0)
  • 参数
    • pattern:要匹配的正则表达式字符串。
    • flags:可选参数,用于设置正则表达式的标志位。
  • 返回值:返回一个模式对象,可以使用它的 searchmatchfindallfinditer, 和 sub 方法。
  • 返回值类型re.Pattern 类型的对象。

re.fullmatch(pattern, string, flags=0)

  • 用途:在整个字符串中匹配正则表达式。
  • 语法re.fullmatch(pattern, string, flags=0)
  • 参数
    • pattern:要匹配的正则表达式字符串。
    • string:要搜索的目标字符串。
    • flags:可选参数,用于设置正则表达式的标志位。
  • 返回值:如果整个字符串与正则表达式完全匹配,则返回一个匹配对象;否则返回 None

四、re对象

class re.Pattern

由 re.compile() 返回的已编译正则表达式对象。

创建 re.Pattern 对象
  • 函数re.compile(pattern, flags=0)
  • 参数
    • pattern:正则表达式的字符串形式。
    • flags:可选参数,用于设置正则表达式的标志位,如 re.IGNORECASE 等。
使用 re.Pattern 对象

一旦有了re.Pattern对象,就可以使用它的方法来进行匹配操作:

  • search(string, pos=0, endpos=None):搜索字符串中是否存在与正则表达式匹配的子串。
    • 参数
      • string:要搜索的目标字符串。
      • pos:可选参数,指定搜索的起始位置。
      • endpos:可选参数,指定搜索的结束位置。
    • 返回值:如果找到匹配项,则返回一个匹配对象;否则返回 None
  • match(string, pos=0, endpos=None):从字符串的起始位置匹配正则表达式。
    • 参数
      • string:要搜索的目标字符串。
      • pos:可选参数,指定搜索的起始位置。
      • endpos:可选参数,指定搜索的结束位置。
    • 返回值:如果匹配成功,则返回一个匹配对象;否则返回 None
  • fullmatch(string, pos=0, endpos=None):在整个字符串中匹配正则表达式。
    • 参数
      • string:要搜索的目标字符串。
      • pos:可选参数,指定搜索的起始位置。
      • endpos:可选参数,指定搜索的结束位置。
    • 返回值:如果整个字符串与正则表达式完全匹配,则返回一个匹配对象;否则返回 None
  • findall(string, pos=0, endpos=None):在字符串中查找所有与正则表达式匹配的子串。
    • 参数
      • string:要搜索的目标字符串。
      • pos:可选参数,指定搜索的起始位置。
      • endpos:可选参数,指定搜索的结束位置。
    • 返回值:返回一个包含所有匹配项的列表。
  • finditer(string, pos=0, endpos=None):返回一个迭代器,该迭代器会产生每个匹配项的匹配对象。
    • 参数
      • string:要搜索的目标字符串。
      • pos:可选参数,指定搜索的起始位置。
      • endpos:可选参数,指定搜索的结束位置。
    • 返回值:返回一个迭代器,可用于遍历所有匹配项。
  • sub(repl, string, count=0, pos=0, endpos=None):替换字符串中所有与正则表达式匹配的子串。
    • 参数
      • repl:替换字符串或一个回调函数。
      • string:要搜索的目标字符串。
      • count:可选参数,指定最多替换的匹配次数。
      • pos:可选参数,指定搜索的起始位置。
      • endpos:可选参数,指定搜索的结束位置。
    • 返回值:返回替换后的字符串。

可以使用上面的函数,如下:

r = re.compile(p)
print(r.findall(s))  # ['abc', 'a'] 

class re.Match

由成功的 match 和 search 所返回的匹配对象。

使用 re.Match 对象

re.Match 对象提供了以下方法来访问匹配结果:

  • group([group1, ...]):返回一个或多个分组匹配的字符串。如果没有分组,则返回整个匹配的字符串。
    • 参数
      • group1, ...:可选参数,指定要返回的分组编号。
    • 返回值:匹配的字符串或元组。
  • groups(default=None):返回一个包含所有非空分组的元组。
    • 参数
      • default:可选参数,如果分组为空则返回此默认值。
    • 返回值:一个包含所有非空分组的元组。
  • start([group]):返回匹配开始的位置。
    • 参数
      • group:可选参数,指定要返回开始位置的分组编号。
    • 返回值:匹配开始的位置。
  • end([group]):返回匹配结束的位置。
    • 参数
      • group:可选参数,指定要返回结束位置的分组编号。
    • 返回值:匹配结束的位置。
  • span([group]):返回一个元组,包含匹配的开始和结束位置。
    • 参数
      • group:可选参数,指定要返回位置的分组编号。
    • 返回值:一个元组,包含匹配的开始和结束位置。
  • re:返回用于匹配的正则表达式对象。
  • string:返回原始的搜索字符串。
  • lastgroup:返回最后一个匹配的分组编号。
  • lastindex:返回最后一个匹配的分组索引。

五、分组

分组匹配的概念

在正则表达式中,可以使用圆括号 () 来定义一个分组。一个分组内的表达式作为一个整体参与匹配过程,并且可以通过分组编号来引用该分组匹配到的内容。

基本用法

  • 创建分组:使用圆括号 () 将需要作为一个整体匹配的部分括起来。
  • 引用分组:使用反斜杠 \ 后跟一个数字来引用分组。数字表示分组的编号,从左到右依次递增,第一个分组编号为 1。
'''如果你需要找到所有符合分组要求的匹配项,应该使用 re.findall。
如果你需要从字符串的开头进行精确匹配,并且只需要第一个匹配项,可以使用 re.match。
如果你需要在字符串中找到第一个匹配项,无论它出现在什么位置,可以使用 re.search。'''

import re
s = 'my birth is 2024-8-3, xixi is 2022-4-2'
print(re.findall('\d+',s)) # ['2024', '8', '3', '2022', '4', '2']
print(re.findall('\d+-\d+-\d+',s)) # ['2024-8-3', '2022-4-2']
print(re.findall('(\d+)-(\d+)-(\d+)',s)) # [('2024', '8', '3'), ('2022', '4', '2')]  二维 最低层是找到的单元组


m = re.match('(\d+)-(\d+)-(\d+)',s) # match必需匹配开头,且只找1个
print(m) # None
# print(m.group()) # AttributeError: 'NoneType' object has no attribute 'group'

m = re.search('(\d+)-(\d+)-(\d+)',s) # search 只找到第一个匹配
print(m)  # <re.Match object; span=(0, 8), match='2024-8-3'>
print(m.group())  # 2024-8-3
print(m.group(1)) # 2024
print(m.group(2)) # 8
print(m.groups()) # ('2024', '8', '3')
示例

假设我们要从一个字符串中提取日期格式为 YYYY-MM-DD 的日期,我们可以使用以下正则表达式:

1import re
2
3date_pattern = r'(\d{4})-(\d{2})-(\d{2})'
4text = 'Today is 2023-08-03.'
5
6# 使用 findall 获取所有匹配的日期
7matches = re.findall(date_pattern, text)
8print(matches)  # 输出: [('2023', '08', '03')]

在这个例子中,我们定义了三个分组:

  1. 第一个分组:(\d{4}) - 匹配四位数字,表示年份。
  2. 第二个分组:(\d{2}) - 匹配两位数字,表示月份。
  3. 第三个分组:(\d{2}) - 匹配两位数字,表示日期。

使用 re.Match 对象访问分组

当你使用 re.search, re.match, re.finditer 等方法时,返回的 re.Match 对象可以用来访问分组匹配的结果。

使用 group() 方法
  • group():返回整个匹配的字符串。
  • group(n):返回第 n 个分组匹配的字符串。
  • group(0):返回整个匹配的字符串。
  • group(1, 2, ...):返回多个分组匹配的字符串作为元组。
使用 groups() 方法
  • groups():返回所有非空分组匹配的字符串组成的元组。
  • groups(default):返回所有非空分组匹配的字符串组成的元组,对于未匹配的分组,使用 default 作为占位符。
使用 span() 和 start()end() 方法
  • span():返回一个元组,包含匹配的开始和结束位置。
  • span(n):返回第 n 个分组匹配的开始和结束位置。
  • start():返回匹配的开始位置。
  • start(n):返回第 n 个分组匹配的开始位置。
  • end():返回匹配的结束位置。
  • end(n):返回第 n 个分组匹配的结束位置。
示例

下面是一个使用 re.Match 对象访问分组的示例:

1import re
2
3date_pattern = r'(\d{4})-(\d{2})-(\d{2})'
4text = 'Today is 2023-08-03.'
5
6# 使用 search 查找第一个匹配
7match = re.search(date_pattern, text)
8if match:
9    print("Full date:", match.group())  # 输出: Full date: 2023-08-03
10    print("Year:", match.group(1))  # 输出: Year: 2023
11    print("Month:", match.group(2))  # 输出: Month: 08
12    print("Day:", match.group(3))  # 输出: Day: 03
13    print("Year, Month, Day:", match.groups())  # 输出: Year, Month, Day: ('2023', '08', '03')

使用分组进行替换

在使用 re.sub 进行替换时,也可以使用分组来构建新的字符串。

示例

假设我们需要将日期格式从 YYYY-MM-DD 转换为 DD-MM-YYYY

1import re
2
3date_pattern = r'(\d{4})-(\d{2})-(\d{2})'
4text = 'Today is 2023-08-03.'
5
6# 使用 sub 替换日期格式
7new_text = re.sub(date_pattern, r'\3-\2-\1', text)
8print(new_text)  # 输出: Today is 03-08-2023.

在这个例子中,我们使用 \3-\2-\1 来引用分组,并按照新的顺序重新构造日期。

总结

  • 使用圆括号 () 创建分组。
  • 使用 group() 和 groups() 访问分组匹配的结果。
  • 使用 span()start(), 和 end() 获取匹配的位置信息。
  • 使用分组进行替换时,可以通过反斜杠 \ 后跟分组编号来引用分组。

附录:

1、官方地址:re --- 正则表达式操作 — Python 3.12.4 文档

2、源码:https://github.com/python/cpython/tree/3.12/Lib/re/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值