python文件操作处理 (六)

文件操作

冯诺依曼体系架构

在这里插入图片描述

  • CPU由运算器和控制器组成
  • 运算器,完成各种算数运算、逻辑运算、数据传输等数据加工处理
  • 控制器,控制计算机各部件协调运行
  • 存储器,用于记忆程序和数据,例如内存
  • 输入设备,将数据或者程序输入到计算机中,例如键盘、鼠标
  • 输出设备,将数据或程序的处理结果展示给用户,例如显示器、打印机等
    一般说IO操作,指的是文件IO,如果指的是网络IO,都会直接说网络IO

文件IO常用操作

在这里插入图片描述

打开操作

open(file, mode=‘r’, buffering=-1, encoding=None, errors=None, newline=None, closefd= True, opener=None)

  • 打开一个文件,返回一个文件对象(流对象)和文件描述符。打开文件失败,则返回异常
  • 基本使用:
    创建一个文件test,然后打开它,用完关闭
f = open("tese") # file对象
# windows <_io.TextIOWrapper name='tese' mode='r' encoding='cp936'>
# linux <_io.TextIOWrapper name='tese' mode='r' encoding='UTF-8'>
print(f.read()) # 读取文件
f.close() # 关闭文件
12345
  • 文件操作中,最常用的操作就是 “读”“写”
  • 文件访问的模式有两种:
  • 文本模式和二进制模式。不同模式下,操作函数不相同,表现的结果也不一样
    注:
  • windows中使用codepage代码页,可以认为每一个代码页就是一张编码表。cp936等同于GBK

open的参数

  • file
  • 打开或者要创建的文件名。如果不指定路径,默认是当前路径
  • mode模式
    在这里插入图片描述
    在上面的例子中,可以看到默认是文本打开模式,且是只读的

r 模式

  • 只读打开文件,如果使用write方法,会抛异常
  • 如果文件不存在,抛出FileNotFoundError异常
  • open模式是只读模式r打开已存在的文件
    在这里插入图片描述

w 模式

  • 表示只写打开方式,如果读取则抛出异常
  • 如果文件不存在,则直接创建文件
  • 如果文件存在,则清空文件内容
    在这里插入图片描述

x 模式

  • 文件不存在,创建文件,并只写方式打开
  • 文件存在,抛出FileExistsError异常
    在这里插入图片描述

a 模式

  • 文件存在,只写打开,追加内容
  • 文件不存在,则创建后,只写打开,追加内容
    在这里插入图片描述

总结

  • r 是只读,wxa 都是只写
  • wxa 模式都可以产生新文件
    w 不管文件存在与否,都会生成全新内容的文件
    a 不管文件是否存在,都能在打开的文件尾部追加
    x 必须要求文件事先不存在,自己创建一个新文件

文本模式 t

  • 字符流,将文件的字节按照某种字符编码理解,按照字符操作。open的默认mode就是 rt

二进制模式 b

  • 字节流,将文件就按照字节理解,与字符编码无关。二进制模式操作时,字节操作使用bytes类型
f = open("test3",'rb') # 二进制只读
s = f.read()
print(type(s)) # bytes
print(s)
f.close() # 关闭文件

f = open("tese3",'wb') # IO对象
s = f.write("你好".encode()) # encode默认使用UTF-8编码,一个中文3个字节
print(s) #   6
f.ckose()
12345678910

+ 模式

  • 为r、w、a、x提供缺失的读或写功能,但是获取文件对象依旧按照r、w、a、x自己的特征
  • +模式不能单独使用,可以认为它是为前面的模式字符做增强功能的

文件指针

  • 文件指针,指向当前字节位置
  • mode=r,指针起始在0
  • mode=a,指针起始在EOF
  • tell()显示指针当前位置
  • seek(offset[,whence]) 移动文件指针位置。offest偏移多少字节,whence从哪里开始

文本模式下

whence 0 缺省值,表示从头开始,offest只能正整数
whence 1 表示从当前位置,offest只接受0
whence 2 表示从EOF开始,offest只接受0
在这里插入图片描述

  • 文本模式支持从开头向后偏移的方式
  • whence为1表示从当前位置开始偏移,但是只支持偏移0,相当于原地不动,所以没什么用
  • whence为2表示从EOF开始,只支持偏移0,相当于移动文件指针到EOF
  • seek是按照字节偏移的
  • read在文本模式是按照字符读取的

二进制模式下

whence 0 缺省值,表示从头开始,offest只能正整数
whence 1 表示从当前位置,offest可正可负
whence 2 表示从EOF开始,offest可正可负
在这里插入图片描述
二进制模式支持任意起点的便宜,从头、从尾、从中间位置开始
向后seek可以超界,但是向前seek的时候,不能超界,否则抛异常

buffering:缓冲区

  • -1表示使用缺省大小的buffer。如果是二进制模式,使用价格io.DEFAULT_BUFFER_SIZE值,默认是4096或者8192。如果是文本模式,如果是终端设备,实行缓存方式,如果不是,则使用二进制模式的策略
  • 0,只在二进制模式使用,表示关buffer
  • 1,只在文本模式使用,表示使用行缓冲。意思就是见到换行符就flush
  • 大于1,用于指定buffer的大小

buffer缓冲区

  • 缓冲区一个内存空间,一般来说是一个FIFO队列,到缓冲区满了或者达到阈值,数据才会flush到磁盘。

fluch()

  • 将缓冲区数据写入磁盘

chlose()

  • 关闭前会调用flush()
  • io.DEFAULT_BUFFER_SIZE 缺省缓冲区大小,字节

二进制模式

import io

f = open('test','w+b')
print(io.DEFAULT_BUFFER_SIZE) # 8192
f.write("hello".encode())
# cat test
f.seek(0) # 指针回0
# cat test
f.write('python'.encode())
f.flush()
f.close()


f = open('test','w+b',4) # 缓冲区大小
f.write(b'hello')
# cat test
f.write(b'python')
# cat test
f.close()
12345678910111213141516171819

文本模式

import io

# buffering=1,使用行缓冲
f = open('test','w+',1)
f.write('hello') # cat test
f.write('python'*4) # cat test
f.write('\n') # cat test
f.write('hi\nPython') # cat test
f.close()

#buffering>1,使用指定大小的缓冲区
f = open('test','w+',15)
f.write('hello') # cat test
f.write('python') # cat test
f.write('hi\n') # cat test
f.write('\nPython') # cat test
f.write('a' * (io.DEFAULT_BUFFER_SIZE - 20)) # 设置为大于1没什么用
f.write('\nhelloPython')
f.close()
12345678910111213141516171819
  • buffering=0
  • 这是一种特殊的二进制模式,不需要内存的buffer,可以看作是一个FIFO的文件
f = open('test','wb+',0)
f.write('hello') # cat test
f.write('python'*4) # cat test
f.write('\n') # cat test
f.write('hi\nPython') # cat test
f.close()
123456

