13. 正则表达式

文章目录

正则表达式

1. property属性

1.1 property属性的介绍

property属性就是负责把一个方法当做属性进行使用,这样做可以简化代码使用。

定义property属性有两种方式

  1. 装饰器方式
  2. 类属性方式

1.2 装饰器方式

class Studen(object):
    def __init__(self):
        # 私有属性
        self.__age = 10

    @property # 当对象调用age属性时会执行下面的方法
    def age(self):
        print("获取私有属性")
        return self.__age

    # 使用装饰器方式的property属性那么方法名要保持一致,就像java里的setter getter
    # 上面使用property装饰器下边的方法名是什么,这里使用的setter前面的方法名要一致
    @age.setter # 当对象调用age属性的时候会调用下面的方法
    def age(self,new_age):
        if new_age >= 0 and new_age <= 130:
            self.__age = new_age
        else:
            print("闹妖了")

# 提示: 使用装饰器方式的property属性那么方法名要保持一致
student = Studen()
age = student.age
print(age)


student.age = 50

age = student.age
print(age)

运行结果:

D:\Python\python.exe E:/PythonResources/day11/01.property属性-装饰器方式的使用.py
获取私有属性
10
获取私有属性
50

Process finished with exit code 0

代码说明:

  • @property 表示把方法当做属性使用, 表示当获取属性时会执行下面修饰的方法
  • @方法名.setter 表示把方法当做属性使用,表示当设置属性时会执行下面修饰的方法
  • 装饰器方式的property属性修饰的方法名一定要一样。

1.3 类属性方式

class Student(object):
    def __init__(self):
        # 私有属性
        self.__age = 10

    def get_age(self):
        print("获取age属性")
        return self.__age

    def set_age(self, new_age):
        print("设置age属性")
        if new_age >= 0 and new_age <= 130:
            self.__age = new_age
        else:
            print("闹妖了")

    # 1. get_age 表示获取age属性的时候执行的方法
    # 2. set_age 表示设置age属性的时候执行的方法
    age = property(get_age, set_age)


student = Student()
age = student.age
print(age)

student.age = 20

age = student.age
print(age)

运行结果:

D:\Python\python.exe E:/PythonResources/day11/02.property属性-类属性方式的使用.py
获取age属性
10
设置age属性
获取age属性
20

Process finished with exit code 0

代码说明:

  • property的参数说明:
    • 第一个参数是获取属性时要执行的方法
    • 第二个参数是设置属性时要执行的方法

1.4 小结

  • 定义property属性有两种方式:
    1. 装饰器方式
    2. 类属性方式
  • 装饰器方式:
    1. @property 修饰获取值的方法
    2. @方法名.setter 修饰设置值的方法
  • 类属性方式:
    1. 类属性 = property(获取值方法, 设置值方法)

2. with语句和上下文管理器

2.1 with语句的使用

向文件中写入数据的示例代码:

 # 1、以写的方式打开文件
 f = open("1.txt", "w")
 # 2、写入文件内容
 f.write("hello world")
 # 3、关闭文件
 f.close()

代码说明:

  • 文件使用完后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的

这种写法可能出现一定的安全隐患,错误代码如下:

 # 1、以读的方式打开文件
 f = open("1.txt", "r")
 # 2、读取文件内容
 f.write("hello world")
 # 3、关闭文件
 f.close()

运行结果:

Traceback (most recent call last):
    f.write("hello world")
io.UnsupportedOperation: not writable

代码说明:

  • 由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。
  • 为了保证无论是否出错都能正确地关闭文件,我们可以使用try … finally来解决

安全写法, 代码如下:

try:
    # 1、以读的方式打开文件
    f = open("1.txt", "r")
    # 2、读取文件内容
    f.write("xxxxx")

except IOError as e:
    print("文件操作出错", e)

finally:
    # 3、关闭文件
    f.close()

运行结果:

文件操作出错 not writable

这种方法虽然代码运行良好,但是缺点就是代码过于冗长,并且需要添加try-except-finally语句,不是很方便,也容易忘记.

在这种情况下,Python提供了 with 语句的这种写法,既简单又安全,并且 with 语句执行完成以后自动调用关闭文件操作,即使出现异常也会自动调用关闭文件操作

