说明
正则表达式(Regular Expression,简称为 regex 或 regexp)是一种用于描述、匹配和操作字符串模式的工具。它是一种强大的文本处理工具,用于在字符串中查找、替换、分割和验证特定模式的文本。
正则表达式由一系列字符和特殊字符组成,用于构建一个模式,该模式描述了你想要匹配的字符串的特定格式。你可以使用正则表达式来处理以下任务:
-
匹配字符串: 正则表达式可以用于确定一个字符串是否匹配某个特定的模式。例如,你可以使用正则表达式来检查一个字符串是否是有效的电子邮件地址。
-
查找和提取: 你可以使用正则表达式来在一个文本中查找特定模式的字符串,并从中提取所需的信息。例如,从一串文本中提取所有的电话号码。
-
替换: 正则表达式可以用于替换文本中满足特定模式的部分。例如,将一个文本中的所有 URL 替换成链接标签。
-
分割字符串: 正则表达式可以用于将字符串根据特定模式进行分割。例如,将一个包含多个单词的字符串分割成单独的单词列表。
-
验证: 你可以使用正则表达式来验证一个字符串是否满足特定的格式要求。例如,验证一个密码是否符合强度要求。
正则表达式语法非常丰富,允许你使用元字符(metacharacters)和特殊序列(special sequences)来构建模式。例如,.
表示匹配任意字符,\d
表示匹配一个数字,[a-zA-Z]
表示匹配任何一个大小写字母等等。
特点
1. 可读性差:包含元字符、特殊序列等 2. 通用性强,适用于很多编程语言,比如python、java、JavaScript
常见的元字符和特殊序列
正则表达式使用一系列的元字符(metacharacters)和特殊序列(special sequences)来构建匹配模式。以下是一些常见的元字符和特殊序列,以及关于正则表达式的最佳实践和示例:
元字符(Metacharacters):
.
:匹配任意字符,除了换行符(\n)。^
:匹配字符串的开头。$
:匹配字符串的结尾。*
:匹配前一个字符零次或多次。+
:匹配前一个字符一次或多次。?
:匹配前一个字符零次或一次。[]
:定义一个字符集,匹配其中的任意一个字符。()
:用于分组,也可以用于捕获匹配的内容。|
:表示逻辑或,匹配左右两边任意一个表达式。\
:用于转义特殊字符,或表示特殊序列。
特殊序列(Special Sequences):
\d
:匹配任意数字,相当于[0-9]
。\D
:匹配任意非数字字符,相当于[^0-9]
。\w
:匹配任意字母数字字符,相当于[a-zA-Z0-9_]还有汉字
。\W
:匹配任意非字母数字字符,相当于[^a-zA-Z0-9_]汉字
。\s
:匹配任意空白字符,包括空格、制表符即tab键(\t)、换行等。\S
:匹配任意非空白字符。\b
:匹配单词边界,通常用于确保只匹配完整的单词。\B
:匹配非单词边界。
分组
正则表达式的分组允许你将一个或多个表达式组合在一起,并对其进行操作。分组使你能够对模式的一部分进行匹配、捕获和应用重复操作等。
分组的语法:
(expression)
: 用括号将表达式括起来,形成一个分组。括号内的表达式可以包含元字符、特殊序列以及其他正则表达式元素。
分组的用途:
-
匹配和捕获: 分组允许你对特定的模式部分进行匹配,从而在匹配的同时也可以捕获这部分内容。
-
重复操作: 你可以对整个分组应用重复操作,例如使用
*
、+
、?
或{}
来指定分组重复的次数。 -
替换和反向引用: 你可以在替换操作中使用分组来捕获的内容,并在替换字符串中引用这些捕获的内容。
最佳实践:
-
使用括号进行分组: 对于复杂的正则表达式,使用括号对相关部分进行分组,以便进行操作。
-
考虑非捕获分组: 如果你只需要分组用于重复操作,但不需要捕获内容,可以使用
(?:expression)
形式的非捕获分组,以避免在捕获时产生额外的内存开销。 -
命名分组: 在一些正则表达式引擎中,你可以使用命名分组(
(?P<name>expression)
)来为分组指定名称,以便于后续操作。
分组示例:
1 import re 2 3 ''' 4 1. 使用分组匹配日期:使用正则表达式date_pattern 来匹配一个日期字符串。 5 分3组 (\d{4})、(\d{2}) 和 (\d{2}) 分别匹配年、月和日的数字。 6 当正则表达式匹配成功后,通过 match.groups() 获取捕获的分组内容(返回一个元组对象),然后打印输出。 7 ''' 8 date_pattern = r"(\d{4})-(\d{2})-(\d{2})" 9 text = "Today's date is 2023-08-29." 10 match = re.search(date_pattern, text) 11 if match: 12 year, month, day = match.groups() 13 print(f"Year: {year}, Month: {month}, Day: {day}") # Year: 2023, Month: 08, Day: 29 14 15 ''' 16 2. 使用重复操作匹配重复单词: 使用正则表达式 repeated_words_pattern 来匹配重复出现的单词。 17 \b 表示单词边界,(\w+) 匹配一个或多个字母数字字符(单词) 18 \s+ 匹配一个或多个空格 19 然后 \1 引用前面捕获的分组,用于匹配相同的单词。re.findall() 返回所有匹配项。 20 匹配到2组:Hello Hello 和 World World。(\w+)分别是Hello、World 21 ''' 22 23 repeated_words_pattern = r"\b(\w+)\s+\1\b" 24 text = "Hello Hello World World" 25 matches = re.findall(repeated_words_pattern, text) 26 print(matches) # 输出: ['Hello', 'World'] 27 28 ''' 29 3. 使用分组在替换中引用捕获内容: 使用正则表达式 name_pattern 来匹配包含姓和名的字符串。 30 (\w+) 捕获姓和名作为分组。 31 在 re.sub() 中,r"\2 \1" 使用引用捕获的分组,以实现将名字和姓氏互换位置的替换操作。 32 ''' 33 name_pattern = r"Last name: (\w+), First name: (\w+)" 34 text = "Last name: Smith, First name: John" 35 replaced_text = re.sub(name_pattern, r"\2 \1", text) 36 print(replaced_text) # 输出: John Smith
贪婪和非贪婪模式
在正则表达式中,贪婪(greedy)和非贪婪(non-greedy)模式是用来控制匹配重复元素的方式。它们决定了正则表达式在匹配时是尽可能多地匹配还是尽可能少地匹配。让我详细解释这两种模式、最佳实践以及提供示例:
贪婪模式:
贪婪模式是默认模式,表示正则表达式会尽可能多地匹配。在重复元素后面加上 *
、+
、?
、{}
等重复操作时,默认会匹配尽可能多的字符。例如,正则表达式 .*
会匹配任何字符(包括换行符)直到字符串的结尾。
非贪婪模式(非贪心模式、惰性模式):
非贪婪模式表示正则表达式会尽可能少地匹配,以避免过度匹配。在重复元素后面加上?,比如 *?
、+?
、??
、{}?
等操作时,正则表达式会选择尽可能少的匹配。这个模式也被称为惰性模式。
最佳实践:
-
理解贪婪和非贪婪: 在编写正则表达式时,理解贪婪和非贪婪模式的区别很重要。默认情况下,正则表达式是贪婪的。
-
使用非贪婪模式来限制匹配范围: 在可能导致过度匹配的情况下,考虑使用非贪婪模式来限制匹配范围。例如,使用
.*?
来匹配最短的可能字符串。 -
使用限定符明确指定重复次数: 如果你希望精确控制重复的次数,最好使用
{min,max}
这种形式来明确指定。
示例:
1 import re 2 3 # 1. 贪婪模式示例 4 text = "abcgdefg" 5 pattern_greedy = r"a.*g" # 贪婪模式 6 match_greedy = re.search(pattern_greedy, text) 7 print("Greedy:", match_greedy.group()) # 输出: abcdefg 8 9 # 2. 非贪婪模式示例 10 pattern_non_greedy = r"a.*?g" # 非贪婪模式 11 match_non_greedy = re.search(pattern_non_greedy, text) 12 print("Non-Greedy:", match_non_greedy.group()) # 输出: abcg 13 14 # 3.非贪婪模式限制重复次数示例 15 repeated_text = "aaaaaaa" 16 pattern_limit = r"a{2,4}?" # 非贪婪限定重复次数 17 matches_limit = re.findall(pattern_limit, repeated_text) 18 print("Limit:", matches_limit) # 输出: ['aa', 'aa', 'aa'] 19 20 # 4. 贪婪模式限制重复次数示例,只是用于和非贪婪模式对比 21 repeated_text = "aaaaaaa" 22 pattern_limit = r"a{2,4}" # 非贪婪限定重复次数 23 matches_limit = re.findall(pattern_limit, repeated_text) 24 print("Limit:", matches_limit) # 输出: Limit: ['aaaa', 'aaa']
re模块
re
模块是Python中用于处理正则表达式的标准库模块。它提供了一组功能丰富的方法,用于匹配、搜索、替换和处理文本中的模式。下面详细介绍 re
模块的用法、最佳实践、示例以及一些需要注意的坑。
re
模块的常用方法:
re.search(pattern, text)
: 在文本中搜索匹配指定模式的第一个位置。re.match(pattern, text)
: 检查文本是否从开头匹配指定模式。 一定要记住是从头开始匹配,头不匹配就game over了re.findall(pattern, text)
: 查找文本中所有匹配指定模式的字符串,返回一个列表。re.finditer(pattern, text)
: 查找文本中所有匹配指定模式的字符串,返回一个迭代器。re.sub(pattern, replacement, text)
: 将匹配指定模式的部分替换为指定的字符串。
最佳实践:
-
使用原始字符串: 在正则表达式中,使用原始字符串(raw strings)可以避免特殊字符的转义问题。例如,推荐使用
r"\d+"
而不是"\\d+"
。 -
编译正则表达式: 如果你需要多次使用同一个正则表达式,最好先将其编译成一个正则对象,以提高效率。
-
测试和验证: 在编写复杂的正则表达式之前,务必进行测试和验证。在线正则表达式工具(如Regex101、RegExr等)可以帮助你实时验证和调试正则表达式,避免Redos问题。
坑:
-
贪婪匹配可能导致意外结果: 默认情况下,正则表达式是贪婪的。在匹配时可能会选择匹配更多的字符。要使用非贪婪模式
*?
、+?
等,以避免意外的匹配结果。 -
转义字符的注意事项: 正则表达式中的某些字符具有特殊意义,如
.
,*
,+
等。如果想匹配这些字符本身,需要使用转义字符\
。例如,要匹配.
,需要使用\.
。 -
性能问题: 复杂的正则表达式可能会导致性能问题,尤其是在长文本上进行多次匹配。尽量避免在大型文本上使用复杂的正则表达式。
-
不同引擎的差异: 不同编程语言和工具中的正则表达式引擎可能存在差异,某些特性在不同环境下可能表现不同。要注意跨平台和跨语言的差异。
示例
匹配单个字符-点-示例:
1 import re 2 3 # . 匹配任意1个字符(除了\n) 4 # 匹配数据 5 result = re.match("Allen.", "Allen\n") # . 不能匹配换行符\n 6 7 # 获取数据 8 if result: 9 info = result.group() 10 print(f"匹配到数据:{info}") 11 else: 12 print("没有匹配到") 13 14 result = re.match("Allen.", "Allen88") 15 16 # 获取数据 17 if result: 18 info = result.group() 19 print(f"匹配到数据:{info}") 20 else: 21 print("没有匹配到")
输出:
没有匹配到 匹配到数据:Allen8
匹配单个字符-[]-示例:
1 import re 2 3 # [ ] 匹配[ ]中列举的字符 4 # 匹配数据 5 result = re.match("Allen[123abc]", "Allen-") 6 7 # 获取数据 8 if result: 9 info = result.group() 10 print(f"匹配到数据:{info}") 11 else: 12 print("没有匹配到") 13 14 15 result = re.match("Allen[123abc]", "Allen3") 16 17 # 获取数据 18 if result: 19 info = result.group() 20 print(f"匹配到数据:{info}") 21 else: 22 print("没有匹配到")
输出:
没有匹配到 匹配到数据:Allen3
匹配单个字符-数字-非数字-
1 import re 2 3 # \d 匹配数字,即0-9 => [0123456789] => [0-9] 4 # 匹配数据 5 result = re.match("Allen\d", "Allen5") 6 7 # 获取数据 8 if result: 9 info = result.group() 10 print(f"匹配到数据:{info}") 11 else: 12 print("没有匹配到") 13 14 # \D 匹配非数字,即不是数字 15 # 匹配数据 16 result = re.match("Allen\D", "Allen-") 17 18 # 获取数据 19 if result: 20 info = result.group() 21 print(f"匹配到数据:{info}") 22 else: 23 print("没有匹配到")
输出:
匹配到数据:Allen5 匹配到数据:Allen-
匹配单个字符-空白-非空白-示例:
1 import re 2 3 # \s 匹配空白,即空格,tab键 4 # 匹配数据 5 result = re.match("Allen\s111", "Allen\t111") 6 7 # 获取数据 8 if result: 9 info = result.group() 10 print(f"匹配到数据:{info}") 11 else: 12 print("没有匹配到") 13 14 # \S 匹配非空白 15 # 匹配数据 16 result = re.match("Allen\S", "Allen\t") 17 18 # 获取数据 19 if result: 20 info = result.group() 21 print(info) 22 else: 23 print("没有匹配到")
输出:
匹配到数据:Allen 111 没有匹配到
单个字符匹配-非特殊字符-特殊字符-示例:
import re # \w 匹配非特殊字符,即a-z, A-Z, 0-9, _, 汉字 # 匹配数据 result = re.match("Allen\w", "Allen!") # 获取数据 if result: info = result.group() print(f"匹配到数据:{info}") else: print("没有匹配到") # 匹配数据 result = re.match("Allen\w", "Allen_") # 获取数据 if result: info = result.group() print(f"匹配到数据:{info}") else: print("没有匹配到") # \W 匹配特殊字符,即非字母, 非数字, 非_, 非汉字 # 匹配数据 result = re.match("Allen\W", "Allen0") # 获取数据 if result: info = result.group() print(f"匹配到数据:{info}") else: print("没有匹配到")
输出:
没有匹配到 匹配到数据:Allen_ 没有匹配到
匹配多个字符-*-示例
1 import re 2 3 # * 匹配前一个字符出现0次或者无限次,即可有可无 4 # 匹配数据 5 result = re.match("Allen\d*Allen", "AllenAllen") 6 7 # 获取数据 8 if result: 9 info = result.group() 10 print(f"Allen\d*Allen 匹配到数据:{info}") 11 else: 12 print("没有匹配到")
输出:Allen\d*Allen 匹配到数据:AllenAllen
匹配多个字符-+-示例:
1 import re 2 3 # + 匹配前一个字符出现1次或者无限次,即至少有1次 4 # 匹配数据 5 result = re.match("Allen\d+Allen", "Allen12Allen") 6 7 # 获取数据 8 if result: 9 info = result.group() 10 print(f"匹配到数据:{info}") 11 else: 12 print("没有匹配到")
输出:匹配到数据:Allen12Allen
匹配多个字符1次或0次-示例:
1 import re 2 3 # ? 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 4 result = re.match("Allen\d?Allen", "AllenAllen") 5 6 # 获取数据 7 if result: 8 info = result.group() 9 print(f"匹配到数据:{info}") # n\d 匹配到了说明n出现了1次 10 else: 11 print("没有匹配到") 12 13 result = re.match("Allen\d?Allen", "AllennAllen") # 此处Allenn出现了2次,并且match是从头开始一一匹配的 14 15 # 获取数据 16 if result: 17 info = result.group() 18 print(f"匹配到数据:{info}") 19 else: 20 print("没有匹配到")
输出:
匹配到数据:AllenAllen 没有匹配到
匹配多个字符-出现m次-示例:
import re # {m} 匹配前一个字符出现m次 result = re.match("Allen\d{2}Allen", "Allen12Allen") # 此处前面是数字\d 并且要求出现2次 # 获取数据 if result: info = result.group() print(f"匹配到数据:{info}") else: print("没有匹配到")
输出:匹配到数据:Allen12Allen
匹配多个字符-出现m-n次-示例:
1 import re 2 3 # {m,n} 匹配前一个字符出现从m到n次 4 result = re.match("Allen\d{2,5}Allen", "Allen12112312312312312Allen") # 匹配\d即数字出现2-5次.因此没有匹配到 5 6 # 获取数据 7 if result: # 匹配result不为None,匹配不到为None 8 info = result.group() 9 print(f"匹配到数据:{info}") 10 else: 11 print("没有匹配到") 12 13 result = re.match("Allen\d{2,5}Allen", "Allen1211Allen") 14 15 # 获取数据 16 if result: 17 info = result.group() 18 print(f"匹配到数据:{info}") 19 else: 20 print("没有匹配到")
输出:
没有匹配到 匹配到数据:Allen1211Allen
匹配开头-示例:
import re # ^ 匹配字符串开头 # 匹配数据 result = re.match("^\dAllen", "22Allen") # 获取数据 if result: info = result.group() print(f"匹配到数据:{info}") else: print("没有匹配到") result = re.match("^\d{2}Allen", "22Allen") # 获取数据 if result: info = result.group() print(f"匹配到数据:{info}") else: print("没有匹配到")
输出:
没有匹配到 匹配到数据:22Allen
1 import re 2 3 # 以数字为开头的字符串,后面跟除\n任务字符0-n个 4 result = re.match("^\d.*", "2Allen") 5 6 # 获取数据 7 if result: 8 info = result.group() 9 print(f"匹配到数据:{info}") 10 else: 11 print("没有匹配到")
输出:匹配到数据:2Allen
匹配以数字开头和结尾-示例:
1 import re 2 3 # $ 匹配数字结尾未匹配 4 result = re.match(".*\d$", "Allen") # 以数字结尾 5 6 # 获取数据 7 if result: 8 info = result.group() 9 print(f"匹配到数据:{info}") 10 else: 11 print("没有匹配到") 12 13 # 匹配以数字为开头以数字为结尾 14 result = re.match("^\d.*\d$", "11Allen22") 15 16 # 获取数据 17 if result: 18 info = result.group() 19 print(f"匹配到数据:{info}") 20 else: 21 print("没有匹配到")
输出:
没有匹配到 匹配到数据:11Allen22
import re # [^指定字符] 匹配以数字开头并除了指定字符以外的所有字符结尾 result = re.match("^\d.*[^4]$", "11Alllen@") # 获取数据 if result: info = result.group() print(f"匹配到数据:{info}") else: print("没有匹配到")
输出:匹配到数据:11Alllen@
match方法示例:
1 # 1 导入re模块 import re 2 import re 3 4 # 2 match匹配数据: 从头开始匹配 5 # match(正则表达式,要匹配的字符串) 6 # result = re.match(正则表达式,要匹配的字符串) 7 result = re.match("All", "Allen") 8 print(type(result)) # <class 're.Match'> 9 # 3 group提取数据: result.group() 10 info = result.group() 11 print(info) 12 13 # Allen的头是A,ll的头是l,因此从头开始匹配,没有匹配成功。 14 result = re.match("ll", "Allen") 15 print(type(result)) # <class 'NoneType'> 16 17 if result: 18 # 3 group提取数据 19 # result.group() 20 info = result.group() 21 print(info) 22 else: 23 print("match没有匹配到")
输出:
<class 're.Match'> All <class 'NoneType'> match没有匹配到
综合示例:
1 import re 2 3 # 1. 使用 re.search 查找匹配 4 pattern = r"apple" 5 text = "I have an apple and a other apple ." 6 match = re.search(pattern, text) # search方法只匹配符合要求的第1个即可 7 if match: 8 print("Match found:", match.group()) # 输出: Match found: apple 9 10 # 2. 使用 re.findall 查找所有匹配 11 pattern_numbers = r"\d+" 12 text_numbers = "There are 123 apples and 456 bananas." 13 numbers = re.findall(pattern_numbers, text_numbers) # 返回一个元组 14 print(numbers) # 输出: ['123', '456'] # 特别说明正则表达式是处理字符串的、处理字符串的、处理字符串的 15 16 ''' 17 3.使用 re.sub 替换匹配 18 1. pattern_replace 是一个正则表达式模式,其中的 \b 表示单词边界。这个模式将匹配一个单独的单词 "apple",而不会匹配包含 "apple" 的其他词,因为它会确保 "apple" 前后都不是字母数字字符。 19 2. text_replace 是一个包含文本的字符串,其中包含了 "apple" 和 "pineapple" 两个单词。 20 3. re.sub(pattern_replace, "fruit", text_replace) 使用 re 模块的 sub 函数来将匹配的单词替换为 "fruit"。由于我们使用了 \b,所以只会匹配完整的 "apple" 单词,而不会匹配 "pineapple" 中的 "apple" 部分。 21 4. 最终结果是将 "apple" 替换为 "fruit",输出为 "I have an fruit and a pineapple."。 22 ''' 23 pattern_replace = r"\bapple\b" 24 text_replace = "I have an apple and a pineapple." 25 replaced_text = re.sub(pattern_replace, "fruit", text_replace) 26 print(replaced_text) # 输出: I have an fruit and a pineapple.
元字符\b
则表达式中的 \b
是一个称为单词边界(word boundary)的元字符。它匹配一个位置,该位置位于一个单词字符(字母、数字、下划线)和一个非单词字符之间,或者位于字符串的开头或结尾。\b
不会消耗任何字符,它只匹配一个位置。
最佳实践:
-
用于限定完整单词:
\b
通常用于确保匹配整个单词,而不是单词的一部分。例如,使用\bapple\b
可以确保只匹配 "apple" 这个完整的单词,而不是 "pineapple" 中的一部分。 -
注意边界情况:
\b
在字符串开头或结尾也会匹配,所以在某些情况下需要注意边界情况,以避免误匹配。 - 结合其他模式使用:
\b
通常会与其他模式一起使用,以便进行更精确的匹配。例如,结合\b
和\w+
可以匹配一个完整的单词。
坑:
\b
的位置:\b
匹配的是位置,而不是字符。它确保匹配发生在单词字符和非单词字符之间,而不会匹配实际的字符。
示例:
1 import re 2 3 # 匹配完整的单词 4 pattern = r"\bapple\b" 5 text = "I have an apple and a pineapple." 6 matches = re.findall(pattern, text) 7 print(matches) # 输出: ['apple'] 8 9 # 使用 \b 匹配完整单词 10 pattern_words = r"\b\w+\b" 11 text_words = "Hello, world!" 12 words = re.findall(pattern_words, text_words) 13 print(words) # 输出: ['Hello', 'world']
使用了 \b
来匹配完整的单词。第一个例子中,只有 "apple" 被匹配,因为它是一个完整的单词。在第二个例子中,使用 \b\w+\b
匹配了文本中的单词。
注意,\b
只匹配位置,不涉及字符匹配。eg: r"\b\w+\b"就是匹配1到n个字符的单词。记住是单词、单词、单词
标签:info,匹配,正则表达式,re,result,print,match From: https://www.cnblogs.com/allenxx/p/17668498.html