Python——Regular Expression(正则表达式)RE模块

正则表达式

正则表达式是一种强大的文本处理工具,它使用一种特殊的语法来匹配、查找以及替换字符串中的字符组合。

一、re模块:使python语言拥有了所有正则表达式的功能

"re模块"是Python中用于处理正则表达式的标准库,英文全称叫做 "Regular Expression"。它提供了多个函数来执行正则表达式的匹配、查找、替换和分割操作。

简单案例:匹配手机号码的正则表达式

^1[34578]\d{9}$

这个正则表达式的含义如下:

  • ^ 表示字符串的开始。
  • 1 表示手机号码以数字1开头(运营商规则)。
  • [3-8] 表示第二位数字是34578中的任意一个数字(目前运营商第二位只允许是这几个数)。
  • \d{9} 表示接下来是9位任意数字(\d 是数字的简写,等价于 [0-9])。
  • $ 表示字符串的结束,如果少于或者超出都算做不匹配。

使用re.match()函数进行匹配:

从一个list列表中,匹配所有的号码,将每个号码进行匹配,属于手机号的输出“格式正确”,否则输出“格式错误”

import re  
  
# 假设这是你的列表  
list = ["13800138000", "12345678901", "abc123", "15912345678"]  
  
# 定义一个正则表达式模式来匹配中国的手机号码  
pattern = r'^1[34578]\d{9}$'  
  
# 遍历列表中的每个元素  
for number in list:  
    # 使用re.match()函数进行匹配  
    if re.match(pattern, number):  
        print(f"{number}: 手机号码格式正确")  
    else:  
        print(f"{number}: 手机号码格式错误")

改成函数调用写法:

import re  
  
def phone_number(number):  
    # 定义一个正则表达式模式来匹配中国的手机号码  
    pattern = r'^1[34578]\d{9}$'  
    # 使用re.match()函数进行匹配  
    if re.match(pattern, number):  
        return "手机号码格式正确"  
    else:  
        return "手机号码格式错误"  
  
# 假设这是你的列表  
a = ["13800138000", "12345678901", "abc123", "15912345678"]  
  
# 遍历列表中的每个元素,并调用phone_number()函数进行匹配  
for number in a:  
    print(f"{number}: {phone_number(number)}")

二、正则表达式基本语法:

1、匹配单个字符与数字

匹配 说明
. 匹配除换行符以外的任意字符,当flags被设置为re.S时,可以匹配包含换行符以内的所有字符
[] 里面是字符集合,匹配[]里任意一个字符
[0123456789] 匹配任意一个数字字符
[0-9] 匹配任意一个数字字符
[a-z] 匹配任意一个小写英文字母字符
[A-Z] 匹配任意一个大写英文字母字符
[A-Za-z] 匹配任意一个英文字母字符
[A-Za-z0-9] 匹配任意一个数字或英文字母字符
^[abcde] 匹配任何以 "a"、"b"、"c"、"d" 或 "e" 开头的单个字符的字符串。^ 通常被称为“锚点”或“开始锚点”
[^a-e] []里的^称为脱字符,表示非,匹配不在[]内的(a-e)任意一个字符
\d 匹配任意一个数字字符,相当于[0-9]
\D 匹配任意一个非数字字符,相当于[^0-9]
\w 匹配字母、下划线、数字中的任意一个字符,相当于[0-9A-Za-z_]
\W 匹配非字母、下划线、数字中的任意一个字符,相当于[^0-9A-Za-z_]
\s 匹配空白符(空格、换页、换行、回车、制表),相当于[ \f\n\r\t]
\S 匹配非空白符(空格、换页、换行、回车、制表),相当于[^ \f\n\r\t]

例1:在字符串 "world" 中使用正则表达式 [^hello] 进行匹配时,[^hello]等价于[^helo],会逐个字符地检查该字符串中的每个字符,看它们是否不在集合 helo 中。