with 语句的示例代码:

# 为了简化读取文件的操作, python提供了 with 语句的这种写法,既简单又安全,
#  当with语句执行完成,那么关闭文件操作自动完成, 即使有异常以后关闭文件
with open("1.txt", "a") as file:
    # file_data = file.read()
    # print(file_data)
    file.write("abc\n")

2.2 上下文管理器

一个类只要实现了__enter__()和__exit__()这个两个方法,通过该类创建的对象我们就称之为上下文管理器。

上下文管理器可以使用 with 语句,with语句之所以这么强大,背后是由上下文管理器做支撑的,也就是说刚才使用 open 函数创建的文件对象就是就是一个上下文管理器对象。

自定义上下文管理器类,模拟文件操作:

定义一个File类,实现 __enter__() 和 __exit__()方法,然后使用 with 语句来完成操作文件, 示例代码:

# 上下文管理器: 在类里面实现__enter__ 和 __exit__方法 创建对象就是上下文管理器

# 自定义上下文管理器类
class File(object):
    def __init__(self, file_name, file_mode):
        self.file_name = file_name
        self.file_mode = file_mode

    def __enter__(self):
        # 上文方法,负责返回操作对象资源,比如:文件对象,数据库连接对象
        self.file = open(self.file_name, self.file_mode)
        return self.file

    # 当with语句执行完成以后自动执行__exit__方法
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 下文方法,负责释放对象资源,比如: 关闭文件,关闭数据库连接对象
        print("over")
        self.file.close()

# with 语句结合上下文管理器对象使用
with File("1.txt", "r") as file:
    # file_data = file.read()
    # print(file_data)
    file.write("abc\n")


运行结果:

D:\Python\python.exe E:/PythonResources/day11/04.自定义上下文管理器.py
over
Traceback (most recent call last):
  File "E:\PythonResources\day11\04.自定义上下文管理器.py", line 24, in <module>
    file.write("abc")
io.UnsupportedOperation: not writable
    
# 由此可见,不管是否是正常执行的。都会自动进行关闭文件操作,释放资源

代码说明:

  • __enter__表示上文方法,需要返回一个操作文件对象
  • __exit__表示下文方法,with语句执行完成会自动执行,即使出现异常也会执行该方法。

2.3 上下文管理器的另外一种实现方式

假如想要让一个函数成为上下文管理器,Python 还提供了一个 @contextmanager 的装饰器,更进一步简化了上下文管理器的实现方式。通过 yield 将函数分割成两部分,yield 上面的语句在 __enter__ 方法中执行,yield 下面的语句在 __exit__ 方法中执行,紧跟在 yield 后面的参数是函数的返回值。

from contextlib import contextmanager


# 加上装饰器这个代码,那么下面函数创建的对象就是一个上下文管理器
@contextmanager
def my_open(file_name, file_mode):

    try:
        file = open(file_name, file_mode)
        # yield关键之前的代码可以认为是上文方法,负责返回操作对象资源
        yield file
    except Exception as e:
        print(e)
    finally:
        print("over")
        # yield关键字后面的代码可以任务是下文方法,负责释放操作对象的资源
        file.close()


# 普通函数不能结合with语句使用,with语句结合上下文管理器
with my_open("1.txt", "r") as file:
    # file_data = file.read()
    # print(file_data)
    file.write('abc')
  • Python 提供了 with 语句用于简化资源释放的操作,使用 with 语句操作建立在上下文管理器(实现__enter__和__exit__)的基础上
  • Python 还提供了一个 @contextmanager 装饰器,更进一步简化上下管理器的实现,让一个函数可以成为上下文管理器,结合 with 语句来使用

3. 生成器的创建方式

3.1 生成器的介绍

根据程序员制定的规则循环生成数据,当条件不成立时则生成数据结束。数据不是一次性全部生成处理,而是使用一个,再生成一个,可以节约大量的内存

3.2 创建生成器的方式

  1. 生成器推导式
  2. yield 关键字

生成器推导式:

  • 与列表推导式类似,只不过生成器推导式使用小括号
# 生成器:根据程序员指定的算法规则循环生成数据,当条件不成立时生成数据就结束,提示:不是一次性把所有数据全部生成出来
# 而是使用一次生成一次,这样可以节省大量的内存空间。

