python学习-正则

115 阅读8分钟

python学习-正则

本文介绍 python 的正则表达式,包括正则表达式的基本语法、常用函数、匹配模式、分组捕获、贪婪匹配和非贪婪匹配、替换和分割字符串、编译正则表达式等。

供自己以后查漏补缺,也欢迎同道朋友交流学习。

引言

上一篇文章介绍了程序的异常处理,但在处理异常的时候,我们经常要进行逻辑 if 判断,简单的就是比较相等或者大小,但真实场景里我们还需要判断是否是手机号银行卡号邮箱密码格式...。

这时候就需要使用正则去更简单的进行逻辑判断和字符串匹配了。

因此,本章主要介绍 python 的正则表达式,包括正则表达式的基本语法、常用函数、匹配模式、分组捕获、贪婪匹配和非贪婪匹配、替换和分割字符串、编译正则表达式等。

正则的用途

正则表达式(简称regexregexp)是一种文本模式描述的方法,它由一系列字符组成,这些字符可以是普通字符(如字母 az)、特殊字符(如. * ?)或两者的组合。

正则表达式用于描述匹配查找管理文本字符串中的复杂模式。正则表达式的主要用途包括:

  • 文本搜索:在大量文本中查找特定的单词或短语。
  • 数据验证检查电子邮件地址、电话号码、邮政编码等是否符合特定格式。
  • 文本替换:在文本中替换修改特定的单词或短语。
  • 数据提取:从复杂的文本数据中提取有用的信息。
  • 文本分割:将文本分割成更小的部分,以便进一步处理。

基本语法

正则表达式的基本语法是构建正则表达式模式的基础,它包括字面量字符特殊字符元字符预定义字符类数量词

字面量字符

字面量字符是指在正则表达式中直接表示其自身含义的字符。例如,字母a、数字1等都是字面量字符。

特殊字符和转义

特殊字符在正则表达式中有特殊的含义,它们用于定义搜索模式的规则。为了匹配这些特殊字符本身,需要使用反斜杠 \ 进行转义。

特殊字符说明
.匹配任意单个字符(除了换行符)
^匹配输入字符串的开始位置
$匹配输入字符串的结束位置
*匹配前面的子表达式零次或多次
+匹配前面的子表达式一次或多次
?匹配前面的子表达式零次或一次
[]匹配括号内的任意字符(字符集)
|匹配两项之间的任意一项(或)
\转义特殊字符或表示特殊序列的开始

元字符

元字符是正则表达式中具有特殊意义的字符,它们用于指定字符串的特定模式。包含了上面特殊字符,新增了一些特殊的含义。

元字符说明
......上面的特殊字符
{n}表示前面的字符恰好出现 n
{n,}表示前面的字符至少出现 n
{n,m}表示前面的字符至少出现 n 次,但不超过 m

预定义字符类

预定义字符类是正则表达式中用于匹配特定类型的字符集。

预定义字符类说明
\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

常用函数

re.match()

re.match() 函数尝试从字符串的开始位置匹配正则表达式,如果字符串开始处就匹配,则返回一个匹配对象;否则返回 None

# 语法 
# match_result = re.match(pattern, string, flags=0)
# pattern:正则表达式的模式字符串。
# string:要匹配的字符串。
# flags:编译时用的匹配模式,如re.IGNORECASE或re.MULTILINE。
import re

match_result = re.match(r'^\d+', '123abc')
if match_result:
  print('@@@@ match 匹配成功', match_result.group())  
else:
  print("@@@@ match 匹配失败")
  
# 输出:@@@@ match 匹配成功 123

re.search()

re.search() 函数扫描整个字符串,寻找正则表达式的第一次出现,如果找到匹配,则返回一个匹配对象;否则返回 None

# 语法 
# search_result = re.search(pattern, string, flags=0)
# pattern:正则表达式的模式字符串。
# string:要匹配的字符串。
# flags:编译时用的匹配模式。
import re

search_result = re.search(r'^\d+', 'abc123def')
if search_result:
  print('@@@@ search 匹配成功', search_result.group())  
else:
  print("@@@@ search 匹配失败")

# 输出:@@@@ search 匹配失败

re.findall()

re.findall() 函数找出字符串中所有匹配正则表达式的子串,并返回一个列表。

# 语法 
# all_matches = re.findall(pattern, string, flags=0)
# pattern:正则表达式的模式字符串。
# string:要匹配的字符串。
# flags:编译时用的匹配模式。
import re

all_matches = re.findall(r'\d+', 'abc123def456')
print("@@@@ findall 所有匹配", all_matches)