注意,这里的 l 出现了两次,但在字符集中,重复的字符并没有特殊意义,它们只会被当作单独的字符来匹配。所以,[^hello] 和 [^helo] 在功能上是等价的。

  • 字符 'w' 不在 helo 中,因此会匹配。
  • 接着检查字符 'o',但是 'o' 在 helo 中,因此不会匹配。
  • 然后检查字符 'r','r' 不在 helo 中,因此会匹配。
  • 接着是字符 'l','l' 在 helo 中,因此不会匹配。
  • 最后是字符 'd','d' 不在 helo 中,因此会匹配。

因此,在字符串 "world" 中,使用 [^helo] 进行匹配,将会匹配到字符 'w'、'r' 和 'd'。这三个字符是字符串中不在集合 helo 中的字符。注意,[^helo] 只匹配单个字符,所以它会分别匹配每个符合条件的字符,而不是整个子串或单词。

hello: 第一个字符匹配, h
apple8901: 第一个字符不匹配, [^a-e]
camp123: 第一个字符不匹配, [^a-e]
word5678: 第一个字符匹配, w
'hello': 匹配到, [^a-e]: ['h', 'l', 'l', 'o']
'apple8901': 匹配到, [^a-e]: ['p', 'p', 'l', '8', '9', '0', '1']
'camp123': 匹配到, [^a-e]: ['m', 'p', '1', '2', '3']
'word5678': 匹配到, [^a-e]: ['w', 'o', 'r', '5', '6', '7', '8']

2、匹配锚字符

锚字符:用来判定是否按照规定开始或者结尾

匹配 说明
^ 行首匹配,和[]里的^不是一个意思
$ 行尾匹配

3、限定符

限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。

匹配 说明
(xyz) 匹配括号内的xyz,作为一个整体去匹配 一个单元 子存储
x? 匹配0个或者1个x,非贪婪匹配
x* 匹配0个或任意多个x
x+ 匹配至少一个x
x{n} 确定匹配n个x,n是非负数
x{n,} 至少匹配n个x
x{n,m} 匹配至少n个最多m个x
x|y |表示或的意思,匹配x或y

4、通用flags(修正符)

flags 参数用于控制正则表达式的匹配方式。它是一个可选参数,用于指定不同的匹配标志,可以是多个标志的组合(使用按位或 | 运算符组合)。

一些常用的 flags 包括:

  • re.IGNORECASE 或 re.I:使匹配对大小写不敏感。
  • re.MULTILINE 或 re.M:使开始和结束字符 ^ 和 $ 分别匹配每一行的开始和结束,而不是整个字符串的开始和结束。
  • re.DOTALL 或 re.S:使 . 特殊字符匹配任何字符,包括换行符。
  • re.VERBOSE 或 re.X:此标志允许你在正则表达式中写入注解,这将使正则表达式更易读。
说明
re.I 是匹配对大小写不敏感
re.S 使.匹配包括换行符在内的所有字符
re.M 使开始和结束字符 ^ 和 $ 分别匹配每一行的开始和结束,而不是整个字符串的开始和结束。
re.X 此标志允许你在正则表达式中写入注解,这将使正则表达式更易读。

例如,使用 re.IGNORECASE 使得匹配不区分大小写:

import re  
  
pattern = re.compile(r'apple', flags=re.IGNORECASE)  
string = 'This is an Apple, and that is an apple.'  
  
matches = pattern.findall(string)  
print(matches)  # 输出: ['Apple', 'apple']

在这个例子中,尽管 "Apple" 和 "apple" 大小写不同,但由于使用了 re.IGNORECASE 标志,它们都被匹配到了。

你可以根据需要组合这些标志。例如,要同时忽略大小写和让 . 匹配包括换行符在内的任何字符,你可以这样做:

pattern = re.compile(r'.*', flags=re.IGNORECASE | re.DOTALL)

例2,使用re.MULTILINE匹配多行:

当设置了re.MULTILINE标志时,简写为re.M,正则表达式的开始和结束字符^$会分别匹配每一行的开始和结束,而不是整个字符串的开始和结束。

这在处理多行文本时特别有用,尤其是当你需要在每行的开始或结束处进行匹配时。如果不设置re.MULTILINE标志,^$会默认匹配整个字符串的开始和结束。