# 生成器的创建有两种方式

# 1. 生成器推导式,把列表推导式中的中括号改成小括号即可
# 2. yield关键字,只要在函数里面看到有yield那么这个函数可以任务是一个生成器


# 创建了生成器
# my_generator = [value * 2 for value in range(3)]
# print(type(my_generator)) # <class 'list'>
# print(my_generator) # [0, 2, 4]

my_generator = (value * 2 for value in range(3))
print(type(my_generator)) # <class 'generator'>
print(my_generator) # <generator object <genexpr> at 0x000002459DF043C0>

# # 生成器取值使用next函数获取生成器中的下一个值
# value = next(my_generator)
# print(value)
# value = next(my_generator)
# print(value)
# value = next(my_generator)
# print(value)

# # 当生成器已经没有值时,会抛出StopIteration,表示生成器生成数据完毕
# value = next(my_generator)
# print(value)
#
# while True:
#     try:
#         value = next(my_generator)
#         print(value)
#     except Exception as e:
#         break # 跳出循环表示取值完成

# for循环内部循环调用next函数获取生成器中的下一值,当出现异常for循环内部自动进行了异常捕获。
for value in my_generator:
    print(value)

代码说明:

  • next 函数获取生成器中的下一个值
  • for 循环遍历生成器中的每一个值

运行结果:

<class 'generator'>
<generator object <genexpr> at 0x000002E5854B43C0>
0
2
4

yield 关键字:

  • 只要在def函数里面看到有 yield 关键字那么就是生成器
# 在函数里面看到有yield关键字,那么这个函数就是生成器了
def my_generator():
    for i in range(3):
        print("开始生成数据啦...")
        # 当程序执行到yield关键字的时候代码暂停并把结果返回,再次启动生成器的时候会在暂停的位置继续往下执行
        yield i
        print("上一次的数据生成完了...")


result = my_generator()
print(result)

# # 获取生成器下一个值
# # 生成器把所有数据生成完毕后,再次其它生成器会抛出一个StopIteration异常
#
# value = next(result)
# print(value)
#
# value = next(result)
# print(value)
#
# value = next(result)
# print(value)
#
# value = next(result)
# print(value)

for value in result:
    print(value)

代码说明:

  • 代码执行到 yield 会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
  • 生成器如果把数据生成完成,再次获取生成器中的下一个数据会抛出一个StopIteration 异常,表示停止迭代异常
  • while 循环内部没有处理异常操作,需要手动添加处理异常操作
  • for 循环内部自动处理了停止迭代异常,使用起来更加方便,推荐大家使用。

运行结果:

<generator object my_generator at 0x000001A15CBFDA80>
开始生成数据啦...
0
上一次的数据生成完了...
开始生成数据啦...
1
上一次的数据生成完了...
开始生成数据啦...
2
上一次的数据生成完了...

Process finished with exit code 0

3.3 生成器的使用场景

数学中有个著名的斐波拉契数列(Fibonacci),数列中第一个数为0,第二个数为1,其后的每一个数都可由前两个数相加得到:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …

现在我们使用生成器来实现这个斐波那契数列,每次取值都通过算法来生成下一个数据, 生成器每次调用只生成一个数据,可以节省大量的内存。

def fibonacci(num):
	# 初始化前两个值
    a = 0
    b = 1

    # 记录生成fibonacci数字的下标
    current_index = 0
	# 循环判断条件是否成立
    while current_index < num:
        result = a
        a, b = b, a + b
        current_index += 1
        # 代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
        yield result

# 创建生成器
fib = fibonacci(5)
# 遍历生成的数据
for value in fib:
    print(value)

运行结果:

0
1
1
2
3

3.4 小结

  • 生成器是根据算法生成数据的一种机制,每次调用生成器只生成一个值,可以节省大量内存。
  • 生成器的创建有两种方式:
      1. 生成器推导式
      1. yield 关键字

4. 深拷贝和浅拷贝

4.1 浅拷贝

copy函数是浅拷贝,只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象。

不可变类型的浅拷贝示例代码:

import copy  # 使用浅拷贝需要导入copy模块

# 不可变类型有: 数字、字符串、元组

