IO 在计算机中指 Input/Output, 也就是输入和输出. 由于程序和运行时数据是在内存中驻留, 由 CPU 这个超快的计算核心来执行, 涉及到数据交换的地方, 通常是磁盘, 网络等, 就需要 IO 接口.
文件读写
上代码:
- try:
- // 读文件
- f = open('/path/to/file', 'r')
- print(f.read())
- // 写文件
- f = open('/Users/michael/test.txt', 'w')
- f.write('Hello, world!')
- finally:
- if f:
- // 关闭文件
- f.close()
等价于:
- // 读文件
- with open('/path/to/file', 'r') as f:
- print(f.read())
- // 写文件
- with open('/Users/michael/test.txt', 'w') as f:
- f.write('Hello, world!')
打开文件
打开文本文件
1, 读模式打开文件
要以读文件的模式打开一个文件对象, 使用 Python 内置的 open() 函数, 传入文件名和标示符:
>>> f = open('/Users/michael/test.txt', 'r')
标示符'r'表示读, 这样, 我们就成功地打开了一个文件.
2, 写模式打开文件
>>> f = open('/Users/michael/test.txt', 'w')
3, 带编码方式打开文件 (读或者写)
默认读写文件的编码方式是 UTF-8, 要读取非 UTF-8 编码的文本文件, 需要给 open() 函数传入 encoding 参数, 例如, 读取 GBK 编码的文件:
>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk')
打开二进制文件
要读取二进制文件, 比如图片, 视频等等, 用'rb'模式打开文件即可:
>>> f = open('/Users/michael/test.jpg', 'rb')
读文件
如果文件打开成功, 接下来, 调用 read() 方法可以一次读取文件的全部内容, Python 把内容读到内存, 用一个 str 对象表示:
>>> f.read()
调用 read() 会一次性读取文件的全部内容, 如果文件有 10G, 内存就爆了, 所以, 要保险起见, 可以反复调用 read(size) 方法, 每次最多读取 size 个字节的内容. 另外, 调用 readline() 可以每次读取一行内容, 调用 readlines() 一次读取所有内容并按行返回 list. 因此, 要根据需要决定怎么调用.
如果文件很小, read() 一次性读取最方便; 如果不能确定文件大小, 反复调用 read(size) 比较保险; 如果是配置文件, 调用 readlines() 最方便:
- for line in f.readlines():
- print(line.strip()) # 把末尾的'\n'删掉
- file-like Object
像 open() 函数返回的这种有个 read() 方法的对象, 在 Python 中统称为 file-like Object. 除了 file 外, 还可以是内存的字节流, 网络流, 自定义流等等. file-like Object 不要求从特定类继承, 只要写个 read() 方法就行.
StringIO 就是在内存中创建的 file-like Object, 常用作临时缓冲.
写文件
写文件和读文件是一样的, 唯一区别是调用 open() 函数时, 传入标识符'w'或者'wb'表示写文本文件或写二进制文件:
f.write('Hello, world!')
关闭文件
最后一步是调用 close() 方法关闭文件. 文件使用完毕后必须关闭, 因为文件对象会占用操作系统的资源, 并且操作系统同一时间能打开的文件数量也是有限的:
>>> f.close()
StringIO 和 BytesIO
很多时候, 数据读写不一定是文件, 也可以在内存中读写.
StringIO
StringIO 顾名思义就是在内存中读写 str.
要把 str 写入 StringIO, 我们需要先创建一个 StringIO, 然后, 像文件一样写入即可:
- >>> from io import StringIO
- >>> f = StringIO()
- >>> f.write('hello')
- 5
- >>> f.write(' ')
- 1
- >>> f.write('world!')
- 6
- >>> print(f.getvalue())
- hello world!
getvalue() 方法用于获得写入后的 str.
要读取 StringIO, 可以用一个 str 初始化 StringIO, 然后, 像读文件一样读取:
- >>> from io import StringIO
- >>> f = StringIO('Hello!\nHi!\nGoodbye!')
- >>> while True:
- ... s = f.readline()
- ... if s == '':
- ... break
- ... print(s.strip())
- ...
- Hello!
- Hi!
- Goodbye!
- BytesIO
StringIO 操作的只能是 str, 如果要操作二进制数据, 就需要使用 BytesIO.
BytesIO 实现了在内存中读写 bytes, 我们创建一个 BytesIO, 然后写入一些 bytes:
- >>> from io import BytesIO
- >>> f = BytesIO()
- >>> f.write('中文'.encode('utf-8'))
- 6
- >>> print(f.getvalue())
- b'\xe4\xb8\xad\xe6\x96\x87'
请注意, 写入的不是 str, 而是经过 UTF-8 编码的 bytes.
和 StringIO 类似, 可以用一个 bytes 初始化 BytesIO, 然后, 像读文件一样读取:
- >>> from io import BytesIO
- >>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
- >>> f.read()
- b'\xe4\xb8\xad\xe6\x96\x87'
操作文件和目录
如果我们要操作文件, 目录, 可以在命令行下面输入操作系统提供的各种命令来完成. 比如 dir,cp 等命令.
如果要在 Python 程序中执行这些目录和文件的操作怎么办? 其实操作系统提供的命令只是简单地调用了操作系统提供的接口函数, Python 内置的 os 模块也可以直接调用操作系统提供的接口函数.
操作文件和目录的函数一部分放在 os 模块中, 一部分放在 os.path 模块中.
看看如何利用 Python 的特性来过滤文件. 比如我们要列出当前目录下的所有目录, 只需要一行代码:
- >>> [x for x in os.listdir('.') if os.path.isdir(x)]
- ['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Applications', 'Desktop', ...]
要列出所有的. py 文件, 也只需一行代码:
- >>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
- ['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']
序列化
pickle
我们把变量从内存中变成可存储或传输的过程称之为序列化, 在 Python 中叫 pickling, 在其他语言中也被称之为 serialization,marshalling,flattening 等等, 都是一个意思.
序列化之后, 就可以把序列化后的内容写入磁盘, 或者通过网络传输到别的机器上.
反过来, 把变量内容从序列化的对象重新读到内存里称之为反序列化, 即 unpickling.
Python 提供了 pickle 模块来实现序列化. Pickle 的问题和所有其他编程语言特有的序列化问题一样, 就是它只能用于 Python, 并且可能不同版本的 Python 彼此都不兼容, 因此, 只能用 Pickle 保存那些不重要的数据, 不能成功地反序列化也没关系.
JSON
如果我们要在不同的编程语言之间传递对象, 就必须把对象序列化为标准格式, 比如 XML, 但更好的方法是序列化为 JSON, 因为 JSON 表示出来就是一个字符串, 可以被所有语言读取, 也可以方便地存储到磁盘或者通过网络传输. JSON 不仅是标准格式, 并且比 XML 更快, 而且可以直接在 web 页面中读取, 非常方便.
Python 内置的 json 模块提供了非常完善的 Python 对象到 JSON 格式的转换. 我们先看看如何把 Python 对象变成一个 JSON:
- >>> import json
- >>> d = dict(name='Bob', age=20, score=88)
- >>> json.dumps(d)
- '{"age": 20,"score": 88,"name":"Bob"}'
dumps() 方法返回一个 str, 内容就是标准的 JSON. 类似的, dump() 方法可以直接把 JSON 写入一个 file-like Object.
要把 JSON 反序列化为 Python 对象, 用 loads() 或者对应的 load() 方法, 前者把 JSON 的字符串反序列化, 后者从 file-like Object 中读取字符串并反序列化:
- >>> json_str = '{"age": 20,"score": 88,"name":"Bob"}'
- >>> json.loads(json_str)
- {'age': 20, 'score': 88, 'name': 'Bob'}
由于 JSON 标准规定 JSON 编码是 UTF-8, 所以我们总是能正确地在 Python 的 str 与 JSON 的字符串之间转换.
来源: https://juejin.im/post/5b2c414ce51d455cd054a4b7