import re  
  
# 定义一个多行字符串  
text = """  
This is line 1  
This is line 2  
This is line 3  
"""  
  
# 使用re.MULTILINE标志查找每行开始的"This"  
pattern = re.compile(r'^This', flags=re.MULTILINE)  
matches = pattern.findall(text)  
print(matches)  # 输出: ['This', 'This', 'This']

5、pattern语法

字符串'正则表达式'前面的前缀rR表示这个字符串是一个原始字符串(raw string)。

pattern = r'正则表达式'

使用原始字符串:

pattern = r'^1[34578]\d{9}$' 

如果字符串'^1[34578]\d{9}$'不被r标记,那么字符串中的反斜杠\会被当作普通字符处理,而不是作为转义字符。在正则表达式中,反斜杠用于引入特殊字符或转义普通字符。因此,如果你不使用r前缀,并且想要表示一个正则表达式的特殊字符,你需要使用双反斜杠\\来代替单反斜杠\,如:

不使用原始字符串:

pattern = '^1[34578]\\d{9}$'

在正则表达式中,^ 和 $ 是特殊字符,但它们不需要使用双反斜杠 \\ 来转义,即使你不使用原始字符串(即不使用 r 前缀)。这是因为 ^ 和 $ 在字符串中并不具有特殊的转义意义,它们只在正则表达式的上下文中被解释为特殊字符。

无论你是否使用原始字符串,^ 和 $ 的写法都是相同的:

总结:对于正则表达式来说,使用原始字符串可以简化字符串的编写,避免因为忘记转义反斜杠(\d\w\s)而导致的错误。所以,在定义正则表达式时,通常推荐使用原始字符串。pattern = r'正则表达式'


 

三、re模块的常用函数用法