a1 = 123123
b1 = copy.copy(a1)  # 使用copy模块里的copy()函数就是浅拷贝了
# 查看内存地址
print(id(a1))
print(id(b1))

print("-" * 10)
a2 = "abc"
b2 = copy.copy(a2)
# 查看内存地址
print(id(a2))
print(id(b2))

print("-" * 10)
a3 = (1, 2, ["hello", "world"])
b3 = copy.copy(a3)
# 查看内存地址
print(id(a3))
print(id(b3))

运行结果:

140459558944048
140459558944048
----------
140459558648776
140459558648776
----------
140459558073328
140459558073328

不可变类型的浅拷贝说明:

  • 通过上面的执行结果可以得知,不可变类型进行浅拷贝不会给拷贝的对象开辟新的内存空间,而只是拷贝了这个对象的引用。

可变类型的浅拷贝示例代码:

import copy # 使用浅拷贝需要导入copy模块

# 可变类型有: 列表、字典、集合

a1 = [1, 2]
b1 = copy.copy(a1) # 使用copy模块里的copy()函数就是浅拷贝了
# 查看内存地址
print(id(a1))
print(id(b1))
print("-" * 10)
a2 = {"name": "张三", "age": 20}
b2 = copy.copy(a2)
# 查看内存地址
print(id(a2))
print(id(b2))
print("-" * 10)
a3 = {1, 2, "王五"}
b3 = copy.copy(a3)
# 查看内存地址
print(id(a3))
print(id(b3))

print("-" * 10)
a4 = [1, 2, [4, 5]]
# 注意:浅拷贝只会拷贝父对象,不会对子对象进行拷贝
b4 = copy.copy(a4) # 使用copy模块里的copy()函数就是浅拷贝了
# 查看内存地址
print(id(a4))
print(id(b4))
print("-" * 10)
# 查看内存地址
print(id(a4[2]))
print(id(b4[2]))

# 修改数据
a4[2][0] = 6

# 子对象的数据会受影响
print(a4)
print(b4)

运行结果:

139882899585608
139882899585800
----------
139882919626432
139882919626504
----------
139882919321672
139882899616264
----------
139882899587016
139882899586952
----------
139882899693640
139882899693640
[1, 2, [6, 5]]
[1, 2, [6, 5]]

可变类型的浅拷贝说明:

  • 通过上面的执行结果可以得知,可变类型进行浅拷贝只对可变类型的第一层对象进行拷贝,对拷贝的对象会开辟新的内存空间进行存储,子对象不进行拷贝。

4.2 深拷贝

deepcopy函数是深拷贝, 只要发现对象有可变类型就会对该对象到最后一个可变类型的每一层对象就行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储。

不可变类型的深拷贝示例代码:

import copy  # 使用深拷贝需要导入copy模块

# 不可变类型有: 数字、字符串、元组

a1 = 1
b1 = copy.deepcopy(a1)  # 使用copy模块里的deepcopy()函数就是深拷贝了
# 查看内存地址
print(id(a1))
print(id(b1))
print("-" * 10)
a2 = "张三"
b2 = copy.deepcopy(a2)
# 查看内存地址
print(id(a2))
print(id(b2))
print("-" * 10)
a3 = (1, 2)
b3 = copy.deepcopy(a3)
# 查看内存地址
print(id(a3))
print(id(b3))
print("-" * 10)

# 注意: 元组里面要是有可变类型对象,发现对象有可变类型就会该对象到最后一个可变类型的每一层对象进行拷贝
a4 = (1, ["李四"])
b4 = copy.deepcopy(a4)
# 查看内存地址
print(id(a4))
print(id(b4))
print("-" * 10)
# 元组里面的可变类型子对象也会进行拷贝
print(id(a4[1]))
print(id(b4[1]))

运行结果:

9289120
9289120
----------
140115621848320
140115621848320
----------
140115621859592
140115621859592
----------
140115602480584
140115621834568
----------
140115602328136
140115602436168

不可变类型的深拷贝说明:

  • 通过上面的执行结果可以得知:
    • 不可变类型进行深拷贝如果子对象没有可变类型则不会进行拷贝,而只是拷贝了这个对象的引用,否则会对该对象到最后一个可变类型的每一层对象就行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储

