|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
正则表达式(Regular Expression,简称regex)是一种强大的文本处理工具,它提供了一种灵活的方式来匹配、搜索、替换和验证文本。在Python编程中,正则表达式是处理字符串的必备技能,无论是简单的数据验证还是复杂的文本解析,正则表达式都能大大提高开发效率。
正则表达式最初由数学家Stephen Kleene在1950年代提出,后来被广泛应用于各种编程语言和工具中。在Python中,通过内置的re模块,我们可以充分利用正则表达式的强大功能。掌握正则表达式不仅能够简化代码,还能解决许多复杂的文本处理问题。
本文将从正则表达式的基础知识开始,逐步深入到高级应用,帮助读者全面掌握正则表达式在Python字符串处理中的应用,从简单匹配到复杂文本解析,提升开发效率。
正则表达式基础
什么是正则表达式
正则表达式是一种特殊的字符序列,它使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。正则表达式由普通字符(如字母、数字)和特殊字符(称为”元字符”)组成,通过这些字符的组合,可以构建出复杂的匹配模式。
基本语法
正则表达式的基本语法包括:
1. 普通字符:如字母、数字、下划线等,它们匹配自身。例如:a匹配字符”a”,1匹配数字”1”。
2. 例如:a匹配字符”a”,1匹配数字”1”。
3. 元字符:具有特殊含义的字符,如.、*、+、?、^、$、\、|、()、[]、{}等。.:匹配除换行符外的任意单个字符。*:匹配前面的子表达式零次或多次。+:匹配前面的子表达式一次或多次。?:匹配前面的子表达式零次或一次。^:匹配字符串的开始位置。$:匹配字符串的结束位置。\:转义字符,用于匹配特殊字符本身。|:选择符,匹配|左右任意一个表达式。():分组,将括号内的表达式作为一个整体。[]:字符类,匹配方括号中的任意一个字符。{}:量词,指定匹配次数。
4. .:匹配除换行符外的任意单个字符。
5. *:匹配前面的子表达式零次或多次。
6. +:匹配前面的子表达式一次或多次。
7. ?:匹配前面的子表达式零次或一次。
8. ^:匹配字符串的开始位置。
9. $:匹配字符串的结束位置。
10. \:转义字符,用于匹配特殊字符本身。
11. |:选择符,匹配|左右任意一个表达式。
12. ():分组,将括号内的表达式作为一个整体。
13. []:字符类,匹配方括号中的任意一个字符。
14. {}:量词,指定匹配次数。
15. 字符类:使用方括号[]定义,匹配其中的任意一个字符。[abc]:匹配”a”、”b”或”c”。[a-z]:匹配任意小写字母。[A-Z]:匹配任意大写字母。[0-9]:匹配任意数字。[a-zA-Z0-9]:匹配任意字母或数字。
16. [abc]:匹配”a”、”b”或”c”。
17. [a-z]:匹配任意小写字母。
18. [A-Z]:匹配任意大写字母。
19. [0-9]:匹配任意数字。
20. [a-zA-Z0-9]:匹配任意字母或数字。
21. 预定义字符类:\d:匹配任意数字,相当于[0-9]。\D:匹配任意非数字字符,相当于[^0-9]。\w:匹配任意单词字符(字母、数字、下划线),相当于[a-zA-Z0-9_]。\W:匹配任意非单词字符,相当于[^a-zA-Z0-9_]。\s:匹配任意空白字符(空格、制表符、换行符等)。\S:匹配任意非空白字符。
22. \d:匹配任意数字,相当于[0-9]。
23. \D:匹配任意非数字字符,相当于[^0-9]。
24. \w:匹配任意单词字符(字母、数字、下划线),相当于[a-zA-Z0-9_]。
25. \W:匹配任意非单词字符,相当于[^a-zA-Z0-9_]。
26. \s:匹配任意空白字符(空格、制表符、换行符等)。
27. \S:匹配任意非空白字符。
28. 量词:指定匹配的次数。{n}:匹配恰好n次。{n,}:匹配至少n次。{n,m}:匹配至少n次,至多m次。*:匹配0次或多次,相当于{0,}。+:匹配1次或多次,相当于{1,}。?:匹配0次或1次,相当于{0,1}。
29. {n}:匹配恰好n次。
30. {n,}:匹配至少n次。
31. {n,m}:匹配至少n次,至多m次。
32. *:匹配0次或多次,相当于{0,}。
33. +:匹配1次或多次,相当于{1,}。
34. ?:匹配0次或1次,相当于{0,1}。
普通字符:如字母、数字、下划线等,它们匹配自身。
• 例如:a匹配字符”a”,1匹配数字”1”。
元字符:具有特殊含义的字符,如.、*、+、?、^、$、\、|、()、[]、{}等。
• .:匹配除换行符外的任意单个字符。
• *:匹配前面的子表达式零次或多次。
• +:匹配前面的子表达式一次或多次。
• ?:匹配前面的子表达式零次或一次。
• ^:匹配字符串的开始位置。
• $:匹配字符串的结束位置。
• \:转义字符,用于匹配特殊字符本身。
• |:选择符,匹配|左右任意一个表达式。
• ():分组,将括号内的表达式作为一个整体。
• []:字符类,匹配方括号中的任意一个字符。
• {}:量词,指定匹配次数。
字符类:使用方括号[]定义,匹配其中的任意一个字符。
• [abc]:匹配”a”、”b”或”c”。
• [a-z]:匹配任意小写字母。
• [A-Z]:匹配任意大写字母。
• [0-9]:匹配任意数字。
• [a-zA-Z0-9]:匹配任意字母或数字。
预定义字符类:
• \d:匹配任意数字,相当于[0-9]。
• \D:匹配任意非数字字符,相当于[^0-9]。
• \w:匹配任意单词字符(字母、数字、下划线),相当于[a-zA-Z0-9_]。
• \W:匹配任意非单词字符,相当于[^a-zA-Z0-9_]。
• \s:匹配任意空白字符(空格、制表符、换行符等)。
• \S:匹配任意非空白字符。
量词:指定匹配的次数。
• {n}:匹配恰好n次。
• {n,}:匹配至少n次。
• {n,m}:匹配至少n次,至多m次。
• *:匹配0次或多次,相当于{0,}。
• +:匹配1次或多次,相当于{1,}。
• ?:匹配0次或1次,相当于{0,1}。
Python中的re模块
Python中的re模块提供了正则表达式的支持,它包含了一系列函数用于正则表达式的匹配、搜索、替换和分割等操作。在使用re模块之前,需要先导入它:
主要函数和方法
1. re.compile(pattern, flags=0):编译正则表达式,返回一个正则表达式对象。编译后的正则表达式对象可以重复使用,提高效率。flags参数用于指定匹配模式,如re.IGNORECASE(忽略大小写)、re.MULTILINE(多行模式)等。
2. 编译后的正则表达式对象可以重复使用,提高效率。
3. flags参数用于指定匹配模式,如re.IGNORECASE(忽略大小写)、re.MULTILINE(多行模式)等。
4. re.match(pattern, string, flags=0):尝试从字符串的起始位置匹配一个模式。如果匹配成功,返回一个匹配对象;否则返回None。只匹配字符串的开始部分。
5. 如果匹配成功,返回一个匹配对象;否则返回None。
6. 只匹配字符串的开始部分。
7. re.search(pattern, string, flags=0):扫描整个字符串,返回第一个成功的匹配。如果匹配成功,返回一个匹配对象;否则返回None。与match()不同,search()会扫描整个字符串。
8. 如果匹配成功,返回一个匹配对象;否则返回None。
9. 与match()不同,search()会扫描整个字符串。
10. re.findall(pattern, string, flags=0):查找字符串中所有匹配的子串,返回一个列表。如果没有匹配,返回空列表。
11. 如果没有匹配,返回空列表。
12. re.finditer(pattern, string, flags=0):查找字符串中所有匹配的子串,返回一个迭代器。每个迭代元素是一个匹配对象。
13. 每个迭代元素是一个匹配对象。
14. re.sub(pattern, repl, string, count=0, flags=0):替换字符串中所有匹配的子串。repl可以是字符串或函数。count参数指定最大替换次数,默认为0(替换所有匹配)。
15. repl可以是字符串或函数。
16. count参数指定最大替换次数,默认为0(替换所有匹配)。
17. re.split(pattern, string, maxsplit=0, flags=0):根据匹配的子串分割字符串。maxsplit参数指定最大分割次数,默认为0(分割所有匹配)。
18. maxsplit参数指定最大分割次数,默认为0(分割所有匹配)。
re.compile(pattern, flags=0):编译正则表达式,返回一个正则表达式对象。
• 编译后的正则表达式对象可以重复使用,提高效率。
• flags参数用于指定匹配模式,如re.IGNORECASE(忽略大小写)、re.MULTILINE(多行模式)等。
re.match(pattern, string, flags=0):尝试从字符串的起始位置匹配一个模式。
• 如果匹配成功,返回一个匹配对象;否则返回None。
• 只匹配字符串的开始部分。
re.search(pattern, string, flags=0):扫描整个字符串,返回第一个成功的匹配。
• 如果匹配成功,返回一个匹配对象;否则返回None。
• 与match()不同,search()会扫描整个字符串。
re.findall(pattern, string, flags=0):查找字符串中所有匹配的子串,返回一个列表。
• 如果没有匹配,返回空列表。
re.finditer(pattern, string, flags=0):查找字符串中所有匹配的子串,返回一个迭代器。
• 每个迭代元素是一个匹配对象。
re.sub(pattern, repl, string, count=0, flags=0):替换字符串中所有匹配的子串。
• repl可以是字符串或函数。
• count参数指定最大替换次数,默认为0(替换所有匹配)。
re.split(pattern, string, maxsplit=0, flags=0):根据匹配的子串分割字符串。
• maxsplit参数指定最大分割次数,默认为0(分割所有匹配)。
匹配对象
当match()或search()函数成功匹配时,它们会返回一个匹配对象(Match对象)。匹配对象提供了以下方法:
1. group([group1, …]):返回一个或多个匹配的子组。group()或group(0)返回整个匹配。group(1)、group(2)等返回第1、第2个捕获组的内容。
2. group()或group(0)返回整个匹配。
3. group(1)、group(2)等返回第1、第2个捕获组的内容。
4. groups(default=None):返回所有子组的元组。如果没有匹配的子组,返回空元组。
5. 如果没有匹配的子组,返回空元组。
6. groupdict(default=None):返回所有命名子组的字典。键是子组名,值是匹配的内容。
7. 键是子组名,值是匹配的内容。
8. start([group]):返回匹配的起始位置。如果指定了group,则返回指定组的起始位置。
9. 如果指定了group,则返回指定组的起始位置。
10. end([group]):返回匹配的结束位置。如果指定了group,则返回指定组的结束位置。
11. 如果指定了group,则返回指定组的结束位置。
12. span([group]):返回一个包含匹配(start, end)位置的元组。如果指定了group,则返回指定组的(start, end)位置。
13. 如果指定了group,则返回指定组的(start, end)位置。
group([group1, …]):返回一个或多个匹配的子组。
• group()或group(0)返回整个匹配。
• group(1)、group(2)等返回第1、第2个捕获组的内容。
groups(default=None):返回所有子组的元组。
• 如果没有匹配的子组,返回空元组。
groupdict(default=None):返回所有命名子组的字典。
• 键是子组名,值是匹配的内容。
start([group]):返回匹配的起始位置。
• 如果指定了group,则返回指定组的起始位置。
end([group]):返回匹配的结束位置。
• 如果指定了group,则返回指定组的结束位置。
span([group]):返回一个包含匹配(start, end)位置的元组。
• 如果指定了group,则返回指定组的(start, end)位置。
编译标志
re模块提供了一些编译标志,用于修改正则表达式的行为:
1. re.IGNORECASE或re.I:忽略大小写。例如:re.findall(r'[a-z]', 'Hello World', re.I)会匹配所有字母,不区分大小写。
2. 例如:re.findall(r'[a-z]', 'Hello World', re.I)会匹配所有字母,不区分大小写。
3. re.MULTILINE或re.M:多行模式。使^和$匹配每行的开始和结束,而不仅仅是整个字符串的开始和结束。例如:re.findall(r'^\w+', 'Hello\nWorld', re.M)会匹配”Hello”和”World”。
4. 使^和$匹配每行的开始和结束,而不仅仅是整个字符串的开始和结束。
5. 例如:re.findall(r'^\w+', 'Hello\nWorld', re.M)会匹配”Hello”和”World”。
6. re.DOTALL或re.S:使.匹配包括换行符在内的所有字符。默认情况下,.不匹配换行符。例如:re.search(r'.+', 'Hello\nWorld', re.S)会匹配整个字符串”Hello\nWorld”。
7. 默认情况下,.不匹配换行符。
8. 例如:re.search(r'.+', 'Hello\nWorld', re.S)会匹配整个字符串”Hello\nWorld”。
9. - re.VERBOSE或re.X:详细模式。允许正则表达式分成多行,并添加注释,提高可读性。例如:
- “`python
- pattern = r”“”
- \d{3} # 匹配3位数字(区号)# 匹配连字符
- \d{4} # 匹配4位数字(前缀)# 匹配连字符
- \d{4} # 匹配4位数字(后缀)
- “””
- re.compile(pattern, re.VERBOSE)”`
复制代码 10. 允许正则表达式分成多行,并添加注释,提高可读性。
11. - 例如:
- “`python
- pattern = r”“”
- \d{3} # 匹配3位数字(区号)# 匹配连字符
- \d{4} # 匹配4位数字(前缀)# 匹配连字符
- \d{4} # 匹配4位数字(后缀)
- “””
- re.compile(pattern, re.VERBOSE)”`
复制代码 12. - # 匹配连字符
- \d{4} # 匹配4位数字(前缀)
复制代码 13. - # 匹配连字符
- \d{4} # 匹配4位数字(后缀)
- “””
- re.compile(pattern, re.VERBOSE)
复制代码 14. re.ASCII或re.A:使\w、\W、\b、\B、\d、\D、\s和\S只匹配ASCII字符,而不是Unicode字符。在Python 3中,默认情况下这些字符类匹配Unicode字符。
15. 在Python 3中,默认情况下这些字符类匹配Unicode字符。
16. re.UNICODE或re.U:使\w、\W、\b、\B、\d、\D、\s和\S匹配Unicode字符。在Python 3中,这是默认行为。
17. 在Python 3中,这是默认行为。
re.IGNORECASE或re.I:忽略大小写。
• 例如:re.findall(r'[a-z]', 'Hello World', re.I)会匹配所有字母,不区分大小写。
re.MULTILINE或re.M:多行模式。
• 使^和$匹配每行的开始和结束,而不仅仅是整个字符串的开始和结束。
• 例如:re.findall(r'^\w+', 'Hello\nWorld', re.M)会匹配”Hello”和”World”。
re.DOTALL或re.S:使.匹配包括换行符在内的所有字符。
• 默认情况下,.不匹配换行符。
• 例如:re.search(r'.+', 'Hello\nWorld', re.S)会匹配整个字符串”Hello\nWorld”。
re.VERBOSE或re.X:详细模式。
• 允许正则表达式分成多行,并添加注释,提高可读性。
• - 例如:
- “`python
- pattern = r”“”
- \d{3} # 匹配3位数字(区号)# 匹配连字符
- \d{4} # 匹配4位数字(前缀)# 匹配连字符
- \d{4} # 匹配4位数字(后缀)
- “””
- re.compile(pattern, re.VERBOSE)”`
复制代码 • - # 匹配连字符
- \d{4} # 匹配4位数字(前缀)
复制代码 • - # 匹配连字符
- \d{4} # 匹配4位数字(后缀)
- “””
- re.compile(pattern, re.VERBOSE)
复制代码
• - # 匹配连字符
- \d{4} # 匹配4位数字(前缀)
复制代码 • - # 匹配连字符
- \d{4} # 匹配4位数字(后缀)
- “””
- re.compile(pattern, re.VERBOSE)
复制代码
re.ASCII或re.A:使\w、\W、\b、\B、\d、\D、\s和\S只匹配ASCII字符,而不是Unicode字符。
• 在Python 3中,默认情况下这些字符类匹配Unicode字符。
re.UNICODE或re.U:使\w、\W、\b、\B、\d、\D、\s和\S匹配Unicode字符。
• 在Python 3中,这是默认行为。
简单匹配
字符匹配
最基本的正则表达式是字符匹配,它匹配指定的字符。
- import re
- # 匹配单个字符
- result = re.search(r'a', 'apple')
- print(result.group()) # 输出: a
- # 匹配多个字符
- result = re.search(r'apple', 'I like apple')
- print(result.group()) # 输出: apple
复制代码
字符类
字符类使用方括号[]定义,匹配其中的任意一个字符。
- import re
- # 匹配任意一个元音字母
- result = re.search(r'[aeiou]', 'hello')
- print(result.group()) # 输出: e
- # 匹配任意一个数字
- result = re.search(r'[0-9]', 'Python 3.9')
- print(result.group()) # 输出: 3
- # 匹配任意一个非元音字母
- result = re.search(r'[^aeiou]', 'hello')
- print(result.group()) # 输出: h
复制代码
预定义字符类
预定义字符类提供了简写方式,用于匹配常见的字符类型。
- import re
- # 匹配任意数字
- result = re.search(r'\d', 'Python 3.9')
- print(result.group()) # 输出: 3
- # 匹配任意非数字字符
- result = re.search(r'\D', '123abc')
- print(result.group()) # 输出: a
- # 匹配任意单词字符
- result = re.search(r'\w', 'hello world')
- print(result.group()) # 输出: h
- # 匹配任意非单词字符
- result = re.search(r'\W', 'hello world')
- print(result.group()) # 输出: (空格)
- # 匹配任意空白字符
- result = re.search(r'\s', 'hello world')
- print(result.group()) # 输出: (空格)
- # 匹配任意非空白字符
- result = re.search(r'\S', ' hello')
- print(result.group()) # 输出: h
复制代码
量词
量词用于指定匹配的次数。
- import re
- # 匹配0次或多次
- result = re.search(r'ab*c', 'ac')
- print(result.group()) # 输出: ac
- result = re.search(r'ab*c', 'abc')
- print(result.group()) # 输出: abc
- result = re.search(r'ab*c', 'abbbc')
- print(result.group()) # 输出: abbbc
- # 匹配1次或多次
- result = re.search(r'ab+c', 'ac')
- print(result) # 输出: None
- result = re.search(r'ab+c', 'abc')
- print(result.group()) # 输出: abc
- result = re.search(r'ab+c', 'abbbc')
- print(result.group()) # 输出: abbbc
- # 匹配0次或1次
- result = re.search(r'ab?c', 'ac')
- print(result.group()) # 输出: ac
- result = re.search(r'ab?c', 'abc')
- print(result.group()) # 输出: abc
- result = re.search(r'ab?c', 'abbc')
- print(result) # 输出: None
- # 指定匹配次数
- result = re.search(r'a{3}', 'aaa')
- print(result.group()) # 输出: aaa
- result = re.search(r'a{3}', 'aa')
- print(result) # 输出: None
- result = re.search(r'a{2,4}', 'aa')
- print(result.group()) # 输出: aa
- result = re.search(r'a{2,4}', 'aaa')
- print(result.group()) # 输出: aaa
- result = re.search(r'a{2,4}', 'aaaaa')
- print(result.group()) # 输出: aaaa
- result = re.search(r'a{2,}', 'aa')
- print(result.group()) # 输出: aa
- result = re.search(r'a{2,}', 'aaaaa')
- print(result.group()) # 输出: aaaaa
复制代码
边界匹配
边界匹配用于指定匹配的位置。
- import re
- # 匹配字符串的开始
- result = re.search(r'^hello', 'hello world')
- print(result.group()) # 输出: hello
- result = re.search(r'^hello', 'world hello')
- print(result) # 输出: None
- # 匹配字符串的结束
- result = re.search(r'world$', 'hello world')
- print(result.group()) # 输出: world
- result = re.search(r'world$', 'world hello')
- print(result) # 输出: None
- # 匹配单词边界
- result = re.search(r'\bhello\b', 'hello world')
- print(result.group()) # 输出: hello
- result = re.search(r'\bhello\b', 'helloworld')
- print(result) # 输出: None
- result = re.search(r'\Bhello\B', 'helloworld')
- print(result) # 输出: hello
复制代码
进阶匹配
分组和捕获
分组使用圆括号()实现,它可以将多个字符作为一个整体,并且可以捕获匹配的内容。
- import re
- # 分组
- result = re.search(r'(ab)+', 'ababab')
- print(result.group()) # 输出: ababab
- print(result.group(1)) # 输出: ab
- # 捕获组
- result = re.search(r'(\d{4})-(\d{2})-(\d{2})', '2023-05-15')
- print(result.group()) # 输出: 2023-05-15
- print(result.group(1)) # 输出: 2023
- print(result.group(2)) # 输出: 05
- print(result.group(3)) # 输出: 15
- print(result.groups()) # 输出: ('2023', '05', '15')
复制代码
非捕获分组
有时候我们只需要分组的功能,而不需要捕获匹配的内容,这时可以使用非捕获分组(?:...)。
- import re
- # 非捕获分组
- result = re.search(r'(?:ab)+', 'ababab')
- print(result.group()) # 输出: ababab
- print(result.groups()) # 输出: ()
- # 比较捕获分组和非捕获分组
- result = re.search(r'(ab)+', 'ababab')
- print(result.groups()) # 输出: ('ab',)
- result = re.search(r'(?:ab)+', 'ababab')
- print(result.groups()) # 输出: ()
复制代码
命名分组
命名分组使用(?P<name>...)语法,可以为分组指定一个名称,便于后续引用。
- import re
- # 命名分组
- result = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', '2023-05-15')
- print(result.group()) # 输出: 2023-05-15
- print(result.group('year')) # 输出: 2023
- print(result.group('month')) # 输出: 05
- print(result.group('day')) # 输出: 15
- print(result.groupdict()) # 输出: {'year': '2023', 'month': '05', 'day': '15'}
复制代码
反向引用
反向引用允许我们引用前面捕获组匹配的内容,使用\1、\2等语法。
- import re
- # 反向引用
- result = re.search(r'(\w+)\s+\1', 'hello hello')
- print(result.group()) # 输出: hello hello
- result = re.search(r'(\w+)\s+\1', 'hello world')
- print(result) # 输出: None
- # 使用命名分组的反向引用
- result = re.search(r'(?P<word>\w+)\s+(?P=word)', 'hello hello')
- print(result.group()) # 输出: hello hello
复制代码
选择符
选择符|用于匹配多个可能的模式之一。
- import re
- # 选择符
- result = re.search(r'cat|dog', 'I have a cat')
- print(result.group()) # 输出: cat
- result = re.search(r'cat|dog', 'I have a dog')
- print(result.group()) # 输出: dog
- # 与分组结合使用
- result = re.search(r'I have a (cat|dog)', 'I have a cat')
- print(result.group()) # 输出: I have a cat
- print(result.group(1)) # 输出: cat
复制代码
贪婪与非贪婪匹配
默认情况下,量词是贪婪的,它们会尽可能多地匹配字符。非贪婪匹配(也称为懒惰匹配)使用?后缀,会尽可能少地匹配字符。
- import re
- # 贪婪匹配
- result = re.search(r'<.*>', '<div>hello</div>')
- print(result.group()) # 输出: <div>hello</div>
- # 非贪婪匹配
- result = re.search(r'<.*?>', '<div>hello</div>')
- print(result.group()) # 输出: <div>
- # 贪婪与非贪婪的对比
- result = re.search(r'a+', 'aaa')
- print(result.group()) # 输出: aaa
- result = re.search(r'a+?', 'aaa')
- print(result.group()) # 输出: a
- result = re.search(r'a{2,4}', 'aaaaa')
- print(result.group()) # 输出: aaaa
- result = re.search(r'a{2,4}?', 'aaaaa')
- print(result.group()) # 输出: aa
复制代码
复杂文本解析
前瞻和后顾
前瞻和后顾是零宽断言,它们匹配特定的位置,但不消耗字符。
1. 正向先行断言(?=...):匹配后面是特定模式的位置。
2. 负向先行断言(?!...):匹配后面不是特定模式的位置。
3. 正向后行断言(?<=...):匹配前面是特定模式的位置。
4. 负向后行断言(?<!...):匹配前面不是特定模式的位置。
- import re
- # 正向先行断言
- result = re.search(r'\w+(?=\s+apple)', 'I like apple')
- print(result.group()) # 输出: like
- # 负向先行断言
- result = re.search(r'\w+(?!\s+apple)', 'I like apple')
- print(result.group()) # 输出: I
- # 正向后行断言
- result = re.search(r'(?<=I\s+)\w+', 'I like apple')
- print(result.group()) # 输出: like
- # 负向后行断言
- result = re.search(r'(?<!I\s+)\w+', 'I like apple')
- print(result.group()) # 输出: apple
复制代码
条件匹配
条件匹配使用(?(id/name)yes-pattern|no-pattern)语法,根据指定的组是否存在来选择匹配的模式。
- import re
- # 条件匹配
- pattern = r'(\d+)?(?(1)\.\d+|\.\d+)'
- result = re.search(pattern, '123.456')
- print(result.group()) # 输出: 123.456
- result = re.search(pattern, '.456')
- print(result.group()) # 输出: .456
- result = re.search(pattern, '123')
- print(result) # 输出: None
复制代码
回溯控制
回溯控制使用(?>...)语法,也称为原子组,一旦匹配,就不会回溯。
- import re
- # 回溯控制
- result = re.search(r'(?>a+)b', 'aaab')
- print(result.group()) # 输出: aaab
- result = re.search(r'(?>a+)a', 'aaa')
- print(result) # 输出: None
- # 对比普通分组
- result = re.search(r'(a+)a', 'aaa')
- print(result.group()) # 输出: aaa
复制代码
实际应用案例
数据提取
正则表达式常用于从文本中提取特定格式的数据。
- import re
- # 提取电子邮件地址
- text = "Contact us at info@example.com or support@example.org"
- emails = re.findall(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', text)
- print(emails) # 输出: ['info@example.com', 'support@example.org']
- # 提取URL
- text = "Visit our website at https://www.example.com or http://example.org"
- urls = re.findall(r'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', text)
- print(urls) # 输出: ['https://www.example.com', 'http://example.org']
- # 提取电话号码
- text = "Call us at (123) 456-7890 or 123-456-7890"
- phone_numbers = re.findall(r'\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}', text)
- print(phone_numbers) # 输出: ['(123) 456-7890', '123-456-7890']
- # 提取日期
- text = "The event will be held on 2023-05-15 and 2023/06/20"
- dates = re.findall(r'\d{4}[-/]\d{2}[-/]\d{2}', text)
- print(dates) # 输出: ['2023-05-15', '2023/06/20']
复制代码
数据验证
正则表达式常用于验证数据是否符合特定格式。
- import re
- # 验证电子邮件地址
- def is_valid_email(email):
- pattern = r'^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$'
- return bool(re.match(pattern, email))
- print(is_valid_email('user@example.com')) # 输出: True
- print(is_valid_email('invalid.email')) # 输出: False
- # 验证URL
- def is_valid_url(url):
- pattern = r'^https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+[^\s]*$'
- return bool(re.match(pattern, url))
- print(is_valid_url('https://www.example.com')) # 输出: True
- print(is_valid_url('example.com')) # 输出: False
- # 验证密码强度
- def is_strong_password(password):
- # 至少8个字符,包含大写字母、小写字母、数字和特殊字符
- pattern = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$'
- return bool(re.match(pattern, password))
- print(is_strong_password('Password123!')) # 输出: True
- print(is_strong_password('password')) # 输出: False
复制代码
数据替换
正则表达式常用于替换文本中的特定模式。
- import re
- # 替换电子邮件地址
- text = "Contact us at info@example.com or support@example.org"
- new_text = re.sub(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', '[EMAIL]', text)
- print(new_text) # 输出: Contact us at [EMAIL] or [EMAIL]
- # 替换HTML标签
- html = "<div><p>Hello, <b>world</b>!</p></div>"
- plain_text = re.sub(r'<[^>]+>', '', html)
- print(plain_text) # 输出: Hello, world!
- # 格式化电话号码
- text = "Call us at (123) 456-7890 or 123-456-7890"
- formatted_numbers = re.sub(r'\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})', r'\1.\2.\3', text)
- print(formatted_numbers) # 输出: Call us at 123.456.7890 or 123.456.7890
- # 删除多余的空格
- text = "This is a text with extra spaces"
- cleaned_text = re.sub(r'\s+', ' ', text)
- print(cleaned_text) # 输出: This is a text with extra spaces
复制代码
数据分割
正则表达式常用于根据特定模式分割文本。
- import re
- # 分割文本为句子
- text = "Hello! How are you? I'm fine, thanks."
- sentences = re.split(r'[.!?]', text)
- print(sentences) # 输出: ['Hello', ' How are you', " I'm fine, thanks", '']
- # 分割CSV行(考虑引号内的逗号)
- csv_line = 'John,Doe,"New York, NY",30'
- fields = re.split(r',(?=(?:[^"]*"[^"]*")*[^"]*$)', csv_line)
- print(fields) # 输出: ['John', 'Doe', '"New York, NY"', '30']
- # 分割日志条目
- log = "2023-05-15 10:30:00 INFO This is a log message"
- parts = re.split(r'\s+', log, 3)
- print(parts) # 输出: ['2023-05-15', '10:30:00', 'INFO', 'This is a log message']
复制代码
复杂文本解析
正则表达式可以用于解析复杂的文本结构。
- import re
- # 解析INI文件
- ini_content = """
- [database]
- host = localhost
- port = 3306
- username = root
- password = secret
- [server]
- host = 0.0.0.0
- port = 8080
- debug = true
- """
- config = {}
- current_section = None
- for line in ini_content.split('\n'):
- line = line.strip()
- if not line or line.startswith(';') or line.startswith('#'):
- continue
-
- section_match = re.match(r'^\[([^\]]+)\]$', line)
- if section_match:
- current_section = section_match.group(1)
- config[current_section] = {}
- continue
-
- if current_section:
- key_value_match = re.match(r'^([^=]+)=(.*)$', line)
- if key_value_match:
- key = key_value_match.group(1).strip()
- value = key_value_match.group(2).strip()
- config[current_section][key] = value
- print(config)
- # 输出: {
- # 'database': {
- # 'host': 'localhost',
- # 'port': '3306',
- # 'username': 'root',
- # 'password': 'secret'
- # },
- # 'server': {
- # 'host': '0.0.0.0',
- # 'port': '8080',
- # 'debug': 'true'
- # }
- # }
- # 解析简单的XML/HTML标签
- html = """
- <div class="container">
- <h1 id="title">Welcome</h1>
- <p class="content">This is a paragraph.</p>
- <a href="https://example.com">Link</a>
- </div>
- """
- tags = re.findall(r'<(\w+)(?:\s+([^>]*))?>', html)
- print(tags)
- # 输出: [('div', 'class="container"'), ('h1', 'id="title"'), ('p', 'class="content"'), ('a', 'href="https://example.com"')]
- # 提取标签内容
- contents = re.findall(r'<(\w+)(?:\s+[^>]*)?>(.*?)</\1>', html, re.DOTALL)
- print(contents)
- # 输出: [('div', '\n <h1 id="title">Welcome</h1>\n <p class="content">This is a paragraph.</p>\n <a href="https://example.com">Link</a>\n'), ('h1', 'Welcome'), ('p', 'This is a paragraph.'), ('a', 'Link')]
复制代码
性能优化和最佳实践
编译正则表达式
如果同一个正则表达式需要多次使用,最好先编译它。
- import re
- # 不推荐:每次使用都重新编译
- for i in range(100):
- result = re.search(r'\d+', 'abc123def')
- # 推荐:先编译,然后重复使用
- pattern = re.compile(r'\d+')
- for i in range(100):
- result = pattern.search('abc123def')
复制代码
使用原始字符串
在Python中,使用原始字符串(以r前缀)来定义正则表达式可以避免转义字符的问题。
- import re
- # 不推荐:需要双重转义
- pattern = re.compile('\\d+')
- # 推荐:使用原始字符串
- pattern = re.compile(r'\d+')
复制代码
避免回溯灾难
回溯灾难是指正则表达式在匹配失败时需要进行大量回溯,导致性能急剧下降。避免回溯灾难的方法包括:
1. 使用原子组(?>...)
2. 使用占有量词*+,++,?+,{n,m}+
3. 避免嵌套量词,如(a+)+
4. 使用更具体的模式,减少不必要的匹配
- import re
- import time
- # 可能导致回溯灾难的模式
- bad_pattern = re.compile(r'(a+)+b')
- # 优化的模式
- good_pattern = re.compile(r'a+b')
- # 测试性能
- test_string = 'a' * 30 + 'c'
- start_time = time.time()
- bad_pattern.search(test_string)
- bad_time = time.time() - start_time
- start_time = time.time()
- good_pattern.search(test_string)
- good_time = time.time() - start_time
- print(f"Bad pattern time: {bad_time:.6f} seconds")
- print(f"Good pattern time: {good_time:.6f} seconds")
复制代码
使用适当的函数
根据需求选择合适的函数:
• re.match():只检查字符串开头是否匹配
• re.search():检查整个字符串中是否有匹配
• re.findall():查找所有匹配的子串
• re.finditer():查找所有匹配的子串,返回迭代器
• re.sub():替换匹配的子串
• re.split():根据匹配的子串分割字符串
- import re
- text = "The quick brown fox jumps over the lazy dog. The dog is not that lazy."
- # 只检查开头
- if re.match(r'The', text):
- print("Text starts with 'The'")
- # 查找所有匹配
- words = re.findall(r'\b\w{4}\b', text)
- print(words) # 输出: ['over', 'lazy', 'that', 'lazy']
- # 使用迭代器处理大量匹配
- for match in re.finditer(r'\b(\w)(\w+)(\w)\b', text):
- word = match.group(0)
- first = match.group(1)
- middle = match.group(2)
- last = match.group(3)
- print(f"Word: {word}, First: {first}, Middle: {middle}, Last: {last}")
- # 替换匹配
- new_text = re.sub(r'\blazy\b', 'energetic', text)
- print(new_text)
- # 分割字符串
- sentences = re.split(r'\.\s*', text)
- print(sentences)
复制代码
使用详细模式提高可读性
对于复杂的正则表达式,使用re.VERBOSE标志可以提高可读性。
- import re
- # 不推荐:难以阅读
- pattern = re.compile(r'(?:(\d{1,3})\.){3}\d{1,3}')
- # 推荐:使用详细模式
- pattern = re.compile(r"""
- (?: # 开始非捕获分组
- (\d{1,3}) # 匹配1到3位数字(捕获组)
- \. # 匹配点号
- ){3} # 结束分组,重复3次
- \d{1,3} # 匹配1到3位数字
- """, re.VERBOSE)
复制代码
测试和调试正则表达式
使用在线工具或Python的re.debug标志来测试和调试正则表达式。
- import re
- # 使用re.debug标志(Python 3.11+)
- try:
- pattern = re.compile(r'\d+', re.DEBUG)
- except AttributeError:
- print("re.DEBUG is not available in this Python version")
- # 手动测试
- test_cases = [
- ('abc123def', True),
- ('abcdef', False),
- ('123', True),
- ('', False)
- ]
- pattern = re.compile(r'\d+')
- for test_string, expected in test_cases:
- result = bool(pattern.search(test_string))
- print(f"Test: '{test_string}', Expected: {expected}, Result: {result}, {'PASS' if result == expected else 'FAIL'}")
复制代码
总结
正则表达式是Python中处理字符串的强大工具,从简单的字符匹配到复杂的文本解析,它都能提供高效的解决方案。通过本文的学习,我们了解了正则表达式的基础知识、Python中的re模块、简单匹配、进阶匹配、复杂文本解析以及实际应用案例。
掌握正则表达式需要大量的练习和实践。在实际开发中,我们应该根据具体需求选择合适的正则表达式模式,并注意性能优化和最佳实践。同时,也要注意正则表达式的局限性,对于非常复杂的文本解析任务,可能需要结合其他解析技术。
正则表达式是一项必备的编程技能,掌握它将大大提高开发效率,使字符串处理变得更加简单和高效。希望本文能够帮助读者全面了解正则表达式在Python中的应用,并在实际项目中灵活运用。
版权声明
1、转载或引用本网站内容(正则表达式在Python字符串处理中的全面应用从简单匹配到复杂文本解析掌握这一必备编程技能提升开发效率)须注明原网址及作者(威震华夏关云长),并标明本网站网址(https://www.pixtech.cc/)。
2、对于不当转载或引用本网站内容而引起的民事纷争、行政处理或其他损失,本网站不承担责任。
3、对不遵守本声明或其他违法、恶意使用本网站内容者,本网站保留追究其法律责任的权利。
本文地址: https://www.pixtech.cc/thread-35013-1-1.html
|
|