1、re模块中常用的函数及其主要功能:

  1. re.match(pattern, string, flags=0)
    • 功能:从字符串的起始位置匹配一个模式,如果匹配成功返回一个Match对象,否则返回None。
      import re
      
      # 假设这是你的列表
      lists = ["hello", "apple8901", "camp123", "word5678"]
      
      # 定义一个正则表达式模式来匹配规则
      pattern = r'[^a-e]'    # 匹配不是a-e
      
      # 循环拿取列表中的number
      for number in lists:
          # re.match(首个字符匹配)语法
          a = re.match(pattern, number)
          if a:
              print(f"{number}: 首个字符匹配: {a.group()}")
          else:
              print(f"{number}: 首个字符不匹配!")
              
      # 运行结果
      hello: 首个字符匹配: h
      apple8901: 首个字符不匹配!
      camp123: 首个字符不匹配!
      word5678: 首个字符匹配: w
  2. re.search(pattern, string, flags=0)
    • 功能:扫描整个字符串并返回第一个成功匹配的结果,如果匹配成功返回一个Match对象,否则返回None。
      import re
      
      # 定义包含多个字符串的列表,其中可能包含中文汉字
      strings = ["我喜欢学习Python", "Python很有趣", "学习中文也很重要"]
      
      # 定义正则表达式模式,这里是要搜索包含“学习”这个词语的匹配项
      pattern = r'学习'
      
      # 遍历列表中的每个字符串,并尝试搜索匹配项
      for s in strings:
          match = re.search(pattern, s)
          if match:
              # 如果找到匹配项,打印匹配到的字符串和它在原字符串中的位置
              print(f"匹配到 '{s}': {match.group()} at position {match.start()}")
          else:
              print(f"没有匹配到 '{s}'")
              
      # 结果
      匹配到 '我喜欢学习Python': 学习 at position 3
      没有匹配到 'Python很有趣'
      匹配到 '学习中文也很重要': 学习 at position 0
  3. re.findall(pattern, string, flags=0)
    • 功能:在文本中搜索匹配正则表达式的所有字符串,返回字符串列表。
      import re
      
      # 假设这是你的列表
      lists = ["hello", "apple8901", "camp123", "word5678"]
      
      # 定义一个正则表达式模式来匹配规则
      pattern = r'[^a-e]'    # 匹配不是a-e
      
      # 遍历测试字符串并打印匹配结果
      for s in lists:
          # re.findall 所有字符拆分匹配
          matches = re.findall(pattern, s)
          print(f"'{s}': 匹配到, {pattern}: {matches}")
          
      # 结果
      'hello': 匹配到, [^a-e]: ['h', 'l', 'l', 'o']
      'apple8901': 匹配到, [^a-e]: ['p', 'p', 'l', '8', '9', '0', '1']
      'camp123': 匹配到, [^a-e]: ['m', 'p', '1', '2', '3']
      'word5678': 匹配到, [^a-e]: ['w', 'o', 'r', '5', '6', '7', '8']
  4. re.finditer(pattern, string, flags=0)
    • 功能:与findall类似,但返回的是一个迭代器,每个迭代元素是一个Match对象。
      import re
      
      # 定义包含多个日期的字符串
      string = "我的生日是 2023-03-15,而我朋友的生日是 2022-09-20 和 2021-01-01 。"
      
      # 定义正则表达式模式,用于匹配日期(YYYY-MM-DD)
      pattern = r'\b\d{4}-\d{2}-\d{2}\b'  # \b 表示单词边界,\d 表示数字
      
      # 使用 re.finditer() 查找所有匹配项
      matches = re.finditer(pattern, string)
      
      # 遍历匹配项并打印结果
      for match in matches:
          print(f"Found match: {match.group()} at position {match.start()}")
          
      # 结果
      Found match: 2023-03-15 at position 6
      Found match: 2022-09-20 at position 26
      Found match: 2021-01-01 at position 39
  5. re.split(pattern, string, maxsplit=0, flags=0)
    • 功能:将文本中匹配正则表达式的字符串作为分隔符,将文本分隔为列表。
      import re
      
      # 定义包含逗号分隔的字符串列表,可能包含额外的空格
      string = "apple, banana , cherry,  date"
      
      # 定义正则表达式模式,匹配逗号以及可能的前后空格
      pattern = r'\s*,\s*'  # \s* 匹配零个或多个空白字符(包括空格、制表符、换行符等)
      
      # 使用 re.split() 根据模式分割字符串,定义最大分割次数为 2
      split_list = re.split(pattern, string, maxsplit=2)
      
      # 打印分割后的列表
      print(split_list)
      
      # 由于 maxsplit 被设置为 2,所以只进行了两次分割,剩余的字符串'cherry,  date'保持原样。
      ['apple', 'banana', 'cherry,  date']
  6. re.sub(pattern, repl, string, count=0, flags=0)
    • 功能:在文本中搜索匹配正则表达式的字符串并替换为指定字符串,返回替换后的文本。
      import re  
        
      # 定义一个包含多个字符串的列表  
      lists = ["我有1个苹果", "他有2本书", "她花了30元"]  
        
      # 定义要匹配的正则表达式模式(匹配任何数字)  
      pattern = r'\d+'  
        
      # 定义替换字符串  
      repl = 'N'  
        
      # 使用列表推导式替换每个字符串中的数字  
      new_lists = [re.sub(pattern, repl, s) for s in lists]  
        
      # 打印替换后的字符串列表  
      print(new_lists)
      
      # 结果
      ['我有N个苹果', '他有N本书', '她花了N元']
  7. re.subn(pattern, repl, string, count=0, flags=0)
    • 功能:与sub类似,但返回的是一个元组,包含替换后的文本和替换次数。
      import re  
        
      # 定义一个包含多个字符串的列表  
      lists = ["我有1个苹果", "他有2本书", "她买了3支笔和1个包", "总价是45元"]  
        
      # 定义要匹配的正则表达式模式(匹配任何数字)  
      pattern = r'\d+'  
        
      # 定义替换字符串  
      repl = 'N'  
        
      # 初始化一个空列表来存储替换后的字符串和替换次数  
      subbed_lists = []  
      替换次数列表 = []  
        
      # 遍历列表中的每个字符串,使用 re.subn() 进行替换并获取替换次数  
      for s in lists:  
          subbed_string,替换次数 = re.subn(pattern, repl, s)  
          subbed_lists.append(subbed_string)  
          替换次数列表.append(替换次数)  
        
      # 打印替换后的字符串列表  
      print("替换后的字符串列表:")  
      print(subbed_lists)  
        
      # 打印每个字符串的替换次数列表  
      print("每个字符串的替换次数:")  
      print(替换次数列表)
      
      # 替换后的字符串列表:  
      ['我有N个苹果', '他有N本书', '她买了N支笔和N个包', '总价是N元']  
      每个字符串的替换次数:  
      [1, 1, 2, 1]
    • 遍历 lists 中的每个字符串,并使用 re.subn() 函数替换其中的数字。对于每个字符串,re.subn() 返回一个元组,其中包含替换后的字符串和替换次数。我们将这两个值分别添加到 subbed_lists 和 替换次数列表 中。最后,我们打印出替换后的字符串列表和每个字符串的替换次数列表。

      通过这种方式,我们不仅得到了替换后的字符串,还知道了在每个字符串中进行了多少次替换操作。这对于分析字符串中特定模式的出现频率非常有用。

  8. re.escape(pattern)
    • 功能:对字符串中的正则表达式特殊字符进行转义。
      import re  
        
      # 假设我们有一个用户输入的字符串,我们想要将它用作正则表达式的一部分  
      user_input = "example.com"  
        
      # 在构建正则表达式时,我们需要确保 user_input 中的点号不会被当作正则表达式中的特殊字符(点号在正则表达式中代表任意字符)  
      escaped_input = re.escape(user_input)  
        
      # 现在,我们可以安全地在正则表达式中使用 escaped_input  
      pattern = r"http://{}".format(escaped_input)  
        
      # 创建一个包含目标字符串的文本  
      text = "Visit http://example.com for more information."  
        
      # 使用 re.search() 查找匹配项  
      match = re.search(pattern, text)  
        
      # 如果找到匹配项,打印它  
      if match:  
          print("Found match:", match.group())  
      else:  
          print("No match found.")
    • 在这个例子中,用户输入了一个字符串 example.com,我们想要构建一个正则表达式来匹配这个字符串作为 URL 的一部分。由于点号在正则表达式中有特殊含义(代表任意字符),我们需要使用 re.escape() 来转义它,这样它就会被当作普通字符处理。

      re.escape(user_input) 会返回 example\.com,这样我们就可以安全地将它包含在正则表达式中,而不用担心它会干扰正则表达式的匹配行为。然后,我们构建了正则表达式 pattern 并用它来搜索 text 字符串中的匹配项。

      如果 text 中包含 http://example.com,则 re.search() 会找到一个匹配项,并打印出来。如果 text 中不包含该字符串或格式不正确,则不会找到匹配项。

  9. re.compile(pattern, flags=0)
    • 功能:将正则表达式编译为Pattern对象,可以多次使用该对象进行匹配。
      import re
      
      lists = ["我有1个苹果", "他有2本书", "她买了3支笔", "桌子上有4个杯子"]
      
      pattern_str = r'\d+'  # 匹配一个或多个数字
      
      pattern = re.compile(pattern_str)
      
      for s in lists:
          match = pattern.search(s)
          if match:
              print(f"在 '{s}' 中找到了数字:{match.group()}")
          else:
              print(f"在 '{s}' 中没有找到数字")
      
      # 结果
      在 '我有1个苹果' 中找到了数字:1
      在 '他有2本书' 中找到了数字:2
      在 '她买了3支笔' 中找到了数字:3
      在 '桌子上有4个杯子' 中找到了数字:4
    • 通过使用 pattern = re.compile(pattern_str) 编译正则表达式,我们可以在后续多次使用相同的正则表达式时提高效率,因为编译过程只需要执行一次。这在处理大量文本或需要频繁使用相同正则表达式的情况下非常有用。
    • 对比单次调用
      import re
      
      lists = ["我有1个苹果", "他有2本书", "她买了3支笔", "桌子上有4个杯子"]
      
      pattern = r'\d+'  # 匹配一个或多个数字
      
      for s in lists:
          match = re.search(pattern, s)
          if match:
              print(f"在 '{s}' 中找到了数字:{match.group()}")
          else:
              print(f"在 '{s}' 中没有找到数字")
  10. re.purge()
    • 功能:清除正则表达式缓存。
  11. re.escape(string)
    • 功能:对字符串中的特殊字符进行转义,使其能够被当作普通字符处理。
  12. re.fullmatch(pattern, string, flags=0)
    • 功能:整个字符串需要完全匹配模式,成功则返回Match对象,否则返回None。
  13. re.search(pattern, string, pos, endpos)
    • 功能:与re.search()类似,但允许指定搜索的起始和结束位置。
  14. re.match(pattern, string, pos, endpos)
    • 功能:与re.match()类似,但允许指定匹配的起始和结束位置。
  15. re.ASCII, re.IGNORECASE, re.MULTILINE, re.DOTALL, re.UNICODE, re.VERBOSE 等
    • 功能:这些是正则表达式的标志位,用于修改正则表达式的匹配行为。

 