可变类型的深拷贝示例代码:

import copy  # 使用深拷贝需要导入copy模块

# 可变类型有: 列表、字典、集合

a1 = [1, 2]
b1 = copy.deepcopy(a1)  # 使用copy模块里的deepcopy()函数就是深拷贝了
# 查看内存地址
print(id(a1))
print(id(b1))
print("-" * 10)
a2 = {"name": "张三"}
b2 = copy.deepcopy(a2)
# 查看内存地址
print(id(a2))
print(id(b2))
print("-" * 10)
a3 = {1, 2}
b3 = copy.deepcopy(a3)
# 查看内存地址
print(id(a3))
print(id(b3))
print("-" * 10)

a4 = [1, 2, ["李四", "王五"]]
b4 = copy.deepcopy(a4)  # 使用copy模块里的deepcopy()函数就是深拷贝了
# 查看内存地址
print(id(a4))
print(id(b4))

# 查看内存地址
print(id(a4[2]))
print(id(b4[2]))
a4[2][0] = "王五"
# 因为列表的内存地址不同,所以数据不会收到影响
print(a4)
print(b4)

运行结果:

140348291721736
140348291721928
----------
140348311762624
140348311221592
----------
140348311457864
140348291752456
----------
140348291723080
140348291723144
140348291723208
140348291723016
[1, 2, ['王五', '王五']]
[1, 2, ['李四', '王五']]

可变类型的深拷贝说明:

  • 通过上面的执行结果可以得知, 可变类型进行深拷贝会对该对象到最后一个可变类型的每一层对象就行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储。

4.3 浅拷贝和深拷贝的区别

  • 浅拷贝最多拷贝对象的一层
  • 深拷贝可能拷贝对象的多层

4.4 总结

  • 浅拷贝使用copy.copy函数
  • 深拷贝使用copy.deepcopy函数
  • 不管是给对象进行深拷贝还是浅拷贝,只要拷贝成功就会开辟新的内存空间存储拷贝的对象。
  • 浅拷贝和深拷贝的区别是:
    • 浅拷贝最多拷贝对象的一层,深拷贝可能拷贝对象的多层。

5. 正则表达式的概述

5.1 正则表达式的介绍

在实际开发过程中经常会有查找符合某些复杂规则的字符串的需要,比如:邮箱、图片地址、手机号码等,这时候想匹配或者查找符合某些规则的字符串就可以使用正则表达式了。

5.2 正则表达式概念

正则表达式就是记录文本规则的代码

5.3 正则表达式的样子

0\d{2}-\d{8} 这个就是一个正则表达式,表达的意思是匹配的是座机号码

5.4 正则表达式的特点

  • 正则表达式的语法很令人头疼,可读性差
  • 正则表达式通用行很强,能够适用于很多编程语

6. re模块介绍

6.1 re模块的介绍

在Python中需要通过正则表达式对字符串进行匹配的时候,可以使用一个 re 模块

# 导入re模块
import re

# 使用match方法进行匹配操作
result = re.match(正则表达式,要匹配的字符串)

# 如果上一步匹配到数据的话,可以使用group方法来提取数据
result.group()

6.2 re模块的使用

re.match() 根据正则表达式从头开始匹配字符串数据

import re


# 使用match方法进行匹配操作
result = re.match("hello","hello world")
# 获取匹配结果
info = result.group()
print(info)

运行结果:

hello

7. 匹配单个字符

代码功能
.匹配任意1个字符(除了\n)
[ ]匹配[ ]中列举的字符
\d匹配数字,即0-9
\D匹配非数字,即不是数字
\s匹配空白,即 空格,tab键
\S匹配非空白
\w匹配非特殊字符,即a-z、A-Z、0-9、_、汉字
\W匹配特殊字符,即非字母、非数字、非汉字

7.1 示例: . 匹配任意1个字符(除了\n)

import re

ret = re.match(".","M")
print(ret.group())

ret = re.match("t.o","too")
print(ret.group())

ret = re.match("t.o","two")
print(ret.group())

运行结果:

M
too
two

7.2 示例:[] 匹配[ ]中列举的字符

import re

# 如果hello的首字符小写,那么正则表达式需要小写的h
ret = re.match("h","hello Python") 
print(ret.group())