# 输出:@@@@ findall 所有匹配 ['123', '456']

re.finditer()

re.finditer() 函数返回一个迭代器,迭代器产生 Match 对象。这个函数类似于 re.findall(),但它返回的是 Match 对象,而不是字符串列表,这使得可以访问匹配的详细信息。

# 语法 
# finditer_result = re.finditer(pattern, string, flags=0)
# pattern:正则表达式的模式字符串。
# string:要匹配的字符串。
# flags:编译时用的匹配模式。
import re

for match in re.finditer(r'\d+', 'abc123def456'):
  print("匹配:", match.group())

# 输出:
# 匹配: 123
# 匹配: 456

re.sub()

re.sub() 函数用于替换字符串中的正则表达式匹配项。它返回一个新的字符串,其中所有的匹配都被替换。

# 语法 
# new_string = re.sub(pattern, repl, string, count=0, flags=0)
# pattern:正则表达式的模式字符串。
# repl:替换匹配项的字符串或函数。
# string:要匹配的字符串。
# count:模式匹配后替换的最大次数,默认0表示替换所有匹配。
# flags:编译时用的匹配模式。
import re

new_string = re.sub(r'\d+', '数字', 'abc123def456', count=1)
print("@@@@ sub 替换结果", new_string)

# 输出:@@@@ sub 替换结果 abc数字def456

匹配模式

匹配模式指的是用于识别字符串中特定模式的规则和结构。这些模式可以是简单的字符序列,也可以是复杂的结构,包括字符组合重复选择等。

  • 全匹配:要求整个字符串或字符串的特定部分完全符合正则表达式定义的模式。
  • 部分匹配:只要求字符串中的一部分符合正则表达式定义的模式。

以下是一些匹配模式的例子和解释:

  • 简单匹配

    • 模式\d+
    • 描述:匹配一个或多个数字。
    • 示例:在字符串"abc123def"中,\d+会匹配"123"。
  • 字符类匹配

    • 模式[abc]+
    • 描述:匹配一个或多个a、b或c。
    • 示例:在字符串"xyzabc123"中,[abc]+会匹配"abc"。
  • 范围匹配

    • 模式[a-z]
    • 描述:匹配任意一个小写字母。
    • 示例:在字符串"Hello World"中,[a-z]会匹配"ello"。
  • 选择匹配

    • 模式(cat|dog)
    • 描述:匹配"cat"或"dog"。
    • 示例:在字符串"I have a cat"中,(cat|dog)会匹配"cat"。
  • 重复匹配

    • 模式\d{3,5}
    • 描述:匹配3到5个数字。
    • 示例:在字符串"123456"中,\d{3,5}会匹配"123"、"1234"或"12345"。
  • 位置匹配

    • 模式^admin
    • 描述:匹配以"admin"开头的字符串。
    • 示例:在字符串"admin123"中,^admin会匹配整个字符串。
  • 结束匹配

    • 模式end$
    • 描述:匹配以"end"结尾的字符串。
    • 示例:在字符串"theend"中,end$会匹配"end"。

分组捕获

分组捕获是一种将正则表达式的某部分括起来的机制,使得匹配到的字符串可以被单独提取出来。

分组使用圆括号()来定义,它们不仅帮助我们匹配复杂的模式,还可以让我们访问匹配的具体部分。

分组的语法

  • 普通分组:使用圆括号()将正则表达式的某部分括起来,如(abc)
  • 非捕获分组:使用?:后跟圆括号来创建一个非捕获分组,如(?:abc)。这种分组用于分组而不捕获匹配的文本。

提取信息

从匹配的字符串中提取特定的子字符串。

import re

phone = "123-456-7890"
match = re.match(r"(\d{3})-(\d{3})-(\d{4})", phone)

print("区号:", match.group(1)) # 区号: 123
print("交换机号码:", match.group(2)) # 交换机号码: 456
print("线路号码:", match.group(3)) # 线路号码: 7890

分组引用

在同一个正则表达式中,后续的分组可以通过前面的分组捕获的内容进行匹配。

import re
text = "abc abc"
match = re.search(r"(\w+) \1", text)
if match:
  print("匹配:", match.group())

# 输出: 匹配: abc abc

使用非捕获分组

使用?:后跟圆括号来创建一个非捕获分组,这种分组用于分组而不捕获匹配的文本。

import re
phone = "123-456-7890"
match = re.match(r"(?:\d{3})-(\d{3})-(\d{4})", phone)
if match:
  print("交换机号码:", match.group(1)) # 交换机号码: 456
  print("线路号码:", match.group(2)) # 线路号码: 7890