这些函数和标志位提供了强大的正则表达式处理能力,使得Python能够方便地进行复杂的文本匹配和替换操作。在使用这些函数时,通常需要结合正则表达式语法来编写匹配模式。

2、关键参数用法详解。

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

re.sub() 函数用于在字符串中查找正则表达式的所有非重叠匹配项,并用另一个字符串替换它们。

  • pattern: 要查找的正则表达式模式。
  • repl: 用于替换匹配项的字符串或函数。
  • string: 要在其上执行查找和替换操作的原始字符串。
  • count: 可选参数,表示要替换的最大匹配次数。默认是0,表示替换所有匹配项。
  • flags: 可选参数,用于修改正则表达式的匹配方式。

示例1:

import re  
  
pattern = "world"  
repl = "Python"  
string = "Hello, world! This is the world we live in."  
result = re.sub(pattern, repl, string, count=1)  
print(result)  # 输出: Hello, Python! This is the world we live in.

在这个例子中,"world" 的第一个匹配项被替换为 "Python",由于 count=1,所以只有第一个匹配项被替换。

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

re.split() 函数根据正则表达式的匹配项来分割字符串。

  • pattern: 正则表达式模式,根据此模式分割字符串。
  • string: 要分割的字符串。
  • maxsplit: 可选参数,表示最大分割次数。默认是0,表示不限制分割次数。
  • flags: 可选参数,用于修改正则表达式的匹配方式。