在这里插入图片描述

  1. 文本模式,一般都用默认缓冲区大小
  2. 二进制模式,是一个个字节的操作,可以指定buffer的大小
  3. 一般来说,默认缓冲区大小是个比较好的选择,除非明确知道,否则不调整它
  4. 一般编程中,明确知道需要写磁盘了,都会手动调用一次flush,而不是等到自动flush或者close的时候

encoding:编码,仅文本模式使用

  • None表示使用缺省编码,依赖操作系统。windows、linux下测试如下代码
f = open('test','w')
f.write('啊')
f.close()
123

windows下缺省GBK(0xB0A1),linux下缺省UTF-8(0xE5 95 8A)

其他参数

errors

  • 什么样的编码错误将被捕获
  • None和strict表示有编码错误将抛出ValueError异常;ignore表示忽略

newline

  • 文本模式中,换行的转换。可以用None、“空串、’\r’、’\n’、’\r\n’
  • 读时,None表示’\r’、’\n’、’\r\n’都被转换为’\n’;’'空串表示不会自动转换通用换行符;其它合法字符表示换行符就是指定字符,就会按照指定字符分行
  • 写时,None表示’\n’都会被替换为系统缺省行分割符os.linesep;’\n’或’‘空串表示’\n’不替换;其它合法字符表示’\n’会被替换为指定的字符
f = open('/etc/test', 'w')
# newline缺省为None,windows下会把\n替换为\r\n
f.write('python\rwww.python.org\nwww.baidu.com\r\npython3')
# 真正写入的是
# 'python\rwww.python.org\rwww.baidu.com\r\rpython3'
f.close()

newlines = [None, '', '\n', '\r\n']
for nl in newlines:
    f = open('/etc/test', newline=nl) # 缺省替换所有换行符
    print(f.readlines())
    f.close()
# 运行结果如下
# ['python\n', 'www.python.org\n', 'www.baidu.con\n', '\n', 'python3']
# 常见换行符都替换为\n
# ['python\r', 'www.python.org\r\n', 'www.baidu.con\r', '\r\n', 'python3']
# ''表示什么都不做
# ['python\rwww.python.org\r\n', 'www.baidu.con\r\r\n', 'python3']
#  \n做作为换行符
# ['python\rwww.python.org\r\n', 'www.baidu.con\r\r\n', 'python3']
# \r\n作为换行符
123456789101112131415161718192021

closefd

  • 表示文件描述符,True表示关闭它。False会在文件关闭后保持这个描述符。fileobj.fileno()查看

read

  • read(size=-1)
  • size表示读取的多少个字符或字节;负数或者None表示读取到EOF
f = open('/etc/test', 'r+')
f.write("hello")
f.write('\n')
f.write('你好')
f.seek(0)
f.read(7)
f.close()
# 二进制
f = open('/etc/test', 'rb+')
f.read(7)
f.read(1)
f.close()
123456789101112

行读取

readline(size=1)

  • 一行行读取文件内容。size设置一次能读取行内几个字符或字节

readlines(hint=-1)

  • 读取所有行的列表。指定hint则返回指定的行数
# 按行迭代
f = open('test') # 返回可迭代对象

for line in f:
	print(line.encode())

f.close()
1234567

write

  • write(s),把字符串s写入到文件中并返回字符的个数
  • writelines(lines),将字符串列表写入文件
f = open('test', 'w+')

line = ['abc', '123\n', 'hello'] # 提供换行符
f.writelines(lines)

f.seek(0)
print(f.read())
f.close()
12345678

close

  • flush并关闭文件对象
  • 文件已经关闭,再次关闭没有任何效果

其他

在这里插入图片描述

上下文管理

  • 在Linux中,执行
lst = []
for _ in range(2000):
	lst.append(open('test'))
# OSError: [Errno 24] Too many open files: 'test'

print(len(lst))
123456

lsof:列出打开的文件

没有就# yum install lsof

$ ps aux | grep python
$ lsof -p 9255 | grep test |wc -1
$ ulimit -a
123
  • ps命令返回进程,grep处python进程ID
  • lsof -p 进程号,列出该进程的所有文件描述符,grep出test文件的文件描述符,wc统计
  • ulimit -a 查看所有限制。其中open files就是打开问价数的限制,默认1024
for x in lst:
	x.close()
12

将文件一次关闭,然后就可以继续打开了。再看一次lsof
1、异常处理

  • 当出现异常的时候,拦截异常。但是,因为很多代码都可能出现OSError异常,还不好判断异常就是因为资源限制产生的
f = open('test')
try:
	f.writs("abc") # 文件只读,写入失败
finally:
	f.close() # 这样才行
12345
  • 使用finally可以保证打开的文件可以被关闭
    2、上下文管理
    一种的特殊语法,交给解释器去释放文件对象

上下文管理

del f
with open('test') as f:
	f.write('abc') # 文件只读,写入失败

#测试f是否关闭
f1.closed
123456
  • 对于类似文件对象的IO对象,一般来说都需要在不使用的时候关闭、注销,以释放资源
  • IO被打开的时候,会获得一个文件描述符。计算机资源是有限的,所以操作系统都会做限制。就是为了保护计算机的资源不要被完全耗尽,计算资源是共享的,不是独占的
  • 一般情况下,除非特别明确的知道资源情况,否则不要提高资源的限制值来解决问题

StringIO

  • io模块中的类
    from io impoet StringIO
  • 内存中,开辟的一个文本模式的buffer,可以像文件对象一样操作它
  • 当close方法被调用的时候,这个buffer会被释放

getvalue()

  • 获取全部内容。跟文件指针没有关系
from io impoer StringIO