贪婪匹配与非贪婪匹配

在正则表达式中,量词(如*、+、?和{})可以指定一个模式出现的次数。默认情况下,这些量词是贪婪的,意味着它们会尽可能多地匹配字符。

  • 贪婪匹配:匹配尽可能的字符。
  • 非贪婪匹配:匹配尽可能的字符。

非贪婪量词

要将量词从贪婪模式转换为非贪婪模式,可以在量词后面添加?

import re

text = "我是中国人,我爱中国。"

# 贪婪匹配
greedy_match = re.search(r"中国.*", text)
if greedy_match:
  print("贪婪匹配:", greedy_match.group())
  
# 输出: 贪婪匹配: 中国,我爱中国。

# 非贪婪匹配
non_greedy_match = re.search(r"中国.*?", text)
if non_greedy_match:
  print("非贪婪匹配:", non_greedy_match.group())
  
# 输出: 非贪婪匹配: 中国

替换和分割字符串

替换 re.sub()

在常用函数里已经介绍过了,这里就不再赘述了。

re.split()

re.split() 函数用于根据匹配正则表达式的模式来分割字符串。它返回一个列表,其中包含被分割的部分。

# 语法 
# split_result = re.split(pattern, string, maxsplit=0, flags=0)
# pattern:正则表达式的模式字符串。
# string:要匹配的字符串。
# maxsplit:分割的最大次数,默认0表示无限制。
# flags:编译时用的匹配模式。
import re

text = "apple,banana,cherry"
fruits = re.split(r',', text)
print(fruits)

# 输出:['apple', 'banana', 'cherry']

替换和分割的高级技巧

  • 使用函数作为替换参数

re.sub()允许传递一个函数作为repl参数,该函数接受一个匹配对象,并返回用于替换的字符串。

import re
text = "今天是2023-11-21"
def replace_with_comma(match):
  return match.group().replace('-', ',')

new_text = re.sub(r'(\d+)-(\d+)-(\d+)', replace_with_comma, text)
print(new_text)
# 输出:今天是2023,11,21
  • 限制分割次数re.split()maxsplit 参数可以用来限制分割的次数,这在处理大型文件或需要特定分割位置时非常有用。
import re
text2 = "apple,banana,cherry,date,fig"
fruits = re.split(r',', text2, maxsplit=2)
print(fruits)
# 输出:['apple', 'banana', 'cherry,date,fig']

编译正则表达式

正则表达式可以通过 re 模块提供的函数直接使用,但频繁使用相同的正则表达式模式时,每次都进行编译会降低效率

为了解决这个问题,可以使用 re.compile() 函数将正则表达式编译成正则表达式对象(Pattern对象),这样可以提高匹配效率,尤其是在需要多次使用同一模式的情况下。

re.compile() 函数用于编译正则表达式模式,生成一个正则表达式对象。

# 语法 
# pattern = re.compile(pattern, flags=0)
# flags:编译时用的匹配模式。
import re

pattern = re.compile(r'\d+')
result = pattern.match('123abc')
if result:
  print("匹配成功:", result.group())
  
# 输出:匹配成功: 123

编译后的正则表达式对象提供了以下方法:

  • match():从字符串的开始位置匹配正则表达式。
  • search():扫描整个字符串,寻找正则表达式的第一次出现。
  • findall():找出字符串中所有匹配正则表达式的子串。
  • finditer()返回一个迭代器,迭代器产生 Match 对象。
  • sub()替换字符串中的正则表达式匹配项。
  • split():根据匹配正则表达式的模式来分割字符串。

正则高级技巧

使用断言

  • 前瞻断言((?=...)):确保某个模式后面跟着特定的字符串。
  • 后顾断言((?<=...)):确保某个模式前面是特定的字符串。
  • 负向前瞻断言((?!...)):确保某个模式后面不跟着特定的字符串。
  • 负向后顾断言((?<!...)):确保某个模式前面不是特定的字符串。
import re

# 匹配一个单词,它后面跟着"ing"
text = "I am running and I am swimming."
matches = re.findall(r'\b\w+(?=ing)', text)
print(matches)  
# 输出: ['runn', 'swimm']

使用字符集和排除

  • 利用字符集 [...] 来匹配一系列可能的字符。
  • 使用 ^ 在字符集开始处排除某些字符。
import re

# 匹配除了数字之外的任意字符
text2 = "abc123def456"
matches2 = re.findall(r'[^\d]+', text2)
print(matches2)  
# 输出: ['abc', 'def']

python学习专栏系列

练习代码库地址

python-study