# 如果hello的首字符大写,那么正则表达式需要大写的H
ret = re.match("H","Hello Python") 
print(ret.group())

# 大小写h都可以的情况
ret = re.match("[hH]","hello Python")
print(ret.group())
ret = re.match("[hH]","Hello Python")
print(ret.group())
ret = re.match("[hH]ello Python","Hello Python")
print(ret.group())

# 匹配0到9第一种写法
ret = re.match("[0123456789]Hello Python","7Hello Python")
print(ret.group())

# 匹配0到9第二种写法
ret = re.match("[0-9]Hello Python","7Hello Python")
print(ret.group())

ret = re.match("[0-35-9]Hello Python","7Hello Python")
print(ret.group())

# 下面这个正则不能够匹配到数字4,因此ret为None
ret = re.match("[0-35-9]Hello Python","4Hello Python")
# print(ret.group())

运行结果:

h
H
h
H
Hello Python
7Hello Python
7Hello Python
7Hello Python

7.3 示例:\d 匹配数字,即0-9

import re

# 普通的匹配方式
ret = re.match("嫦娥1号","嫦娥1号发射成功") 
print(ret.group())

ret = re.match("嫦娥2号","嫦娥2号发射成功") 
print(ret.group())

ret = re.match("嫦娥3号","嫦娥3号发射成功") 
print(ret.group())

# 使用\d进行匹配
ret = re.match("嫦娥\d号","嫦娥1号发射成功") 
print(ret.group())

ret = re.match("嫦娥\d号","嫦娥2号发射成功") 
print(ret.group())

ret = re.match("嫦娥\d号","嫦娥3号发射成功") 
print(ret.group())

运行结果:

嫦娥1号
嫦娥2号
嫦娥3号
嫦娥1号
嫦娥2号
嫦娥3

7.4 示例:\D 匹配非数字,即不是数字

import re

match_obj = re.match("\D", "f")
if match_obj:
    # 获取匹配结果
    print(match_obj.group())
else:
    print("匹配失败")

运行结果:

f

7.5 示例:\s 匹配空白,即空格,tab键

import re

# 空格属于空白字符
match_obj = re.match("hello\sworld", "hello world")
if match_obj:
    result = match_obj.group()
    print(result)
else:
    print("匹配失败")



# \t 属于空白字符
match_obj = re.match("hello\sworld", "hello\tworld")
if match_obj:
    result = match_obj.group()
    print(result)
else:
    print("匹配失败")

运行结果:

hello world
hello world

7.6 示例:\S 匹配非空白

import re

match_obj = re.match("hello\Sworld", "hello&world")
if match_obj:
result = match_obj.group()
print(result)
else:
print("匹配失败")



match_obj = re.match("hello\Sworld", "hello$world")
if match_obj:
result = match_obj.group()
print(result)
else:
print("匹配失败")

运行结果:

hello&world  
hello$world

7.7 示例:\w 匹配非特殊字符,即a-z、A-Z、0-9、_、汉字

import re

# 匹配非特殊字符中的一位
match_obj = re.match("\w", "A")
if match_obj:
    # 获取匹配结果
    print(match_obj.group())
else:
    print("匹配失败")

执行结果:

A

7.8 示例:\W 匹配特殊字符,即非字母、非数字、非汉字、非下划线

# 匹配特殊字符中的一位
match_obj = re.match("\W", "&")
if match_obj:
    # 获取匹配结果
    print(match_obj.group())
else:
    print("匹配失败")

执行结果:

&

7.9 小结

  • . 表示匹配任意1个字符(除了\n)
  • 表示匹配[ ]中列举的1个字符
  • \d 表示匹配一个数字,即0-9
  • \D 表示匹配一个非数字,即不是数字
  • \s 表示匹配一个空白字符,即 空格,tab键
  • \S | 匹配一个非空白字符
  • \w | 匹配一个非特殊字符,即a-z、A-Z、0-9、_、汉字
  • \W | 匹配一个特殊字符,即非字母、非数字、非汉字

8. 匹配多个字符

