当前位置: 首页 > news >正文

【Python Cookbook】文件与 IO(二)

文件与 IO(二)

  • 6.字符串的 I/O 操作
  • 7.读写压缩文件
  • 8.固定大小记录的文件迭代(⭐⭐)

6.字符串的 I/O 操作

你想使用操作类文件对象的程序来操作文本或二进制字符串。

使用 io.StringIO()io.BytesIO() 类来创建类文件对象操作字符串数据。比如:

>>> s = io.StringIO()
>>> s.write('Hello World\n')
12
>>> print('This is a test', file=s)
15
>>> # Get all of the data written so far
>>> s.getvalue()
'Hello World\nThis is a test\n'
>>>>>> # Wrap a file interface around an existing string
>>> s = io.StringIO('Hello\nWorld\n')
>>> s.read(4)
'Hell'
>>> s.read()
'o\nWorld\n'
>>>

io.StringIO 只能用于文本。如果你要操作二进制数据,要使用 io.BytesIO 类来代替。比如:

>>> s = io.BytesIO()
>>> s.write(b'binary data')
>>> s.getvalue()
b'binary data'
>>>

当你想模拟一个普通文件的时候,StringIOBytesIO 类是很有用的。比如,在单元测试中,你可以使用 StringIO 来创建一个包含测试数据的类文件对象,这个对象可以被传给某个参数为普通文件对象的函数。

需要注意的是, StringIOBytesIO 实例并没有正确的整数类型的文件描述符。因此,它们不能在那些需要使用真实的系统级文件,如文件,管道或者是套接字的程序中使用。

🚀 在博主的另一篇博客《解析 io.StringIO 与 io.BytesIO》中有更为详细的介绍。

7.读写压缩文件

你想读写一个 gzipbz2 格式的压缩文件。

gzipbz2 模块可以很容易的处理这些文件。 两个模块都为 open() 函数提供了另外的实现来解决这个问题。比如,为了以文本形式读取压缩文件,可以这样做:

# gzip compression
import gzip
with gzip.open('somefile.gz', 'rt') as f:text = f.read()# bz2 compression
import bz2
with bz2.open('somefile.bz2', 'rt') as f:text = f.read()

类似的,为了写入压缩数据,可以这样做:

# gzip compression
import gzip
with gzip.open('somefile.gz', 'wt') as f:f.write(text)# bz2 compression
import bz2
with bz2.open('somefile.bz2', 'wt') as f:f.write(text)

如上,所有的 I/O 操作都使用文本模式并执行 Unicode 的编码/解码。类似的,如果你想操作二进制数据,使用 rb 或者 wb 文件模式即可。

大部分情况下读写压缩数据都是很简单的。但是要注意的是选择一个正确的文件模式是非常重要的。如果你不指定模式,那么默认的就是二进制模式,如果这时候程序想要接受的是文本数据,那么就会出错。gzip.open()bz2.open() 接受跟内置的 open() 函数一样的参数,包括 encodingerrorsnewline 等等。

当写入压缩数据时,可以使用 compresslevel 这个可选的关键字参数来指定一个压缩级别。比如:

with gzip.open('somefile.gz', 'wt', compresslevel=5) as f:f.write(text)

默认的等级是 9 9 9,也是最高的压缩等级。等级越低性能越好,但是数据压缩程度也越低。

最后一点, gzip.open()bz2.open() 还有一个很少被知道的特性,它们可以作用在一个已存在并以二进制模式打开的文件上。比如,下面代码是可行的:

import gzip
f = open('somefile.gz', 'rb')
with gzip.open(f, 'rt') as g:text = g.read()

这样就允许 gzipbz2 模块可以工作在许多类文件对象上,比如套接字,管道和内存中文件等。

8.固定大小记录的文件迭代(⭐⭐)

你想在一个固定长度记录或者数据块的集合上迭代,而不是在一个文件中一行一行的迭代。

通过下面这个小技巧使用 iterfunctools.partial() 函数:

from functools import partialRECORD_SIZE = 32with open('somefile.data', 'rb') as f:records = iter(partial(f.read, RECORD_SIZE), b'')for r in records:...
  • partial(f.read, RECORD_SIZE):
    • functools.partial 创建一个新的函数,这个新函数会固定 f.read 的第一个参数为 RECORD_SIZE(即 32)。
    • 相当于每次调用 partial(f.read, RECORD_SIZE)() 都会执行 f.read(32),即从文件中读取 32 字节的数据。
  • iter(callable, sentinel):
    • iter 不仅可以用于可迭代对象,还可以接受一个可调用对象(callable)和一个哨兵值(sentinel)。
    • 它会重复调用 callable,直到返回 sentinel 为止,此时迭代停止。
    • 在这里,callablepartial(f.read, RECORD_SIZE)sentinelb''(空字节串)。
    • 因此,iter 会不断调用 f.read(32),直到返回空字节串(表示文件读取完毕),然后停止迭代。
  • for r in records::
    • records 是一个迭代器,每次迭代会返回一个最多 RECORD_SIZE 字节的记录(r)。
    • 当文件读取完毕时,f.read(32) 返回 b'',迭代终止。

这个例子中的 records 对象是一个可迭代对象,它会不断的产生固定大小的数据块,直到文件末尾。要注意的是如果总记录大小不是块大小的整数倍的话,最后一个返回元素的字节数会比期望值少。

iter() 函数有一个鲜为人知的特性就是,如果你给它传递一个可调用对象和一个标记值,它会创建一个迭代器。这个迭代器会一直调用传入的可调用对象直到它返回标记值为止,这时候迭代终止。

在例子中, functools.partial 用来创建一个每次被调用时从文件中读取固定数目字节的可调用对象。标记值 b'' 就是当到达文件结尾时的返回值。

最后再提一点,上面的例子中的文件是以二进制模式打开的。如果是读取固定大小的记录,这通常是最普遍的情况。而对于文本文件,一行一行的读取(默认的迭代行为)更普遍点。

🚀 假设 somefile.data 的内容是 b'HelloWorld' * 10(即重复 10 次的 b'HelloWorld',共 100 字节),并且 RECORD_SIZE = 32

文件内容

b'HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorld'

运行代码

from functools import partialRECORD_SIZE = 32with open('somefile.data', 'rb') as f:records = iter(partial(f.read, RECORD_SIZE), b'')for i, r in enumerate(records, 1):print(f"Record {i}: {r}")

输出

Record 1: b'HelloWorldHelloWorldHelloWorldHello'
Record 2: b'WorldHelloWorldHelloWorldHelloWorld'
Record 3: b'HelloWorldHelloWorldHelloWorldHello'
Record 4: b'World'

说明

  • 文件共100字节,每次读取32字节:
    • 第1次读取:32字节(b'HelloWorldHelloWorldHelloWorldHello'
    • 第2次读取:32字节(b'WorldHelloWorldHelloWorldHelloWorld'
    • 第3次读取:32字节(b'HelloWorldHelloWorldHelloWorldHello'
    • 第4次读取:剩余4字节(b'World'
    • 第5次读取:返回 b'',迭代终止。

适用场景

这种方法非常适合处理固定大小的记录文件,例如:

  • 二进制文件格式(如数据库文件、图像文件等)。
  • 网络协议数据包(固定大小的数据块)。
  • 任何需要分块处理的流式数据。

相关文章:

  • Langchaine4j 流式输出 (6)
  • Vim 支持多种编程语言编辑器
  • NLP学习路线图(十五):TF-IDF(词频-逆文档频率)
  • Python Django完整教程与代码示例
  • 计算机网络(5)——数据链路层
  • 超标量处理器设计6-指令解码
  • Chorme如何对于youtube视频进行画中画背景播放?
  • wordpress免费主题网站
  • 利用 Python 爬虫获取淘宝商品详情
  • Java数据结构——八大排序
  • 04.MySQL数据类型详解
  • 网络攻防技术一:绪论
  • 深入解析Vue.js:构建现代Web应用的高效之道
  • 机电的焊接技术
  • MQTT入门实战宝典:从零起步掌握物联网核心通信协议
  • 如何学习开关电源?从“大”到“小”学习开关电源...
  • 第6节 Node.js 回调函数
  • SSL/TLS 协议详解:安全通信的基石
  • VSCODE的终端无法执行npm命令
  • opencv使用经典bug
  • 有公网ip 如何做一网站/网站关键词优化排名软件
  • 趴比库的网站是谁建设的/关键词抓取工具都有哪些
  • wordpress 国家列表/优化设计三年级下册数学答案
  • 魏公村网站建设/交换链接适合哪些网站
  • 金色世纪做网站的是哪个岗位/seo网站推广推荐
  • 怎么做会员自动售卡网站/百度网络优化推广公司