Python文件处理
纸上得来终觉浅,绝知此事要躬行。
1. 文件打开
日常我们使用中,涉及最多就应该是对文件的处理了,其中文件的读取模式主要涉及到open函数,而该函数的参数又比较多,所以理解该函数才是文件处理的核心要点。
- open:核心函数
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
# 使用open函数打开文件 try: f = open('note.txt', 'r') print(f.read()) finally: f.close() # 使用with文件上下文管理器 with open('note.txt', 'r') as f: print(f.read())
- (1)mode:指定文件的读取模式
Character | Meaning | Description |
---|---|---|
r | open for reading (default) | 只读模式(默认) |
w | open for writing, truncating the file first | 只写模式 |
x | create a new file and open it for writing | 创建新文件并打开写模式 |
a | open for writing, appending to the end of the file if it exists | 追加模式 |
b | binary mode | 二进制模式 |
t | text mode (default) | 文本模式(默认) |
+ | open a disk file for updating (reading and writing) | 读写模式 |
U | universal newline mode (deprecated) | 已经弃用了 |
| r r+ w w+ a a+ x x+ ---------------------|---------------------------------- allow read | ✓ ✓ ✓ ✓ ✓ allow write | ✓ ✓ ✓ ✓ ✓ ✓ ✓ create new file | ✓ ✓ ✓ ✓ ✓ ✓ open existing file | ✓ ✓ ✓ ✓ ✓ ✓ erase file contents | ✓ ✓ allow seek | ✓ ✓ ✓ ✓ ✓ position at start | ✓ ✓ ✓ ✓ ✓ ✓ position at end | ✓ ✓
In [1]: f = open('update_chis.sh', 'r') In [2]: f Out[2]: <_io.TextIOWrapper name='update_chis.sh' mode='r' encoding='UTF-8'>
- (2)buffering:设置缓存大小
- 文本模式下,不设置此参数,遇到换行刷新buffer
- 二进制模式下,不设置此参数,根据操作系统自动判断buffer大小
- 二进制模式下,设置此参数为0,关闭buffer
In [4]: f = open('update_chis.sh', buffering=1024)
- (3)encoding:指定编码格式
- encoding参数只在文本模式下生效
In [7]: f = open('update_chis.sh', encoding='utf-8')
- (4)errors:指定无法解码时的处理模式
- errors只在文本模式下生效
- 参数strict表示严格模式,无法解码抛出异常
- 参数ignore表示忽略模式,无法解码直接pass
In [20]: with open('xxx.txt', errors='ignore') as f: ...: pass ...:
- (5)newline:指定换行符
- newline所指定换行符None、''、n、r、rn
In [21]: !echo "a.nb.nc." > note.txt In [23]: cat note.txt a. b. c. In [24]: f = open('note.txt', newline='n') In [25]: f.readlines() Out[25]: ['a.n', 'b.n', 'c.n'] In [26]: f.close()
2. 文件对象
我们这里介绍一下,常用的文件操作函数。
- close 函数
- 关闭文件对象,并清空缓冲区
- closed属性用来判断文件是否已经关闭
- 在编程中最好使用with open方法,会自动关闭文件
In [27]: f = open('note.txt', node='rt') In [28]: f.close()
- fileno 函数
- 返回一个文件描述符
- name属性用来获取文件名称
In [29]: f = open('note.txt', mode='rt') In [30]: f.fileno Out[30]: <function TextIOWrapper.fileno> In [31]: f.fileno() Out[31]: 11 In [33]: f.name Out[33]: 'note.txt'
- flush 函数
- 强制刷新缓冲区
- 将写入在缓冲区的内容,直接写入文件
In [45]: f = open('note.txt', mode='w') In [47]: f.write('abc') Out[47]: 3 In [48]: !cat note.txt In [49]: f.flush() In [50]: !cat note.txt abc In [51]: f.close()
- read 函数
- 读取时包括换行符
- 读取文件,指定长度,表示可以指定读取字节(二进制)或者字符(文本模式)
- 读取文件,不指定长度,表示读取到文件末尾,参数-1同理
In [51]: !echo "a.nb.nc." > note.txt In [52]: f = open('note.txt', 'r+') In [53]: f.read(2) Out[53]: 'a.' In [54]: f.read(2) Out[54]: 'nb' In [55]: f.close()
- readline 函数
- readline函数经常和strip函数一起使用,用于除去行尾换行符
- readlines函数一次读入所有行,将结果保存到一个列表中,包含换行符
- 当读到文件末尾时,read和readline返回空string/空bytes,而readlines返回空的list列表
In [56]: f = open('note.txt', 'r+') In [57]: f.readline() Out[57]: 'a.n' In [58]: f.readline() Out[58]: 'b.n' In [59]: f.close()
In [60]: f = open('note.txt', 'r+') In [61]: f.readlines() Out[61]: ['a.n', 'b.n', 'c.n'] In [62]: f.close()
- seek 函数
- 移动文件指针
- seek函数中,参数whence表示从哪里开始移动
- 当文本模式打开时,whence只能是0
- 当二进制模式打开时,whence都可用
whence | Description |
---|---|
0 | 从起始位置开始移动,offset偏移量应为零或者整数 |
1 | 从当前位置开始移动,offset偏移量可能为负数 |
2 | 从末尾位置开始移动,offset偏移量通常为负数 |
In [63]: f = open('note.txt', 'r+') In [64]: f.readline() Out[64]: 'a.n' In [65]: f.seek(0) Out[65]: 0 In [66]: f.readline() Out[66]: 'a.n' In [67]: f.tell() Out[67]: 3 In [68]: f.seekable() Out[68]: True In [70]: f.closed Out[70]: False In [72]: f.close()
- write 函数
- 写操作时,换行符始终需要显示传入
- 每次以单行的方式写文件,多行的话需要添加换行符n
- writelines函数用于写入字符串,和write函数类似(字符串也是可迭代对象)
- writelines函数用于写入一个可迭代对象,循环遍历写入,不会在每个元素之后自动添加换行符,需要手动添加n符号
In [73]: f = open('note.txt', 'a+') In [74]: f.writable() Out[74]: True In [75]: f.write('dn') Out[75]: 2 In [76]: f.writelines(['e', 'f']) In [77]: f.flush() In [78]: !cat note.txt a. b. c. d ef In [79]: f.close()
3. 序列化和反序列化
在分布式系统中,很多数据都是需要传输的,所以就需要将数据转换成可传输的二进制流。传输到对应机器上之后,又需要把该二进制流转成对应数据,这就是序列化和反序列化。
pickle
- 适用范围
- 这是Python自带的序列化和反序列化工具
- 参数说明
- object: 要持久化保存的对象,即用于传输的数据。
- file: 一个拥有write()方法的对象,并且这个write()方法能接收一个字符串作为参数。这个对象可以是一个以写模式打开的文件对象或者一个StringIO对象,或者其他自定义的满足条件的对象。
- protoco: 这是一个可选的参数,默认为0。如果设置为1或True,则以高压缩的二进制格式保存持久化后的对象,否则以ASCII格式保存。
# 官方文档 Functions: dump(object, file[, protocol]) dumps(object[, protocol]) -> string load(file) -> object loads(string) -> object
- 示例说明
In [80]: import pickle In [81]: dct = { 'a': 1, 'b': 2, 'c':3 } In [82]: pickle_date = pickle.dumps(dct) In [83]: pickle_date Out[83]: b'x80x03}qx00(Xx01x00x00x00aqx01Kx01Xx01x00x00x00bqx02Kx02Xx01x00x00x00cqx03Kx03u.'
In [85]: with open('data.pickle', 'wb') as f: ...: f.write(pickle_date) ...: In [87]: with open('data.pickle', 'rb') as f: ...: print(pickle.loads(f.read())) ...: {'a': 1, 'b': 2, 'c': 3}
json
- 适用范围
- pickle和json的转换协议不同,所以看到的结果也不一样
- 通常会优先使用json格式,因为其跨平台的特性,用起来很方便
In [89]: import json In [90]: dct = { 'a': 1, 'b': 2, 'c':3 } In [91]: json_data = json.dumps(dct) In [92]: json_data Out[92]: '{"a": 1, "b": 2, "c": 3}'
In [93]: with open('json_date', 'w') as f: ...: f.write(json_data) ...: In [94]: with open('json_data') as f: ...: data = json.loads(f.read()) ...: print(data) ...:
- 示例说明
Encoding basic Python object hierarchies:: >>> import json >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}]) '["foo", {"bar": ["baz", null, 1.0, 2]}]' >>> print(json.dumps(""foobar")) ""foobar" >>> print(json.dumps('u1234')) "u1234" >>> print(json.dumps('\')) "\" >>> print(json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)) {"a": 0, "b": 0, "c": 0} >>> from io import StringIO >>> io = StringIO() >>> json.dump(['streaming API'], io) >>> io.getvalue() '["streaming API"]' Compact encoding:: >>> import json >>> from collections import OrderedDict >>> mydict = OrderedDict([('4', 5), ('6', 7)]) >>> json.dumps([1,2,3,mydict], separators=(',', ':')) '[1,2,3,{"4":5,"6":7}]' Pretty printing:: >>> import json >>> print(json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)) { "4": 5, "6": 7 } Decoding JSON:: >>> import json >>> obj = ['foo', {'bar': ['baz', None, 1.0, 2]}] >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj True >>> json.loads('"\"foo\bar"') == '"foox08ar' True >>> from io import StringIO >>> io = StringIO('["streaming API"]') >>> json.load(io)[0] == 'streaming API' True Specializing JSON object decoding:: >>> import json >>> def as_complex(dct): ... if '__complex__' in dct: ... return complex(dct['real'], dct['imag']) ... return dct ... >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}', ... object_hook=as_complex) (1+2j) >>> from decimal import Decimal >>> json.loads('1.1', parse_float=Decimal) == Decimal('1.1') True Specializing JSON object encoding:: >>> import json >>> def encode_complex(obj): ... if isinstance(obj, complex): ... return [obj.real, obj.imag] ... raise TypeError(repr(obj) + " is not JSON serializable") ... >>> json.dumps(2 + 1j, default=encode_complex) '[2.0, 1.0]' >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j) '[2.0, 1.0]' >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j)) '[2.0, 1.0]' Using json.tool from the shell to validate and pretty-print:: $ echo '{"json":"obj"}' | python -m json.tool { "json": "obj" } $ echo '{ 1.2:3.4}' | python -m json.tool Expecting property name enclosed in double quotes: line 1 column 3 (char 2)