Python_正则表达式:Day16

本文深入解析正则表达式的强大功能,从基础语法到高级应用,如分组、贪婪匹配及正则表达式的预编译技巧。通过实例演示如何进行字符串匹配、切分和提取,适用于各种编程需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

养成一个好的习惯只需要坚持21天,Day16

正则表达式

正则表达式是一种用来匹配字符串的强有力的武器,它用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。
\d可以匹配一个数字,\w可以匹配一个字母或数字,比如下面的例子:

  • ‘00\d’ 可以匹配'007',但无法匹配'00A'
  • '\d\d\d'可以匹配'010';
  • '\w\w\d'可以匹配'py3'
  • 'py.'可以匹配'pyc'、'pyo'、'py!'等等。

.可以匹配任意字符。
要匹配变长的字符,在正则表达式中,用*表示任意个字符(包括0个),用+表示至少一个字符,用?表示0个或1个字符,用{n}表示n个字符,用{n,m}表示n-m个字符。
举一个例子:\d{3}\s+\d{3,8}
其中\d{3}表示匹配3个数字,\s可以匹配一个空格,\s+表示至少有一个空格;\d{3,8}表示可以匹配3-8个数字。
匹配一个座机号码\d{3}\-\d{3,8},比如'010-12345',中间的-是特殊字符,在编写的时候需要用\转义。

进阶

要做更精确的匹配,可以使用[]表示范围,比如:

  • [0-9a-zA-Z\_]可以匹配一个数字、字母或者下划线;
  • [0-9a-zA-Z\_]+可以匹配至少由一个数字、字母或者下划线组成的字符串,比如’a100’,‘0_Z’,'Py3000’等等;
  • [a-zA-Z\_][0-9a-zA-Z\_]*可以匹配由字母或下划线开头,后接任意个由一个数字、字母或者下划线组成的字符串,也就是Python合法的变量;
  • [a-zA-Z\_][0-9a-zA-Z\_]{0, 19}更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)。

A|B可以匹配A或B,所以(P|p)ython可以匹配’Python’或者’python’。
^表示行的开头,^\d表示必须以数字开头。
$表示行的结束,\d$表示必须以数字结束。

re模块

由之前的学习可知,可以使用\转义,也可以使用Python的r前缀,使用r前缀可以避免许多转义的问题。

s = r'ABC\-001' # Python的字符串
# 对应的正则表达式字符串不变:
# 'ABC\-001'

re模块中,使用match()方法判断是否匹配,匹配成功则返回一个Match对象,否则返回None。如下:

import re
test = '用户输入的测试字符串'
if re.match(r'正则表达式', test):  #re.match(r'^\d{3}\-\d{3,8}$', '010-12345')
	print('ok')
else:
	print('failed')
切分字符串

比如我们一般使用python的split()函数进行切分:

'a b  c'.split(' ')
#['a', 'b', '', 'c']无法识别字符串中连续的空格

使用正则表达式进行切分:

re.split(r'\s+','a b  c')
# ['a', 'b', 'c']

如果把划分规则变成逗号:

re.split(r'[\s\,]+','a,b,  c    d')
# ['a', 'b', 'c', 'd']

再加入分号,进行切分:

re.split(r'[\s\,\;]+','a,b;;  c    d')
# ['a', 'b', 'c', 'd']
分组

正则表达式除了能够进行字符串的匹配之外,还能提取子串。即对字符串的内容进行分组操作,用()表示。
^(\d{3})-(\d{3,8})$中由分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:

m = re.match(r'^(\d{3})-(\d{3,8})$', '083-8250168')
print(m)
# <_sre.SRE_Match object; span=(0, 11), match='083-8250168'>
 m.group(0)
#'083-8250168'
m.group(1)
# '083'
m.group(2)
# '8250168'
贪婪

正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。比如:

re.match(r'^(\d+)(0*)$', '102300').groups()
# ('102300', '') 由于\d+采用贪婪匹配,直接把后面的0全部匹配了,所以0*就只能匹配空字符串了。

\d+后面加上一个就采用非贪婪匹配了:

re.match(r'^(\d+?)(0*)$', '102300').groups()
# ('1023', '00')

可以使用re.compile()进行预编译,当正则表达式需要多次使用的时候,可以提高效率!
练习
请尝试写一个验证Email地址的正则表达式。版本一应该可以验证出类似的Email:someone@gmail.com、bill.gates@microsoft.com

import re
re_mail = re.compile(r'^[\w]+\.?[\w]+@[\w]+\.com$')
# 字母一个以上 .有一个或者没有 字母一个以上 @ 字母不限 .com

def is_valid_email(addr):
    if re_mail.match(addr):
        return True
### 高级正则表达式的特性 Python 的 `re` 模块支持多种高级功能,这些功能使得处理复杂模式匹配更加灵活和强大。除了基本的字符类、量词外,还提供了诸如零宽断言、捕获组以及条件子模式等功能。 #### 零宽断言 零宽断言允许在不消耗任何字符的情况下测试某个位置前后的内容是否满足特定条件。这有助于更精确地定位目标字符串中的某些部分而不影响其他部分[^1]。 - **先行断言** (`(?=...)`):仅当后面跟有指定模式时才匹配当前位置。 - **否定先行断言** (`(?!...)`):只有当前面不是指定模式开头时不发生匹配。 ```python import re text = "The rain in Spain" pattern_with_positive_lookahead = r"\b\w+(?=ain\b)" matches = re.findall(pattern_with_positive_lookahead, text) print(matches) # Output: ['rain', 'Spain'] ``` #### 捕获组与命名捕获组 通过括号可以创建捕获组来提取感兴趣的部分,并可通过索引访问它们;而命名捕获组则提供了一种更具可读性的方法来引用分组结果[^3]。 ```python date_string = "2023-04-15" # 使用编号捕获组 match_numbered_groups = re.match(r"(\d{4})-(\d{2})-(\d{2})", date_string) if match_numbered_groups: year, month, day = match_numbered_groups.groups() print(f"Year={year}, Month={month}, Day={day}") # 使用命名捕获组 match_named_groups = re.match( r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})", date_string ) if match_named_groups: formatted_date = f"{match_named_groups['year']}-{match_named_groups['month']}-{match_named_groups['day']}" print(formatted_date) ``` #### 条件子模式 条件语句可以根据前面是否存在某一分支决定后续路径的选择。这对于编写动态变化逻辑非常有用[^4]。 ```python conditional_pattern = r"(?(?=.*world)(hello|hi)|bye)" test_strings = ["hello world", "hi universe", "goodbye"] for s in test_strings: m = re.search(conditional_pattern, s) if m: print(m.group()) ``` #### 支持Perl兼容正则表达式(PCRE) Python 中也实现了许多来自 Perl 正则表达式的扩展语法,比如非贪婪匹配(`*?`, `+?`, `{n,m}?`)等,增强了灵活性并提高了效率。 ```python non_greedy_example = "<p>Some paragraph</p><div>Another block</div>" greedy_vs_non_greedy = [ ("Greedy:", r"<.*>", non_greedy_example), ("Non-Greedy:", r"<.*?>", non_greedy_example), ] for label, pattern, string_to_search in greedy_vs_non_greedy: matches = re.findall(pattern, string_to_search) print(label, matches) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值