代码功能
*匹配前一个字符出现0次或者无限次,即可有可无
+匹配前一个字符出现1次或者无限次,即至少有1次
?匹配前一个字符出现1次或者0次,即要么有1次,要么没有
{m}匹配前一个字符出现m次
{m,n}匹配前一个字符出现从m到n次

8.1 示例:* 匹配前一个字符出现0次或者无限次,即可有可无

需求:匹配出一个字符串第一个字母为大小字符,后面都是小写字母并且这些小写字母可 有可无

import re

# *	匹配前一个字符出现0次或者无限次,即可有可无
# match_obj = re.match("t.*o", "to")
# if match_obj:
#     # 获取匹配结果
#     result = match_obj.group()
#     print(result)
# else:
#     print("匹配失败")

ret = re.match("[A-Z][a-z]*","M")
print(ret.group())

ret = re.match("[A-Z][a-z]*","MnnM")
print(ret.group())

ret = re.match("[A-Z][a-z]*","Aabcdef")
print(ret.group())

运行结果:

M
Mnn
Aabcdef

8.2 示例:+ 匹配前一个字符出现1次或者无限次,即至少有1次

需求:匹配一个字符串,第一个字符是t,最后一个字符串是o,中间至少有一个字符

import re

# + 匹配前一个字符串至少出现一次
# match_obj = re.match("t.+o", "trsdfo")
# if match_obj:
#     # 获取匹配结果
#     result = match_obj.group()
#     print(result)
# else:
#     print("匹配失败")

match_obj = re.match("t.+o", "two")
if match_obj:
    print(match_obj.group())
else:
    print("匹配失败")

运行结果:

two

8.3 示例:? 匹配前一个字符出现1次或者0次,即要么有1次,要么没有

需求:匹配出这样的数据,但是https 这个s可能有,也可能是http 这个s没有

# ?: 匹配前一个字符串出现0次或者1次
match_obj = re.match("https?", "http")
if match_obj:
    # 获取匹配结果
    result = match_obj.group()
    print(result)
else:
    print("匹配失败")

运行结果:

https

8.4 示例:{m}、{m,n} 匹配前一个字符出现m次/出现从m到n次

需求:匹配出,8到20位的密码,可以是大小写英文字母、数字、下划线

import re


ret = re.match("[a-zA-Z0-9_]{6}","12a3g45678")
print(ret.group())

ret = re.match("[a-zA-Z0-9_]{8,20}","1ad12f23s34455ff66")
print(ret.group())

运行结果:

12a3g4
1ad12f23s34455ff66

8.5 小结

  • *表示匹配前一个字符出现0次或者无限次,即可有可无
  • +表示匹配前一个字符出现1次或者无限次,即至少有1次
  • ?表示匹配前一个字符出现1次或者0次,即要么有1次,要么没有
  • {m}表示匹配前一个字符出现m次
  • {m,n}表示匹配前一个字符出现从m到n次

9. 匹配开头和结尾

代码功能
^匹配字符串开头
$匹配字符串结尾

9.1 示例:^ 匹配字符串开头

需求:匹配以数字开头的数据

import re

# 匹配以数字开头的数据
match_obj = re.match("^\d.*", "3hello")
if match_obj:
    # 获取匹配结果
    print(match_obj.group())
else:
    print("匹配失败")

运行结果:

3hello

9.2 示例:$ 匹配字符串结尾

需求: 匹配以数字结尾的数据

import re
# 匹配以数字结尾的数据
match_obj = re.match(".*\d$", "hello5")
if match_obj:
    # 获取匹配结果
    print(match_obj.group())
else:
    print("匹配失败")

运行结果:

hello5

9.3 示例:^ 和 $

需求: 匹配以数字开头中间内容不管以数字结尾

match_obj = re.match("^\d.*\d$", "4hello4")
if match_obj:
    # 获取匹配结果
    print(match_obj.group())
else:
    print("匹配失败")

运行结果:

4hello4

9.4 除了指定字符以外都匹配

[^ 指定字符] 表示除了指定字符都匹配

需求: 第一个字符除了aeiou的字符都匹配

import re


match_obj = re.match("[^aeiou]", "h")
if match_obj:
    # 获取匹配结果
    print(match_obj.group())
else:
    print("匹配失败")

执行结果

h

10. 匹配分组

10.1 匹配分组相关正则表达式