# 内存中构建
sio = StringIO() # 像文件对象一样操作
print(sio.readable(), sio.writable(), sio.seekable())
sio.write("hello\nPython)
sio.seek(0)
print(sio.readline())
print(sio.getvalue()) # 无视指针,输出全部内容
sio.close()
12345678910

好处

  • 一般来说,磁盘的操作比内存的操作要慢的多,内存足够的情况下,一般的优化思想是少落地,减少磁盘IO的过程,可以大大提高程序的运行效率

BytesIO

  • io模块中的类
    from io impoet BytesIO
  • 内存中,开辟的一个二进制模式的buffer,可以像文件对象一样操作它
  • 当close方法被调用的时候,这个buffer会被释放
from io import BytesIO # 内存中构建
bio = BytesIO()
print(bio.readable(), bio.writable(), bio.seekable())
bio.write(b'hello\nPython')
bio.seek(0)
print(bio.readline())
print(bio.getvalue()) # 无视指针,输出全部内容
bio.close()
12345678

file-like对象

  • 类文件对象、可以像文件对象一样操作
  • socket对象、输入输出对象(stdin、stdout)都是类文件对象
from sys import stdout, stderr
f = stdout
print(type(f))
f.write('hello')
f.close()

路径操作模块

3.4版本之前

os.path模块

from os import path
p = path.join('/etc', 'sysconfig', 'network') # 拼接
pring(type(p), p)
print(path.exists(p)) # 存在

print(path.split(p)) # 分割
print(path.dirname(p), path.basename(p)) #路径和基名

print(path.abspath(''),path.abspath('.')) # 绝对路径

print(path.spltdrive('d:/temp/test')) # windows方法

p1 = path.abspath(__file__)
print(p1)
while p1 !=path.dirname(p1):
	dirname = path.ditname(p1)
	print(ditname)
	p1 = dirname
123456789101112131415161718

3.4版本开始

  • 建议使用pathlib模块,提供Path对象来操作。包括目录和文件

pathlib模块

  • from pathlib import Path

目录操作

初始化

p = Path() # 当前目录, Path()、Path('.')、Path('')
p = Path('a', 'b', 'c/d') # 当前目录下的a/b/c/d
p = Path('/etc') # 根下的etc目录
123

路径拼接和分解

  • 操作符 /
    Path对象 / Path对象
    Path对象 / 字符串 或者 字符串 / Path对象
  • 分解
    parts属性,可以返回路径中的每一个部分
  • joinpath
    joinpath(*other)连接多个字符串到Path对象中
from pathlib import Path

p = Path()
p = p / 'a'
p1 = 'b' / p 
p2 = Path('c')
p3 = p2 / p1
print(p3.parts) # ('c', 'b', 'a')
p4 = p3.joinpath('etc', 'init.d', Path('httpd')) # c\b\a\etc\init.d\httpd
print(p4)
12345678910

获取路径

  • str 获取路径字符串
  • bytes 获取路径字符串的bytes
from pathlib import Path

p = Path('/etc')
print(str(p)) # \etc
print(bytes(p)) # b'\\etc'
12345

父目录

  • parent目录的逻辑父目录
  • parents父目录序列,可迭代对象,索引0是直接的父
from pathlib import Path

p = Path('a/b/c/d')
print(p.parent)  # a\b\c
print(p.parent.parent) # a\b
for x in p.parents:
	print(x)
# print(x)执行结果
# a\b\c
# a\b
# a
# .
123456789101112

目录组成部分

  • name、stem、suffix、suffixes、with_suffix(suffix)、whit_name(name)
name 目录的最后一个部分
suffix 目录中最后一个部分的扩展名
stem 目录最后一个部分,没有后缀
name = stem + suffix
suffixes 返回多个扩展名列表
with_suffix(suffix) 有扩展名则替换,无则补充扩展名
whit_name(name) 替换目录最后一个部分并返回一个新的路径
from pathlib import Path

p = Path('/etc/python/mysql.tar.gz')
print(p.name) # mysql.tar.gz
print(p.suffix) # .gz
print(p.suffixes) # ['.tar', '.gz']
print(p.stem) # mysql.tar
print(p.with_name('python-5.tgz')) # \etc\python\python-5.tgz
print(p.with_suffix('.png')) # \etc\python\mysql.tar.png
p = Path('README')
print(p.with_suffix('.txt')) # README.txt
1234567891011

全局方法

  • cwd() 返回当前工作目录
  • home()返回当前家目录

判断方法

  • exists() 目录或者文件是否存在
  • is_dir() 是否是目录,目录存在返回True
  • is_file() 是否是普通文件,文件存在返回True
  • is_symlink() 是否是软链接
  • is_socket() 是否是socket文件
  • is_block_device() 是否是块设备
  • is_char_device() 是否是字符设备
  • is_absolute() 是否是绝对路径

绝对路径

  • resolve() 非Windows,返回一个新的路径,这个新路径就是当前Path对象的绝对路径,如果是软链接则直接被解析
  • absolute() 获取绝对路径
  • rmdir() 删除空目录。没有提供判断目录为空的方法
  • touch(mode=0o666, exist_ok=True) 创建一个文件
  • as_uri() 将路径返回成URI,例如’file:///etc/passwd’
  • mkdir(mode=0o777, parents=False, exist_ok=False)
  • parents,是否创建父目录,True等同于mkdie -p。False时,父目录不存在,则抛出FileNotFoundError
  • exist_ok参数,在3.5版本加入,False时,路径存在。抛出FileExistsError;True时,FileExistsError被忽略
  • iterdir()迭代当前目录,不递归
from pathlib import Path

p = Path()
p /= 'a/b/c/d'
p.exists() # True

#创建目录
p.mkdir() # FileNotFoundError
p.mkdir(parents=True)
p.exists() # True
p.mkdir(parents=True)
p.mkdir(parents=True,exist_ok=True)
p /= 'readme.txt'
p.parent.exists() # False 'a/b/c'
p.mkdir() # FileNotFoundError
p.mkdir(parents=True) # 成功

# 遍历,判断文件类型,如果是目录是否可以判断其是否为空目录?
for x in p.parents[len(p.parents) - 1].iterdir():
	print(x, end='\t')
	if x.is_dir():
		flag = False
		for _ in x.iterdir():
			flag = True
			break

		print('dir', 'Not Empty' if flag else 'Empyt',sep='\t')
	elif x.is_file():
		print('file')
	else:
		print('other file')
12345678910111213141516171819202122232425262728293031

通配符

  • glob(pattern) 通配给定的模式
  • rglob(pattern) 通配给定的模式,递归目录
  • 都返回一个生成器
  • ? 代表一个字符
  • * 表示任意一个字符
  • [abc]或[a-z]表示一个字符
list(p.glob('test*') # 返回当前目录对象下的test开头的文件
list(p.glob('**/*.py') # 递归所有目录,等同rglob
list(p.glob('**/*'))

g = p.rglob('*.py') # 生成器,递归
next(g)
list(p.rglob('*.???')) # 匹配扩展名为3个字符的文件
list(p1.rglob('[a-z]*.???')) # 匹配字母开头的且扩展名是3个字符的文件
12345678

匹配

  • match(pattern)
  • 模式匹配,成功返回True
from pathlib import Path

Path('a/b.py').match('*.py') # True
Path('/a/b/c.py').match('b/*.py') # True
Path('/a/b/c.py').match('a/*.py') # False
Path('/a/b/c.py').match('a/*/*.py') # True
Path('/a/b/c.py').match('a/**/*.py') # True
Path('/a/b/c.py').match('**/*.py') # True
12345678
  • stat()相当于stat命令
  • lstat()同stat(),但如果是符号链接,则显示符号链接本身的文件信息
# ln -s test t

from pathlib import Path
p = Path('test')
p.stat()
p1 = Path('t')
p1.stat()
p1.lstat()
12345678

文件操作

Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
1

使用方法类似内建函数open。返回一个文件对象

  • 3.5增加的新函数
Path.read_bytes()
1

以’rb’读取路径对应文件,并返回二进制流。看源码

Path.read_test(encoding=None, errors=None)
1

以’rt’方式读取路径对应文件,返回文本

Path.write_bytes(data)
1

以’wb’方式写入数据到路径对应文件

Path.write_text(data, encoding=None, errors=None)
1

以’wt’方式写入字符串到路径对应文件

from pathlib import Path

p = Path('my_binary_file')
p.write_bytes(b'Binary file contents')
p.read_bytes() # b'Binary file contents'

p = Path('my_text_file')
p.write_text('Text file contents')
p.read_text() # 'Text file contents'

with p.open() ad f:
	print(f.read(5))
123456789101112

os 模块

  • 操作系统平台
    在这里插入图片描述
  • os.listdie(‘o:/temp’)
    返回指定目录内容列表,不递归
    os也有open、read、write等方法,但是太底层,建议使用内建函数open、read、write,使用方法相似
# 建立一个软链接
$ ln -s test t1
12
  • os.stat(path, *, dir_fd=None,follow_symlinks=True)
  • 本质上调用Linux系统的stat
  • path:路径的string或者bytes,或者fd文件描述符
  • follow_symlinks True返回文件本身信息,False且如果是软链接则显示软链接本身。可以使用os.lstat方法
os.stat('o:/test.txt')
os.stat_result(st_mode=33206, st_ino=281474976710698, st_dev=389224164, st_nlink=1, st_uid=0, st_gid=0, st_size=0, st_atime=1508808249,  st_mtime=1508808249, st_crtime=1508808249)
st_mode=33206 => 100666
os.stat('test')
os.stat_result(st_mode=33204, st_ino=3407875, st_dev=64768, st_nlink=1, st_uid=500, st_gid=500, st_size=3, st_atime=1508690220,  st_mtime=1508690177, st_crtime=1508690177)
12345
  • os.chmod(path, mode, *, dit_fd=None, follow_symlinks=True)
  • os.chmod(‘test’,0o777)
  • os.chown(path,uid,gid)
    改变文件的属主、属组,但需要足够的权限

shutil模块

  • 文件拷贝:使用打开2个文件对象,源文件读取内容,写入目标文件中来完成拷贝过程。但是这样丢失stat数据信息(权限等),因为根本没有复制这些信息过去
  • 如何目录复制,Python提供了一个方便的库shutil(高级文件操作)

copy复制

copyfileobj(fsrc, fdst[, length])

  • 文件对象的复制,fsrc和fdst是open打开的文件对象,复制内容。fdst要求可写,length指定了表示buffer的大小
import shutil

with open('/etc/test', 'r+') as f1:
	f1.write('abcd\n1234')
	f1.flush()
	f1.seek(0) # 指针回0
	with open('/etc/test1', 'w+') as f2:
			shutil.copyfileobj(f1,f2) 
12345678

copyfileobj源码

def copygileobj(fsrc, fdst, length=16*1024):
	"""copy data from file-like object fsrc to file-like object fdst"""
	while 1:
		buf = fsrc.read(length)
		if not buf: # 如果为空则退出循环
			break
		fdst.write(buf)
1234567

copyfile(src, dst, *, follow_symlinks=True)

  • 复制文件内容,不含元数据。src, dst为文件的路径字符串
  • 本质上调用的就是copyfileobj,所以不带元数据二进制内容复制

copymode(src, dst, *, follow_symlinks=True)

  • 仅仅复制权限
    在这里插入图片描述

copystat(src, dst, *, follow_symlinks=True)

  • 复制元数据,stat包含权限

copy(src, dst, *, follow_symlinks=True)

  • 复制文件内容、权限和部分元数据,不包括创建时间和修改时间
  • 本质上调用的是
    copyfile(src, dst, follow_symlinks=follow_symlinks)
    copymode(src, dst, follow_symlinks=follow_symlinks)

copy2

  • 比copy多了复制全部元数据,但需要平台支持
  • 本质上调用的是
    copyfile(src, dst, follow_symlinks=follow_symlinks)
    copystat(src, dst, follow_symlinks=follow_symlinks)

copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False)

  • 递归复制目录。默认使用copy2,也就是带更多的元数据复制
  • src、dst必须是目录,src必须存在,dst必须不存在
  • ignore = func,提供一个callable(src, names) -> ignored_names。提供一个函数,它会被调用。src是源目录,names是os.listdir(src)的结果,就是列出src中的文件名,返回值是要被过滤的文件名的set类型数据
# /etc/temp下有a、b目录
def ignore(src, names):
	ig = filter(lambda x: x.startswith('a'), names) # 忽略a开头的
	return set(ig)

shutil.copytree('/etc/temp', '/etc/python/o', ignore=ignore)
123456

rm 删除

shutil.rmtree(path, ignore_errors=False, οnerrοr=None)

  • 递归删除。如同rm -rf一样危险,慎用
  • 它不是原子操作,有可能删除错误,就会中断,已经删除的就删除了
  • ignore_errors为True,忽略错误。当为False或者omitted时onerror生效
  • onerror为callable,接受函数function、path和execinfo
shutil.rmtree('/etc/bin') # 类似 rm -rf
1

move 移动

move(src, dst, copy_function=copy2)

  • 递归移动文件、目录到目标,返回目标
  • 本身使用的是 os.rename方法
  • 如果不支持rename,如果是目录则copytree再删除源目录
  • 默认使用copy2方法
os.rename('/etc/bin/t.txt', '/etc/temp/t')
os.rename('test3', '/etc/temp/test300')
12
  • shutil还有打包功能。生成tar并亚索。支持zip、gz、bz、xz

csv文件

csv文件简介

  • 参看 RFC 4180
    http://www.ietf.org/rfc/rfc4180.txt
  • 逗号分隔值Comma-Separated Values
  • CSV 是一个被行分隔符、列分隔符划分成行和列的文本文件
  • CSV不指定字符编码
  • 行分隔符为\r\n,最后一行可以没有换行符
  • 列分隔符常为逗号或者制表符
  • 每一行成为一条记录record
  • 字段可以使用双引号括起来,也可以不使用。如果字段中出现了双引号、逗号、换行符必须使用双引号括起来。如果字段的值是双引号,使用两个双引号表示一个转义
  • 表头可选,和字段列对齐就行

手动生成csv文件

from path impoet Path

p = Path('/etc/python/test.csv')
parent = p.parent
if not parent.exists():
	parent.mkdir(parents=True)

csv_body = '''\
id,name,age,comment
1,zs,18,:I'm 18"
2,ls,20,"this is a ""test"" string."
3,ww,23,"你好

计算机
"
'''
p.write_text(csv_body)
1234567891011121314151617

csv 模块

reader(csvfile, dialect=‘excel’, **fmtparams)

  • 返回reader 对象,是一个行迭代器
  • 默认使用excel方言,如下:
    • delimiter 列分隔符,逗号
    • lineterminator 行分隔符\r\n
    • quotechar 字段的引用符号,缺省为 " 双引号
    • 双引号的处理
      • doublequote 双引号的处理,默认为True。如果碰到数据中有双引号,而quotechar也是双引号,True则使用2个双引号表示,False表示使用转义字符将作为双引号的前缀
      • escapechar 一个转义字符,默认为None
      • writer = csv.writer(f, doublequote=False, escapechar=’@’)遇到双引号,则必须提供转义字符
    • quoting 指定双引号的规则
      • QUOTE_ALL 所有字段
      • QUOTE_MINIMAL 特殊字符字段,Excel方言使用该规则
      • QUOTE_NONNUMERIC 非数字字段
      • QUOTE_NONE 都不使用引号

writet(csvfile, dialect=‘excel’, **fmtparams)

  • 返回DictWriter的实例
  • 主要方法有writerow、writerows

writerow(iterable)

import csv
from pathlib import Path

p = Path('c:/tmp/mycsv/test.csv')
with open(str(p)) as f:
	reader = csv.reaber(f)
	print(next(reader))
	print(next(reader))
	for line in reader:
		print(line)

rows = [
	[4,'tom',22,'tom'],
	(5,'jerry',24,'jerry'),
	(6,'justin',22,'just\t"in'),
	"abcdefgh",
	((1,),(2,))
]
row = rows[0]

with open(str(p), 'a') as f:
	writer = csv.writer(f)
	writer.writerow(row)
	writer.writerows(rows)
123456789101112131415161718192021222324
  • 说明row行,需要一个可迭代就可以,可迭代的每一个元素,将作为csv行的每一个元素
  • windows下回再每行末尾多出一个/r,结果办法 open(‘test.csv’, ‘w’, newline=’’)

ini文件处理

  • 作为配置文件,ini文件的格式很流畅
    在这里插入图片描述
  • 中括号里面的部分称为section,译作节、区、段
  • 每一个section内,都是key=value形成的键值对,key称为option选项
  • 注意这里的DEFAULT是缺省section的名字,必须大写

configparser

  • configparser模块的ConfigParser类就是用来操作
  • 可以将section当做key,section存储着键值对组成的字典,可以把ini配置文件当做一个嵌套的字典。默认使用的是有序字典

read(filenames, encoding=None)

  • 读取ini文件,可以是单个文件,也可以是文件列表。可以指定文件编码

sections()

  • 返回section列表。缺省section不包括在内

add_section(section_name)

  • 增加一个section

has_section(section, option)

  • 判断seciton是否存在

options(section)

  • 返回section的所有option,会追加缺省section的option

has_option(section, option)

  • 判断section是否存在这个option

get(section, option, *, raw=False, vars=None[, fallback])

  • 从指定的段的选项上取值,如果找到返回,如果没有找到就去找DEFAULT段有没有

getint(section, option, *, raw=False, vars=None[, fallback])

getfloat(section, option, *, raw=False,vars=None[, fallback])

getboolean(section, option, *, raw=False,vars=None[, fallback])

上面3个方法和get一样,返回指定类型数据

items(raw=False, vars=None)

items(section, raw=Fale, vars=None)

  • 没有section,则返回所有section名字及其对象;如果指定section,则返回这个指定的seciton的键值对组成而元组

set(section, option, value)

  • section存在的情况下,写入option=value,要求option、value必须是字符串

remove_section(section)

  • 移除seciton及其所有option

remove_option(section, option)

  • 移除section下的option

write(fileobject, space_aroud_delimiters=True)

  • 将当前config的所有内容写入fileobject中,一般open函数使用w模式
from configparser import ConfigParser

filename = 'test.ini'
newfilename = 'mysql.ini'

cfg = ConfigParser()
read_ok = cfg.read(filename)
print(read_ok)
# print(cfg.sections())
print(cfg.has_section('client'))

for k,v in cfg.items(): # 未指定section
    print(k, type(k))
    print(v, type(v))
    print(cfg.items(k))
    print()

print('-'*30)

for k,v in cfg.items("mysqld"): # 指定section
    print(k, type(k))
    print(v, type(v))
    print('~~~~~~~')
print('-'*30)

# 取值
tmp = cfg.get('mysqld', 'port')
print(type(tmp), tmp)
tmp = cfg.get('mysqld', 'a')
print(type(tmp), tmp)
print(cfg.get('mysqld', 'hello', fallback='python')) # 缺省值

# 按照类型
tmp = cfg.getint('mysqld', 'port')
print(type(tmp), tmp)

if cfg.has_section('test'):
    cfg.remove_section('test')

cfg.add_section('test')
cfg.set('test', 'test1', '1')
cfg.set('test', 'test2', '2')

with open(newfilename, 'w') as f:
    cfg.write(f)

print(cfg.getint('test', 'test2'))

cfg.remove_option('test', 'test2')

# 字典操作更简单
cfg['test']['x'] = '1000' # key不存在
cfg['test2'] = {'test2':'1000'} # section不存在

print('x' in cfg['test'])
print('x' in cfg['test2'])

# 其他内容方式
print(cfg._dict) # 返回默认的字典类型,默认使用有序字典<<class 'collections.OrderedDict>>

for k,v in cfg._sections.items():
    print(k,v)
    
for k,v in cfg._sections['mysqld'].items():
    print(k,v)

# 修改后需再次写入
with open(newfilename, 'w') as f:
    cfg.write(f)

为什么要序列化

  • 内存中的字典、列表、集合以及各种对象,如何保存到一个文件中?
  • 如果是自己定义的类的实例,如何保存到一个文件中?
  • 如何从文件中读取数据,并让他们在内存中再次恢复成自己对应的类的实例?
  • 要设一套协议,就按照某种规则,把内存中数据保存到文件中。文件是一个字节序列,所以必须把数据转换成字节序列,输出到文件。这就是序列化。反之,把文件的字节序列恢复到内存并且还是原来的类型,就是反序列化

定义

serialization 序列化

  • 将内存中对象存储下来,把它变成一个个字节 ->二进制

deserialization 反序列化

  • 将文件的一个个字节恢复成内存中对象 -> 二进制
  • 序列化保存到文件就是持久化
  • 可以将数据序列化后持久化,或者网络传输;也可以将文件中或者网络接收到的字节序列反序列化
  • Python提供了pickle库

pickle库

  • Python中的序列化、反序列化模块
    在这里插入图片描述
import pickle

filename = 'o:/ser'

i = 99
c = 'c'
l = list('123')
d = {'a':1, 'b':'abc', 'c':[1,2,3]}

# 序列化
with open(filename, 'wb') as f:
	pickle.dump(i, f)
	pickle.dump(c, f)
	pickle.dump(l, f)
	pickle.dump(d, f)

# 反序列化
with open(filename, 'rb') as f:
	print(f.read(), f.seek(0))
	for i in range(4)
		x =pickle.load(f)
		print(x, type(x))
12345678910111213141516171819202122
  • 对象序列化
import pickle

class AAA:
	tttt = 'ABC'
	def show(self):
		print('abc')
	
a1 = AAA() # 创建AAA类的对象

# 序列化
ser = pickle.dumps(a1)
print('ser={}'.format(ser)) # AAA

# 反序列化
a2 = pickle.loads(ser)
print(a2.tttt)
a2.show()
1234567891011121314151617
  • 上面的例子中,故意使用了连续的AAA、ABC、abc等字符串,就是为了在二进制文件中能容易的发现它们
  • 上例中,其实就保存了一个类名,因为所有的其他东西都是类定义的东西,是不变的,所以只序列化一个AAA类名。反序列化的时候找到类就可以恢复一个对象
import pickle

# 对象序列化
class AAA:
	def__int__(self):
		self.aaaa = 'abc'

a1 = AAA() # 创建AAA类的对象

# 序列化
ser = pickle.dumps(a1)
print('ser={}.format(ser)) # AAA aaaa abc

# 反序列化
a2 = pickle.loads(ser)
print(a2, type(a2))
print(a2.aaaa)
print(id(a1), id(a2))
123456789101112131415161718
  • 可以看出这回除了必须保存的AAA,还序列化了aaaa和abc,因为这是每一个对象自己的属性,每一个对象不一样的,所以这些数据需要序列化

序列化、反序列化实验

  • 定义类AAA,并序列化到文件
import pickle

# 实验
# 对象序列化
class AAA:
	def __init__(self):
		self.aaaa = 'abc'

a1 = AAA() # 创建AAA类的对象

# 序列化
ser = pick.dumps(a1)
print('ser={}'.format(ser)) # AAA aaaa abc
print(len(ser))

filename = 'o:/ser'

with open(filename, 'wb') as f:
	pickle.dump(a1, f)
12345678910111213141516171819
  • 将产生的序列化文件ser发送到其他节点上
  • 增加一个x.py文件,内容如下。最后执行这个脚本 $ python x.py
import pickle

with open('ser', 'rb') as f:
		a = pickle.load(f) # 异常
1234
  • 会抛出异常**AttributeError: Can’t get attribute ‘AAA’ on <module ‘main’ from ‘t.py’>
  • 这个异常实际上是找不到类AAA的定义,增加类定义即可解决
  • 反序列化的时候找到AAA类的定义,才能成功。否则就会抛出异常
  • 可以这样理解:反序列化的时候,类是模子,二进制序列就是铁水
import pickle

class AAA:
	def show(self):
		print('xyz')
	
with open('ser', 'rb') as f:
	a = pickle.load(f)
	print(a)
	a.show()
	print(a.aaaa)
1234567891011
  • 这里定义了类AAA,并且上面的代码也能成功的执行
  • 注意:
    这里的AAA定义和原来完全不同了
    因此,序列化、反序列化必须保证使用同一套类的定义,否则会带来不可预料的结果

序列化应用

一般来说,本地序列化的情况,应用较少。大多数场景都应用在网络传输中。
将数据序列化后通过网络传输到远程节点,远程服务器上的服务将接受到的数据反序列化后,就可以使用了
但是需要注意,远程接受端,反序列化时必须有对应的数据类型,否则就会报错。尤其是自定义类,必须远程得有一致的定义
现在,大多数项目,都不是单机的,也不是单服务的,需要多个程序之间配合。需要通过网络将数据传送到其他节点上去,这就需要大量的序列化、反序列化过程
但是,问题是,Python程序之间还可以都用pickle解决序列化、反序列化,如果是跨平台、跨语言、跨协议。pickle就不太适合,需要公共的协议。例如XML、Json、Protocol Buffer等
不同的协议,效率不同,适用不同的场景,根据不同的情况分析选型

Json

Json(JavaScript Object Notation,JS 对象标记)是一种轻量级的数据交换格式。它基于 ECMAScript(w3c组织制定的JS规范)的一个子集,采用完全独立于变成语言的文本格式来存储和表示数据
Json官网:http://json.org/

Json是数据类型

  • 双引号引起来的字符串,数值,true和false,null,对象,数组。这些都是值
    在这里插入图片描述
  • 字符串:由双引号包围起来的任意字符的组合,可以有转义字符
  • 数值:有正负,有整数、浮点数
  • 对象:无序的键值对的集合
    格式:{key1:value1,…,keyn:valuen}
    key必须是一个字符串,需要双引号包围这个字符串
    value可以是任意合法的值

在这里插入图片描述

  • 数组:有序的值的集合
    格式:[val1,…,valn]
    在这里插入图片描述
  • 实例
{
	"person":[
		{
			"name": "tom",
			"age": 18
		},
		{
			"name": "jerry",
			"age": 16
		}
	],
	"total": 2
}		
12345678910111213

Json 模块

Python 与 Json

  • Python支持少量内建数据类型到Json类型的转换

在这里插入图片描述

常用方法

在这里插入图片描述

import json
d = {'name':'Tom', 'age':20, 'interest':('music', 'movie'), 'class':['python']}
j = json.dumps(d)
print(j, type(j)) # 注意引号、括号的变化,注意数据类型的变化

d1 = json.loads(j)
print(d1)
print(id(d), id(d1))
12345678

一般json编码的数据很少落地,数据都是通过网络传输。传输的时候,要考虑压缩它
本质上来说它就是个文本,就是个字符串
json很简单,几乎编程语言都支持json,所以应用范围十分广泛

MessagePack

官网:https://msgpack.org/
MessagePack是一个基于二进制高效的对象序列化类库,可用于跨语言通信
它可以像Json那样,在许多语言之间交换结构对象
但是它比Json更快速也更轻巧
支持Python、Ruby、Java、C/C++等众多语言。
兼容json和pickle
在这里插入图片描述

# Json:27 bytes
{"person":[{"name":"tom","age":18},{"name":"jerry",."age":16}],"total":2}

# MessagePack:18 bytes
# 82 a7 63 6f 6d 70 61 63 74 c3 a6 73 63 68 65 6d 61 00
12345

可以看出,大大的节约了空间

安装

$ pip install msgpack
1

常用方法

  • packb 序列化对象。提供了dumps来兼容pickle和json
  • unpackb 反序列化对象。提供了loads来兼容
  • pack 序列化对象保存到文件对象。提供了dump来兼容
  • unpack 反序列化对象保存到文件对象。提供了load来兼容
import pickle
import msgpack
import json

d = {'person': [{'name': 'tom', 'age': 18}, {'name': 'jerry', 'age': 16}], 'total':2}

# json
data = json.dumps(d)
print(type(data), len(data), data)
print(data.replace(' ', '')
print(len(data.replace(' ', ''))) # 72 bytes 注意这样替换的压缩是不对的

# pickle
data = pickle.dumps(d)
print(type(data), len(data), data) # 101 bytes

# msgpack
data = msgpack.dumps(d)
print(type(data), len(data), data) # 48 bytes

print('-' * 30)

u = msgpack.umpackb(data)
print(typr(u), u)
print(msgpack.loads(data))

u = msgpack.loads(data, encoding='utf-8')
print(type(u), u)
12345678910111213141516171819202122232425262728
  • MessagePack简单易用,高效压缩,支持语言丰富
  • 所以,用它序列化也是一种很好的选择。Python很多大名鼎鼎的库都是用了msgpack

概述

  • 正则表达式,Regular Expression,缩写为regex、regexp、RE等。
  • 正则表达式是文本处理极为重要的技术,用它可以对字符串按照某种规则进行检索、替换。
  • 1970年代,Unix之父Ken Thompson将正则表达式引入到Unix中文本编辑器ed和grep命令中,由此正则表达式普及开来。
  • 1980年后,perl语言对Henry Spencer编写的库,扩展了很多新的特性。1997年开始,Philip Hazel开发出了PCRE(Perl Compatible Regular Expressions),它被PHP和HTTPD等工具采用。
  • 正则表达式应用极其广泛,shell中处理文本的命令、各种高级编程语言都支持正则表达式。
  • 参考:https://www.w3cschool.cn/regex_rmjc/

分类

BRE

  • 基本正则表达式,grep、sed、vi等软件支持。vim有扩展

ERE

  • 扩展正则表达式,egrep(gerp -E)、sed -r等

PCRE

  • 几乎所有高级语言都是PCRE的方言或者变种,Python从1.6开始就使用SRE正则表达式引擎,可以认为是PCRE的子集,见模块re

基本语法

元字符 metacharacter

在这里插入图片描述

转义

  • 凡是在正则表达式中有特殊意义的符号,如果想使用它的本意,需使用\转义
  • 反斜杠自身,得使用\
  • \r,\n代表回车、换行

重复

在这里插入图片描述

练习

  • 1、匹配手机号码
    字符串为“手机号码13899879021”
  • 2、匹配中国坐骑
    字符串为“号码027-65666837、0543-5837291”
  • 代码实现
  • 1、\d{11}
  • 2、\d{3,5}-\d{7,8}

or匹配与分组

在这里插入图片描述

  • 注意:
    分组和捕获是同一个意思
    使用正则表达式,能用简单表达式,就不要复杂的表达式

零宽度断言

在这里插入图片描述

  • 注意:
    断言不占分组号。断言如同条件,只是要求匹配必须满足断言的条件

注释

在这里插入图片描述

贪婪与非贪婪

  • 默认是贪婪模式,也就是说尽量多匹配更长的字符串
  • 非贪婪很简单,在重复的符号后面加上一个?问号,就尽量的少匹配了
    在这里插入图片描述
    在这里插入图片描述

引擎选项

在这里插入图片描述

工作模式

单行模式

  • . 点号可以匹配所有字符,包括换行符
  • ^ 表示整个字符串的开头
  • $ 表示整个字符串的结尾

多行模式

  • . 点号可以匹配除了换行符之外的字符,多行不影响 . 点号
  • ^ 表示行首,$ 表示行尾,不过这里的行是每一行

默认模式

  • 可以看做待匹配的文本是一行,不能看做多行
  • . 点号不能匹配换行符
  • ^$ 表示行首和行尾,而行首行尾就是整个字符串的开头和结尾

总结

  • 简单讲,单行模式只影响 . 点号行为,多行模式重新定义行影响了 ^$
  • 字符串看不见的换行符,\r\n会影响e的 测 试 , e 的测试,ee 只能匹配e\n

举例

very very happy
harry key

  • 上面2行happy之后,有可能是\r\n结尾
  • y$ 单行匹配key的y,多行匹配happy和key的y(这里的匹配是全局匹配)
  • .$ 指的是此行的结尾,而默认模式和单行模式都是一行,指的是这个大字符串的最后一个字符,就是key的y

Python使用re模块提供了正则表达式处理的能力

常量

在这里插入图片描述

多重选项

  • 使用 | 位或 运算开启多重选项
  • 如果单独存在,设计为1-9 如果多个存在,设置为1,2,4,8,16…
impoet re

re.M | re.S
123

方法

编译

re.compile(pattern, flags=0)
1
  • 设定flags,编译模式,返回正则表达式对象regex
  • pattern就是正则表达式字符串,flags是选项,指代当前工作模式。正则表达式需要被编译,为了提高效率,这些编译后的结果被保存,下次使用同样的pattern的时候,就不需要再次编译
  • re的其他方法为了提高效率都调用了编译方法,就是为了提速
# 先编译在操作

import re

s = 'apple\nbig'
regex = re.compile('^a', re.M)
r = regex.match(s)
print(s)
12345678

单次匹配

match

re.match(pattern, string, flags=0)
regex.match(string[, pos[, endpos]])
12
  • match 匹配,只从开头匹配
  • regex对象match方法可以重设定开始位置和结束位置。返回match对象

search

re.search(pattern, string, flags=0)
regex.search(string[, pos[, endpos]])
12
  • 从头搜索直到第一个匹配
  • regex对象search方法可以重设定开始位置和结束位置,返回match对象

fullmatch

re.fullmatch(pattern, string, flags=0)
regex.fullmatch(string[, pos[, endpos]])
12
  • 全长完全匹配,整个字符串和正则表达式匹配

match举例

import re

s = """python\nhello\nwho"""
r = re.match('p', s)
print(type(r), r) # match 对象,出一个结果

# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(0, 1), match='p'>
12345678
# 设置模式

import re

s = """python\nhello\nwho"""
r = re.match('^h', s, re.M)
print(type(r), r) # match 对象,出一个结果

# 打印结果
 <class 'NoneType'> None
# None原因:虽然re.M为多行模式,但是match只从头开始找
1234567891011
# 先编译,在匹配,设置开始位置
import re

s = """python\nhello\nwho"""
regex = re.compile('t', re.M)
r = regex.match(s, 2) # 把索引2作为开始找
print(type(r), r)

#打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(2, 3), match='t'>
# 只有先编译,在match,才可以调整开始位置 
1234567891011
  • 总结:mauch不管单行多行,只从头或指定开始索引找

search举例

import re

s = """python\nhello\nwho"""
#regex = re.compile('h', re.M)
r = re.search('h',s) # 找到python的h就停止匹配了
print(type(r), r)

# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(3, 4), match='h'>
123456789
# 设置模式

import re

s = """python\nhello\nwho"""
#regex = re.compile('h', re.M)
r = re.search('e',s, re.M)
print(type(r), r)

# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(8, 9), match='e'>
1234567891011
# 先编译在匹配,设置开始位置

import re

s = """python\nhello\nwho"""
regex = re.compile('h', re.M) # 设置为多行模式
r = regex.search(s,4,9) # 从索引4开始,到索引8结束,[4,9)
print(type(r), r)

# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(7, 8), match='h'>
1234567891011
  • 总结: search不管是不是多行,找到就返回

fullmatch举例

import re

s = """python\nhello\nwho"""
regex = re.compile('.+', re.S) # .+  在单行模式下, . 点可匹配到换行符
r = regex.search(s)
print(type(r), r) # match='python\nhello\nwho'

# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(0, 16), match='python\nhello\nwho'>
123456789
import re

s = """python\nhello\nwho"""
regex = re.compile('\w+') # 先编译
r = regex.search(s, 1, 3) # 匹配 [1,3)
print(type(r), r) #  match =yt

# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(1, 3), match='yt'>
123456789
  • 总结:fullmatch不管单行多行模式情况下整个字符串(或指定区间)需与正则表达式匹配

全文搜索

findall

re.findall(pattern,string,flags=0)
regex.findall(string[, pos[, endpos]])
12
  • 对整个字符串,从左至右匹配,返回所有匹配项的列表,里面是str

finditer

re.finditer(pattern,string,flags=0)
regex.finditer(string[, pos[, endpos]])
12
  • 对整个字符串,从左至右匹配,返回所有匹配项,返回迭代器
  • 注意每次迭代返回的是match对象

findall举例

import re

s = """python\nhello\nwho"""
r= re.findall('h',s)
print(r)

# 打印结果
['h', 'h', 'h']
12345678
# 按区间匹配,需先编译

import re

s = """python\nhello\nwho"""
regex = re.compile('h')
r= regex.findall(s, 3,10) # 匹配 [3,10)
print(r)

# 打印结果
['h', 'h']
1234567891011

finditer举例

import re

s = """python\nhello\nwho"""
r= re.finditer('h',s)
print(r) # <callable_iterator object at 0x00000000021E09B0>
for i in r:
    print(type(i),i,s[i.start():i.end()]) # s[i.start():i.end()] 切片拿到match值
 
 # 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(3, 4), match='h'> h
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(7, 8), match='h'> h
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(14, 15), match='h'> h
123456789101112

匹配替换

sub

re.sub(pattern, replacement, string, count=0, flags=0)
regex.sub(replacement, string, count=0)
12
  • 使用pattern对字符串string进行匹配,对匹配项使用replacement替换,返回的是str
  • replacement可是是string、bytes、function

subn

re.subn(pattern, replacement, string, count=0, flags=0)
regex.subn(replacement, string, count=0)
12
  • 同sub返回一个元祖(new_string, number_of_subs_made)

sub举例

在这里插入图片描述

import re

s = """python\nhello\nwho"""
r= re.sub('h', 'ab', s)
print(type(r), r)

# 打印结果
<class 'str'> pytabon
abello
wabo
12345678910
# 指定替换次数
import re

s = """python\nhello\nwho"""
r= re.sub('h', 'ab', s, 1) # 替换1次
print(type(r), r)

# 打印结果 # 仅python被替换为pytabon
<class 'str'> pytabon
hello
who
1234567891011
# 先编译后替换,指定替换次数
import re

s = """python\nhello\nwho"""
regex = re.compile('h')
r= regex.sub('ab', s, 2) # 替换2次
print(type(r), r)

# 打印结果 # 仅python和hello被替换
<class 'str'> pytabon
abello
who
123456789101112
  • 引用分组,添加后缀或前缀
# 添加前缀
import re

s = """honey\nhello\nhi"""
r= re.sub('(h\w+)', r'python-----\1', s)
print(r)

# 打印结果
python-----honey
python-----hello
python-----hi

# 添加后缀
r= re.sub('(h\w+)', r'\1------python', s)
print(r)

# 打印结果
honey------python
hello------python
hi------python
1234567891011121314151617181920

sunb举例

import re

s = """honey\nhello\nhi"""
r= re.subn('h', 'p', s)
print(type(r), r)

# 打印结果
<class 'tuple'> ('poney\npello\npi', 3)

for i in r:
    print(i)
   
# 打印结果
poney
pello
pi
3
1234567891011121314151617

分割字符串

re.split(pattern, string, maxsplit=0, flags=0)
1
  • re.split 分割字符串
import re

s = """
os.path.abspath(path)
normpath(join(os.getcwd(), path))
"""
# 把每行单词提取出来
print(s.split())  # 做不到
# 打印结果
['os.path.abspath(path)', 'normpath(join(os.getcwd(),', 'path))']

print(re.split('[\.()\s,]+', s))
# 打印结果
['', 'os', 'path', 'abspath', 'path', 'normpath', 'join', 'os', 'getcwd', 'path', '']

123456789101112131415

分组

  • 使用小括号的pattern捕获的数据被放到了组group中
  • match、search函数可以返回match对象
  • findall返回字符穿列表;finditer返回一个个match对象

group()

  • 如果pattern中使用了分组,如果有匹配的结果,会在match对象中
  1. 使用group(N)方式返回对应分组,1到N是对应的分组,0返回整个匹配的字符串,N不写缺省为0
  2. 如果使用了命名分组,可以使用group(‘name’)的方式取分组
  3. 也可以使用groups()返回所有组
  4. 使用groupdict()返回所有命名的分组

分组

import re

s = '''bottle\nbag\nbig\napple'''

regex = re.compile('(b\w+)') # 先编译
result = regex.match(s) # 从头开始匹配一次
print(type(result)) # <class '_sre.SRE_Match'>
print(result.group()) # bottle
12345678

命名分组

  • 分组命名从1开始,0代表整个match对象
import re

s = '''bottle\nbag\nbig\napple'''

regex = re.compile('(b\w+)\n(?P<name2>b\w+)\n(?P<name3>b\w+)')
result = regex.match(s)

print(result) # type(result)返回的是 <class '_sre.SRE_Match'>
# 打印结果 <_sre.SRE_Match object; span=(0, 14), match='bottle\nbag\nbig'>

print(result.group(1),result.group(2),result.group(3))  # 通过分组索引取对应分组值
# 打印结果 bottle bag big

print(result.group('name2'),result.group('name3')) # 通过命名分组名称取对应分组值
# 打印结果 bag big

print(result.groupdict()) # 将命名分组组成kv对放入字典
# 打印结果 {'name2': 'bag', 'name3': 'big'}

print(result.group(0)) # 等效result.group() 
# 打印结果
bottle
bag
big
123456789101112131415161718192021222324
  • findall用法
import re

s = '''bottle\nbag\nbig\napple'''

regex = re.compile('(b\w+)\n(?P<name2>b\w+)\n(?P<name3>b\w+)')
result = regex.findall(s)
for x in result:
    print(type(x),x)
 
 # 打印结果
 <class 'tuple'> ('bottle', 'bag', 'big')
1234567891011
  • finditer用法
import re

s = '''bottle\nbag\nbig\napple'''

regex = re.compile('(?P<head>b\w+)')
result = regex.finditer(s)
for x in result:
    print(type(x), x, x.group(), x.group('head'))
 
# 打印结果
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(0, 6), match='bottle'> bottle bottle
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(7, 10), match='bag'> bag bag
<class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(11, 14), match='big'> big big

版权声明:本文为weixin_35688430原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。