python-文件处理、路径处理、序列化和反序列化
文件IO常用操作
一般说IO操作,指的是文件IO。
把文件存储到磁盘上的这个过程,叫做落地。
column | column |
---|---|
open | 打开 |
read | 读取 |
write | 写入 |
close | 关闭 |
readline | 行读取 |
readlines | 多行读取 |
seek | 文件指针操作 |
tell | 指针位置 |
open打开操作
1 | open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True,opener=None) |
打开一个文件,返回一个文件对象(流对象)和文件描述符。打开文件失败,则返回异常。
基本使用: 创建一个文件test,然后打开它,用完关闭。
文件操作中最常用的就是读和写。文件访问的模式有两种:文本模式和二进制模式。
注:windows中使用codepage代码页。可以认为每一个代码页就是一张编码表 cp936和gbk等价。
mode模式
描述字符 | |
---|---|
r | 缺省的,表示只读打开 |
w | 只写打开,有的话就清除重新写 |
x | 创建并写入一个新文件 |
a | 写入打开,如果文件存在,则追加 |
b | 二进制模式 |
t | 缺省的,文本模式 |
+ | 读写打开一个文件,给原来只读、只写的增加缺失的功能 |
open默认是只读模式r打开已经存在的文件。
r 只读打开文件,如果使用write方法,会抛异常。 如果文件不存在,抛出FileNotFoundError异常。
w 表示只写方式打开,如果读取则抛出异常 如果文件不存在,则直接创建文件 如果文件存在,则清空文件内容。
x 文件不存在,创建文件,并只写方式打开,文件存在,抛出FileExistsError异常。
a 文件存在,只写打开,追加内容 文件不存在,则创建后,只写打开,追加内容
r是只读,wxa都是只写。 wxa都可以产生新文件,w不管文件存在与否,都会生成全新内容的文件;a不管文件是否存在,都能在打开的文件尾部追加;x必须要求文件事先不存在,自己造一个新文件。
+为r、w、a、x提供缺失的读或写功能,但是,获取文件对象依旧按照r、w、a、x自己的特征。 +不能单独使用,可以认为它是为前面的模式字符做增强功能的。
t和b:
文本模式t 字符流,将文件的字节按照某种字符编码理解,按照字符操作。open的默认mode就是rt。
二进制模式b 字节流,将文件就按照字节理解,与字符编码无关。二进制模式操作时,字节操作使用bytes类型。
t/b不能单独存在,要和a/w/x/r配合使用。
seek文件指针
文件指针,指向当前字节位置。
mode = r,指针起始在0 ,mode = a 指针起始在EOF。
tell():显示指针当前位置。
seek(offset[, whence]) 移动文件指针位置。offest偏移多少字节,whence从哪里开始。
文本模式下 whence 0 缺省值,表示从头开始,offset只能正整数 whence 1 表示从当前位置,offset只接受0,whence 2 表示从EOF开始,offest只接受0
1
2
3
4
5
6
7
8
9
10
11
12
13# 文本模式
f = open('test4','r+')
f.tell() # 起始
f.read()
f.tell() # EOF
f.seek(0) # 起始
f.read()
f.seek(2,0)
f.read()
f.seek(2,0)
f.seek(2,1) # offset必须为0
f.seek(2,2) # offset必须为0
f.close()二进制模式下 whence 0 缺省值,表示从头开始,offest只能正整数 whence 1表示从当前位置,offest可正可负,whence 2 表示从EOF开始,offest可正可负。
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 二进制模式
f = open('test4','rb+')
f.tell() # 起始
f.read()
f.tell() # EOF
f.write(b'abc')
f.seek(0) # 起始
f.seek(2,1) # 从当前指针开始,向后2
f.read()
f.seek(-2,2) # 从EOF开始,向前2
f.read()
f.seek(-20,2) # OSError
f.close()二进制模式支持任意起点的偏移,从头、从尾、从中间位置开始。 向后seek可以超界,但是向前seek的时候,不能超界,否则抛异常。
buffering缓冲区
-1 表示使用缺省大小的buffer。如果是二进制模式,使io.DEFAULT_BUFFER_SIZE值,默认是4096或者8192。如果是文本模式,如果是终端设备,是行缓存方式,如果不是,则使用二进制模式的策略。
- 0 只在二进制模式使用,表示关buffer
- 1 只在文本模式使用,表示使用行缓冲。意思就是见到换行符就flush
- 大于1 用于指定buffer的大小
buffer 缓冲区
缓冲区一个内存空间,一般来说是一个FIFO队列,到缓冲区满了或者达到阈值,数据才会flush到磁盘。
flush() 将缓冲区数据写入磁盘 close() 关闭前会调用flush()。
io.DEFAULT_BUFFER_SIZE 缺省缓冲区大小,字节。
buffering | 说明 |
---|---|
buffering = -1 | t和b,都是io.DEFAULT_BUFFER_SIZE |
buffering = 0 | b 关闭缓冲区 t 不支持 |
buffering = 1 | b 就一个字节 t 行缓冲,遇到换行符才flush |
buffering > 1 | b模式表示行缓冲大小。缓冲区的值可以超过io.DEFAULT_BUFFER_SIZE,直到设定的值超出后才把缓冲区flush 。 |
t模式,是io.DEFAULT_BUFFER_SIZE字节,flush完后把当前字符串也写入磁盘 |
一般来说:
- 文本模式,一般都用默认缓冲区大小
- 二进制模式,是一个个字节的操作,可以指定buffer的大小
- 一般来说,默认缓冲区大小是个比较好的选择,除非明确知道,否则不调整它
- 一般编程中,明确知道需要写磁盘了,都会手动调用一次flush,而不是等到自动flush或者close的时候
其他参数
编码: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’;
- ‘’ 表示不会自动转换通用换行符;其它合法字符表示换行符就是指定字符,就会按照指定字符分行写。
- ‘\n’或’’表示’\n’不替换;其它合法字符表示’\n’会被替换为指定的字符
closefd:关闭文件描述符,True表示关闭它。False会在文件关闭后保持这个描述符。fileobj.fileno()查看。
文件描述符:Linux一切皆文件,文件打开后都会有一个位于的文件描述符,在计算机系统中是一个有限的资源。0,1,2,标准输入,标准输出,标准错误输出。
对于类似于文件对象的IO对象,一般来说都需要在不使用的时候关闭、注销,以释放资源。
IO被打开的时候,会获得一个文件描述符。计算机资源是有限的,所以操作系统都会做限制。就是为了保护计算机的资源不要被完全耗尽,计算资源是共享的,不是独占的。
一般情况下,除非特别明确的知道资源情况,否则不要提高资源的限制值来解决问题。
read()
read(size=-1)
size表示读取的多少个字符或字节;负数或者None表示读取到EOF
readline(size=-1)
一行行读取文件内容。size设置一次能读取行内几个字符或字节。
readlines(hint=-1)
读取所有行的列表。指定hint则返回指定的行数。
write()
write(s),把字符串s写入到文件中并返回字符的个数 writelines(lines),将字符串列表写入文件。
close()
flush并关闭文件对象。
文件已经关闭,再次关闭没有任何效果。
其他
名称 | 说明 |
---|---|
seekable() | 是否可seek |
readable() | 是否可读 |
writeable() | 是否可写 |
closed() | 是否已经关闭 |
上下文管理
1、异常处理
当出现异常的时候,拦截异常。但是,因为很多代码都可能出现OSError异常,还不好判断异常就是应为资源限制产生的。
1 | f = open('test') |
使用finally可以保证打开的文件可以被关闭。
上下文管理
使用with … as 关键字
上下文管理的语句块并不会开启新的作用域
with语句块执行完的时候,会自动关闭文件对象
StringIO操作
io模块中的类
from io import StringIO
内存中,开辟的一个文本模式的buffer,可以像文件对象一样操作它
当close方法被调用的时候,这个buffer会被释放
1 | from io import StringIO |
好处
一般来说,磁盘的操作比内存的操作要慢得多,内存足够的情况下,一般的思路是少落地,减少磁盘IO的过程,可以大大的提高程序的运行效率。
BytesIO操作
io模块中的类
from io import BytesIO
内存中,开辟的一个二进制模式的buffer,可以像文件对象一样操作它
当close方法被调用的时候,这个buffer会被释放
1 | from io import BytesIO # 内存中构建 |
file-like对象
类文件对象,可以像文件对象一样操作。
socket对象,输入输出对象(stdin、stdout)都是类文件对象
1 | from sys import stdout, stderr |
路径操作
os.path模块
3.4版本之前
1 | from os import path |
1 | p1 = path.abspath(".") #“文件路径” |
pathlib模块
提供Path对象来操作。包括目录和文件。
导入模块:from pathlib import Path
目录操作初始化:
1 | p = Path() # 当前目录 WindowsPath('.') |
路径拼接和分解
操作符/
Path对象 / Path对象
Path对象 / 字符串 或者 字符串 / Path对象
分解
parts属性,可以返回路径中的每一个部分
1 | p3.absolute() #WindowsPath('C:/Users/vampire/python/c/a') |
joinpath
joinpath(*other) 连接多个字符串到Path对象中
1 | p = Path() # WindowsPath('.') |
获取路径
str 获取路径字符串
bytes 获取路径字符串的bytes
1 | p = Path('/etc') |
父目录
parent 目录的逻辑父目录
parents 父目录序列,索引0是直接的父
1 | p = Path('/a/b/c/d') |
目录的组合部分
name、stem、suffix、suffixes、with_suffix(suffix)、with_name(name)
name 目录的最后一个部分
suffix 目录中最后一个部分的扩展名
stem 目录最后一个部分,没有后缀
suffixes 返回多个扩展名列表
with_suffix(suffix) 有扩展名则替换,无则补充扩展名
with_name(name) 替换目录最后一个部分并返回一个新的路径
1 | p = Path('mysqlinstall/mysql.tar.gz') |
判断方法
is_dir()
是否是目录,目录存在返回True
is_file()
是否是普通文件,文件存在返回True
is_symlink()
是否是软链接
is_socket()
是否是socket文件
is_block_device()
是否是块设备
is_char_device()
是否是字符设备
is_absolute()
是否是绝对路径
resolve()
返回一个新的路径,这个新路径就是当前Path对象的绝对路径,如果是软链接则直接被解析
absolute()
获取绝对路径
exists()
目录或文件是否存在
rmdir()
删除空目录。没有提供判断目录为空的方法
touch(mode=0o666, exist_ok=True)
创建一个文件
as_uri()
将路径返回成URI,例如’file:///etc/passwd’
mkdir(mode=0o777, parents=False, exist_ok=False)
parents,是否创建父目录,True等同于mkdir -p;False时,父目录不存在,则抛出FileNotFoundError
exist_ok参数,在3.5版本加入。False时,路径存在,抛出FileExistsError;True时,FileExistsError被忽略
iterdir()
迭代当前目录
匹配
match(pattern)
模式匹配,成功返回True。
1 | Path('a/b.py').match('*.py') # True |
stat() 相当于stat命令 ,lstat() 同stat(),但如果是符号链接,则显示符号链接本身的文件信息
pathlib模块下的文件操作
Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
使用的方法类似内建函数open,返回一个文件对象。
3.5增加的新函数
Path.read_bytes()
以’rb’读取路径对应文件,并返回二进制流。看源码
Path.read_text(encoding=None, errors=None)
以’rt’方式读取路径对应文件,返回文本。
Path.write_bytes(data)
以’wb’方式写入数据到路径对应文件。
Path.write_text(data, encoding=None, errors=None)
以’wt’方式写入字符串到路径对应文件。
1 | p = Path('my_binary_file') |
csv文件
逗号分隔值Comma-Separated Values。
CSV 是一个被行分隔符、列分隔符划分成行和列的文本文件。
CSV 不指定字符编码。
行分隔符为\r\n,最后一行可以没有换行符
列分隔符常为逗号或者制表符。
每一行称为一条记录record
字段可以使用双引号括起来,也可以不使用。如果字段中出现了双引号、逗号、换行符必须使用双引号括起来。如果字段的值是双引号,使用两个双引号表示一个转义。
表头可选,和字段列对齐就行了。
手动生成csv文件
1 | from pathlib import Path |
csv模块
1 | def reader(iterable, dialect='excel', *args, **kwargs) |
返回一个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都不使用引号。
1 | def writer(fileobj, dialect='excel', *args, **kwargs) |
返回DictWriter实例,主要的方法有writerow,writerows。
1 | import csv |
ini文件
一般作为配置文件。
ini文件:
1 | [DEFAULT] |
中括号里面的部分称为section,译作节、区、段。
每一个section内,都是key=value形成的键值对,key称为option选项。
这里的DEFAULT是缺省section的名字,必须大写。
configparser模块
configparser模块的ConfigParser类就是用来操作。
可以将section当做key,section存储着键值对组成的字典,可以把ini配置文件当做一个嵌套的字典。默认使用的是有序字典。
1 | read(filenames, encoding=None) |
代码示例:
1 | from configparser import ConfigParser |
序列化和反序列化
要设计一套协议,按照某种规则,把内存中数据保存到文件中,文件是一个字节序列,所以必须把数据转换成<font color=red >字节</font>序列,输出到文件。这就是序列化。反之,从文件的字节序列恢复到内存。就是反序列化。
serialization:序列化
将内存中对象存储下来,变成一个个字节 –> 二进制deseiralization:反序列化
将文件中的一个个字节恢复成内存中对象 <–二进制
序列化保存到文件就是持久化,可以将数据序列化后持久化,或者网络传输;也可以将文件中或者网络中接收到的字节序列反序列化。
pickle库
python中的序列化,反序列化模块。
dumps 对象序列化为bytes对象 dump 对象序列化到文件对象,就是存入文件
loads 从bytes对象反序列化 load 对象反序列化,从文件读取数据
序列化的应用
一般来说,本地序列化的情况,应用较少。大多数场景都应用在网络传输中。
将数据序列化后通过网络传输到远程节点,远程服务器上的服务将接收到的数据反序列化后,就可以使用了。
但是,要注意一点,远程接收端,反序列化时必须有对应的数据类型,否则就会报错。尤其是自定义类,必须远程的有一致的定义。
现在,大多数项目,都不是单机的,也不是单服务的。需要通过网络将数据传送到其他节点上去,这就需要大量的序列化、反序列化过程。
但是,问题是,Python程序之间还可以都是用pickle解决序列化、反序列化,如果是跨平台、跨语言、跨协议pickle就不太适合了,就需要公共的协议。例如XML、Json、Protocol Buffer等。
不同的协议,效率不同、学习曲线不同,适用不同场景,要根据不同的情况分析选型。
Json
Json(JavaScript Object Notation,JS对象标记)是一种轻量级的数据交换格式。它基于 ECMAScript (w3c组织制定的JS规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。网址: http://json.org/
Json的数据类型
值:双引号引起来的字符串,数值,true和false,null,对象,数组,这些都是值
字符串:有正负,有整数,浮点数。
对象:无序的键值对的集合,格式{key:value…},key必须是一个字符串,需要双引号包围这个字符串,value可以是任意合法的值。
数组:有序的值的集合 格式[val1,,,,valn]
1 | { |
Json模块
Python支持少量内建数据类型到Json类型的转换
Python类型 | Json类型 |
---|---|
True | true |
False | false |
None | null |
str | string |
int | integer |
float | float |
list | array |
dict | object |
常用方法
Python类型 | Json类型 |
---|---|
dumps | Json编码 |
dump | Json编码并存入文件 |
loads | Json解码 |
load | Json解码,从文件读取数据 |
一般Json编码的数据很少落地,数据都是通过网络传输,传输的时候,要考虑压缩它,节省流量。本质来说它就是个文本,就是个字符串。
MessagePack
MessagePack是一个基于二进制高效的对象序列化类库,可用于跨语言通信。
它可以像JSON那样,在许多种语言之间交换结构对象。
兼容 json和pickle。
MessagePack简单易用,高效压缩,支持语言丰富。
所以,用它序列化也是一种很好的选择。
安装:$pip install msgpack-python
常用方法:
packb 序列化对象。提供了dumps来兼容pickle和json。
unpackb 反序列化对象。提供了loads来兼容。
pack 序列化对象保存到文件对象。提供了dump来兼容。
unpack 反序列化对象保存到文件对象。提供了load来兼容。
1 | import pickle |
1 | {"person": [{"name": "tom", "age": 18}, {"name": "jerry", "age": 16}], "total": 2} <class 'str'> 82 |