代码功能
|匹配左右任意一个表达式
(ab)将括号中字符作为一个分组
\num引用分组num匹配到的字符串
(?P<name>)分组起别名
(?P=name)引用别名为name分组匹配到的字符串

10.2 示例:| 匹配左右任意一个表达式

需求:在列表中[“apple”, “banana”, “orange”, “pear”],匹配apple和pear

import re

# 水果列表
fruit_list = ["apple", "banana", "orange", "pear"]

# 遍历数据
for value in fruit_list:
    # |    匹配左右任意一个表达式
    match_obj = re.match("apple|pear", value)
    if match_obj:
        print("%s是我想要的" % match_obj.group())
    else:
        print("%s不是我要的" % value)

执行结果:

apple是我想要的
banana不是我要的
orange不是我要的
pear是我想要的

10.3 示例:( ) 将括号中字符作为一个分组

需求:匹配出163、126、qq等邮箱

# 匹配出163、126、qq等邮箱
# \.: 表示对正则表达式里面的.进行了转义,变成了一个普通点,只能匹配.字符
# (163|126|qq) 表示一个分组,出现一个小括号就表示一个分组,分组是从1开始的
# 如果出现多个小括号,分组的顺序是从左到右一次排序
import re

match_obj = re.match("[a-zA-Z0-9_]{4,20}@(163|126|qq|sina|yahoo)\.com", "hello@163.com")
if match_obj:
     # 获取整个匹配的数据,如果使用分组数的化,默认是0
    print(match_obj.group())
    # 获取分组数据
    print(match_obj.group(1))
else:
    print("匹配失败")

执行结果:

hello@163.com
163

需求: 匹配qq:10567这样的数据,提取出来qq文字和qq号码

import re

match_obj = re.match("(qq):([1-9]\d{4,10})", "qq:10567")

if match_obj:
    print(match_obj.group())
    # 分组:默认是1一个分组,多个分组从左到右依次加1
    print(match_obj.group(1))
    # 提取第二个分组数据
    print(match_obj.group(2))
else:
    print("匹配失败")

执行结果:

qq
10567

10.4 示例:\num 引用分组num匹配到的字符串

需求:匹配出<html>hh</html>

match_obj = re.match("<[a-zA-Z1-6]+>.*</[a-zA-Z1-6]+>", "<html>hh</div>")

if match_obj:
    print(match_obj.group())
else:
    print("匹配失败")

match_obj = re.match("<([a-zA-Z1-6]+)>.*</\\1>", "<html>hh</html>")

if match_obj:
    print(match_obj.group())
else:
    print("匹配失败")
    
# \num	引用分组num匹配到的字符串
match_obj = re.match("<([a-zA-Z1-6]+)>.*</\\1>", "<html>hh</div>")
if match_obj:

    result = match_obj.group()
    print(result)
else:
    print("匹配失败")

运行结果:

<html>hh</div>
<html>hh</html>
匹配失败

需求:匹配出<html><h1>www.itcast.cn</h1></html>

match_obj = re.match("<([a-zA-Z1-6]+)><([a-zA-Z1-6]+)>.*</\\2></\\1>", "<html><h1>www.hello.cn</h1></html>")

if match_obj:
    print(match_obj.group())
else:
    print("匹配失败")

运行结果:

<html><h1>www.hello.cn</h1></html>

10.5 示例:(?P<name>) (?P=name) 分组起别名/引用别名为name分组匹配到的字符串

需求:匹配出<html><h1>www.hello.cn</h1></html>

match_obj = re.match("<(?P<name1>[a-zA-Z1-6]+)><(?P<name2>[a-zA-Z1-6]+)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.itcast.cn</h1></html>")

if match_obj:
    print(match_obj.group())
else:
    print("匹配失败")

运行结果:

<html><h1>www.hello.cn</h1></html>

10.6 小结

  • | 表示匹配左右任意一个表达式
  • (ab) 表示将括号中字符作为一个分组
  • \num 表示引用分组num匹配到的字符串
  • (?P<name>) 表示分组起别名
  • (?P=name) 表示引用别名为name分组匹配到的字符串
  • (分组数据):分组数是从左到右的方式进行分配的,最左边的是第一个分组,依次类推
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

脑子是空的啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值