示例2:

import re

pattern = r'\d+'  # 匹配一个或多个数字
string = 'one1two2three3four'

# 不设置 maxsplit
split_result_no_limit = re.split(pattern, string)
print(split_result_no_limit)  # 输出: ['one', 'two', 'three', 'four']

# 设置 maxsplit 为 1  
split_result_limited = re.split(pattern, string, maxsplit=1)
print(split_result_limited)  # 输出: ['one', 'two2three3four']

# 设置 maxsplit 为 2
split_result_limited_more = re.split(pattern, string, maxsplit=2)
print(split_result_limited_more)  # 输出: ['one', 'two', 'three3four']

在这个例子中,根据数字(\d+)将字符串分割成了几个部分。

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

re.findall() 函数查找字符串中所有正则表达式的非重叠匹配项,并返回一个列表,包含所有匹配项。

  • pattern: 要查找的正则表达式模式。
  • string: 要在其上执行查找操作的字符串。
  • flags: 可选参数,用于修改正则表达式的匹配方式。

示例3:

import re  
  
pattern = r'\d+'  
string = "one1two2three3four"  
result = re.findall(pattern, string)  
print(result)  # 输出: ['1', '2', '3']

在这个例子中,我们查找字符串中的所有数字并返回一个列表。

对于上述函数,flags 参数通常用于修改正则表达式的匹配行为,例如 re.IGNORECASE(忽略大小写)或 re.MULTILINE(多行模式)。而 maxsplit 参数只与 re.split() 相关,用于限制分割的最大次数。

posted @ 2024-03-19 11:50  Magiclala  阅读(137)  评论(0编辑  收藏  举报