本文转载改编自: https://docs.python.org/zh-cn/3/library/io.html
在原文中有重新删减、编号、添加示例。
关于 io 模块
io
模块提供了 Python 用于处理各种 I/O 类型的主要工具。
- io — Core tools for working with streams
https://docs.python.org/3/library/io.html - 中文版
https://docs.python.org/zh-cn/3/library/io.html - io 处理流的核心工具
http://study.yali.edu.cn/pythonhelp/library/io.html - 源代码: Lib/io.py
https://github.com/python/cpython/tree/3.10/Lib/io.py
三种主要的 I/O类型分别为:
-
文本 I/O
文本IO所产生的是str对象,如果后备存储本身使用的是字节组成,可以通过编码和解码数据来适应平台数据类型。 -
二进制 I/O
二进制IO也称为缓冲IO,需要类似字节的对象并生成bytes对象,不执行编码,解码或换行,这种类型的流可以用于非文本数据,并且还需要手动控制文本数据的处理。 -
原始 I/O
原始I,也称为无缓冲IO。通常用作二进制和文本流的低级构建块。(一般不推荐使用)
可从用户代码直接操作原始流,也可以通过在缓冲禁用的情况下 以二进制模式打开文件来创建原始流。
这些是泛型类型,有很多种后端存储可以用在他们上面。
一个隶属于任何这些类型的具体对象被称作 file object。
其他同类的术语还有 流
和 类文件对象
。
独立于其类别,每个具体流对象也将具有各种功能:它可以是只读,只写或读写。
它还可以允许任意随机访问(向前或向后寻找任何位置),或仅允许顺序访问(例如在套接字或管道的情况下)。
所有流对提供给它们的数据类型都很敏感。
例如将 str
对象给二进制流的 write()
方法会引发 TypeError
。
将 bytes
对象提供给文本流的 write()
方法也是如此。
在 3.3 版更改: 由于 IOError
现在是 OSError
的别名,因此用于引发 IOError
的操作现在会引发 OSError
。
1、文本 I/O
文本I/O预期并生成 str
对象。
这意味着,无论何时后台存储是由字节组成的(例如在文件的情况下),数据的编码和解码都是透明的,并且可以选择转换特定于平台的换行符。
创建文本流的最简单方法是使用 open()
,可以选择指定编码:
f = open("myfile.txt", "r", encoding="utf-8")
内存中文本流也可以作为 StringIO
对象使用:
f = io.StringIO("some initial text data")
下方 TextIOBase
的文档中详细描述了文本流的API
2、二进制(字节) I/O
二进制I/O(也称为缓冲I/O)预期 bytes-like objects
并生成 bytes
对象。
不执行编码、解码或换行转换。
这种类型的流可以用于所有类型的非文本数据
,并且还可以在需要手动控制文本数据的处理时使用。
创建二进制流的最简单方法是使用 open()
,并在模式字符串中指定 b
:
f = open("myfile.jpg", "rb")
内存中二进制流也可以作为 BytesIO
对象使用:
f = io.BytesIO(b"some initial binary data: \x00\x01")
下方 BufferedIOBase
的文档中详细描述了二进制流 API
其他库模块可以提供额外的方式来创建文本或二进制流。
参见 socket.socket.makefile()
的示例:http://study.yali.edu.cn/pythonhelp/library/socket.html#socket.socket.makefile
3、原始 I/O
原始 I/O(也称为 非缓冲 I/O
)通常用作二进制和文本流的低级构建块。
用户代码直接操作原始流的用法非常罕见。
不过,可以通过在禁用缓冲的情况下以二进制模式打开文件来创建原始流:
f = open("myfile.jpg", "rb", buffering=0)
RawIOBase
的文档中详细描述了原始流的API
http://study.yali.edu.cn/pythonhelp/library/io.html#io.RawIOBase
二、文本编码格式
TextIOWrapper
和 open()
的默认编码格式取决于语言区域设置 locale.getpreferredencoding(False)
: http://study.yali.edu.cn/pythonhelp/library/locale.html#locale.getpreferredencoding.
但是,很多开发者在打开以 UTF-8 编码的文本文件 (例如 JSON, TOML, Markdown 等等…) 时会忘记指定编码格式,因为大多数 Unix 平台默认使用 UTF-8 语言区域。
这会导致各种错误因为大多数 Windows 用户的语言区域编码格式并不是 UTF-8。 例如:
# May not work on Windows when non-ASCII characters in the file.
with open("README.md") as f:
long_description = f.read()
此外,虽然暂时还没有确定的计划,但 Python 可能会在未来将默认的文本文件编码格式改为 UTF-8。
为此,强烈建议你在打开文本文件时显式地指定编码格式。
如果你想要使用 UTF-8,请传入 encoding="utf-8"
。
要使用当前语言区域的编码格式,encoding="locale"
(已在 Python 3.10 中被支持)。
When you need to run existing code on Windows that attempts to open UTF-8 files using the default locale encoding, you can enable the UTF-8 mode. See UTF-8 mode on Windows:
http://study.yali.edu.cn/pythonhelp/using/windows.html#win-utf8-mode.
选择性的 EncodingWarning
3.10 新版功能: 请参阅 PEP 597
https://www.python.org/dev/peps/pep-0597 。
要找出哪里使用了默认语言区域的编码格式,你可以启用 -X warn_default_encoding
命令行选项或设置 PYTHONWARNDEFAULTENCODING
环境变量,这将在使用默认编码格式时发出 EncodingWarning
。
如果你提供了使用 open()
或 TextIOWrapper
的 API 并将 encoding=None
作为形参传入,你可以使用 text_encoding()
以便 API 的调用方在没有传入 encoding
的时候将发出 EncodingWarning
。
但是,对于新的 API 请考虑默认就使用 UTF-8 (即 encoding="utf-8"
)。
三、高阶模块接口
io.DEFAULT_BUFFER_SIZE
包含模块缓冲 I/O 类使用的默认缓冲区大小的 int。
在可能的情况下 open()
将使用文件的 blksize(由 os.stat()
获得)。
io.open
io.open(file, mode=‘r’, buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
这是内置的 open()
函数的别名。
open
附带参数 path
、mode
、flags
会引发 审计事件。
io.open_code
io.open_code(path)
以 rb
模式打开提供的文件。
如果目的是将文件内容做为可执行代码,则应使用此函数。
path
应当为 str
类型并且是一个绝对路径。
此函数的行为可以由对 PyFile_SetOpenCodeHook()
的先期调用所重载。但是,如果 path
为 str
类型并且是一个绝对路径,open_code(path)
的行为应当总是与 open(path, 'rb')
一致。
重载此行为的目的是为了给文件附加额外的验证或预处理。3.8 新版功能.
io.text_encoding(encoding, stacklevel=2)
这是一个针对使用 open()
或 TextIOWrapper
的可调用对象的辅助函数并且具有 encoding=None
形参。
此函数会返回 encoding
,如果它不为 None
的话,或是 "locale"
,如果 encoding
为 None
的话。
如果 sys.flags.warn_default_encoding
为真值并且 encoding
为 None 则此函数会发出 EncodingWarning
。
stacklevel 指定在哪里发出警告。 例如:
def read_text(path, encoding=None):
encoding = io.text_encoding(encoding) # stacklevel=2
with open(path, encoding) as f:
return f.read()
在这个例子中,将为 read_text()
的调用方发出 EncodingWarning
。
- exception
io.BlockingIOError
这是内置的 BlockingIOError
异常的兼容性别名。
- exception
io.UnsupportedOperation
在流上调用不支持的操作时引发的继承 OSError
和 ValueError
的异常。
参见: sys
: http://study.yali.edu.cn/pythonhelp/library/sys.html#module-sys
包含标准IO流:
sys.stdin
: http://study.yali.edu.cn/pythonhelp/library/sys.html#sys.stdinsys.stdout
: http://study.yali.edu.cn/pythonhelp/library/sys.html#sys.stdoutsys.stderr
: http://study.yali.edu.cn/pythonhelp/library/sys.html#sys.stderr
四、类的层次结构
I/O 流被安排为按类的层次结构实现。
首先是 抽象基类 (ABC),用于指定流的各种类别,然后是提供标准流实现的具体类。
IOBase
- RawIOBase
- FileIO
- BufferedIOBase
- BufferedWriter
- BufferedReader
- BufferedRWPair
- BytesIO
- TextIOBase
- TextIOWrapper
- StringIO
注解
-
抽象基类还提供某些方法的默认实现,以帮助实现具体的流类。
例如BufferedIOBase
提供了readinto()
和readline()
的未优化实现。 -
I/O层次结构的顶部是抽象基类
IOBase
。它定义了流的基本接口。
但是请注意,对流的读取和写入之间没有分离。如果实现不支持指定的操作,则会引发UnsupportedOperation
) 。 -
抽象基类
RawIOBase
是IOBase
的子类。它负责将字节读取和写入流中。
RawIOBase
的子类FileIO
提供计算机文件系统中文件的接口。 -
抽象基类
BufferedIOBase
继承了IOBase
,处理原始二进制流(RawIOBase
)上的缓冲。
其子类BufferedWriter
、BufferedReader
和BufferedRWPair
分别缓冲可读、可写以及可读写的原始二进制流。
BufferedRandom
提供了带缓冲的可随机访问流接口。
BufferedIOBase
的另一个子类BytesIO
是内存中字节流。 -
抽象基类
TextIOBase
继承了IOBase
。它处理可表示文本的流,并处理字符串的编码和解码。 -
类
TextIOWrapper
继承了TextIOBase
,是原始缓冲流(BufferedIOBase
)的缓冲文本接口。 -
最后,
StringIO
是文本的内存流。 -
参数名不是规范的一部分,只有
open()
的参数才用作关键字参数。
下表总结了抽象基类提供的 io
模块:
抽象基类 | 继承 | 抽象方法 | Mixin方法和属性 |
---|---|---|---|
IOBase | fileno , seek , 和 truncate | close , closed , __enter__ , __exit__ , flush , isatty , __iter__ , __next__ , readable , readline , readlines , seekable , tell , writable 和 writelines | |
RawIOBase | IOBase | readinto 和 write | 继承 IOBase 方法, read , 和 readall |
BufferedIOBase | IOBase | detach , read , read1 , 和 write | 继承 IOBase 方法, readinto , 和 readinto1 |
TextIOBase | IOBase | detach , read , readline , 和 write | 继承 IOBase 方法, encoding , errors , 和 newlines |
1、I/O 基类
class io.IOBase
The abstract base class for all I/O classes.
此类为许多方法提供了空的抽象实现,派生类可以有选择地重写。
默认实现代表一个无法读取、写入或查找的文件。
尽管 IOBase
没有声明 read()
或 write()
,因为它们的签名会有所不同,但是实现和客户端应该将这些方法视为接口的一部分。此外,当调用不支持的操作时可能会引发 ValueError
(或 UnsupportedOperation
)。
从文件读取或写入文件的二进制数据的基本类型为 bytes
。其他 bytes-like objects 也可以作为方法参数。文本I/O类使用 str
数据。
请注意,在关闭的流上调用任何方法(甚至查询)都是未定义的(undefined)。在这种情况下,实现可能会引发 ValueError
。
IOBase
(及其子类)支持迭代器协议,这意味着可以迭代 IOBase
对象以产生流中的行。根据流是二进制流(产生字节)还是文本流(产生字符串),行的定义略有不同。请参见下面的 readline()
。
IOBase
也是一个上下文管理器,因此支持 with
语句。 在这个示例中,file
将在 with
语句块执行完成之后被关闭 — 即使是发生了异常:
with open('spam.txt', 'w') as file:
file.write('Spam and eggs!')
IOBase
提供以下数据属性和方法:
close()
刷新并关闭此流。
如果文件已经关闭,则此方法无效。
文件关闭后,对文件的任何操作(例如读取或写入)都会引发 ValueError
。
为方便起见,允许多次调用此方法。但是,只有第一个调用才会生效。
closed
如果流已关闭,则返回 True。
fileno()
返回流的底层文件描述符(整数)—如果存在。
如果 IO 对象不使用文件描述符,则会引发 OSError
。
flush()
刷新流的写入缓冲区(如果适用)。
这对只读和非阻塞流不起作用。
isatty()
如果流是交互式的(即连接到终端/tty设备),则返回 True
。
readable()
如果可以读取流,则返回 True
。
否则为 False
,且 read()
将引发 OSError
错误。
readline(size=-1)
从流中读取并返回一行。
如果指定了 size
,将至多读取 size
个字节。
对于二进制文件行结束符总是 \b\n
;
对于文本文件,可以用将 newline
参数传给 open()
的方式来选择要识别的行结束符。
readlines(hint=-1)
从流中读取并返回包含多行的列表。
可以指定 hint 来控制要读取的行数:如果(以字节/字符数表示的)所有行的总大小超出了 hint 则将不会读取更多的行。
0
或更小的 hint 值以及 None
,会被视为没有 hint。
请注意使用 for line in file: ...
就足够对文件对象进行迭代了,可以不必调用 file.readlines()
。
seek(offset, whence=SEEK_SET)
将流位置修改到给定的字节 offset
。
offset
将相对于由 whence
指定的位置进行解析。
whence
的默认值为 SEEK_SET
,可用值有:SEEK_SET
或 0
– 流的开头(默认值);
offset
应为零或正值SEEK_CUR
or 1
– 当前流位置;
offset
可以为负值SEEK_END
or 2
– 流的末尾;
offset
通常为负值返回新的绝对位置。
3.1 新版功能: SEEK_*
常量.
3.3 新版功能: 某些操作系统还可支持其他的值,例如 os.SEEK_HOLE
或 os.SEEK_DATA
。特定文件的可用值还会取决于它是以文本还是二进制模式打开。
seekable()
如果流支持随机访问则返回 True
。
如为 False
,则 seek()
, tell()
和 truncate()
将引发 OSError
。
tell()
返回当前流的位置。
truncate(size=None)
将流的大小调整为给定的 size
个字节(如果未指定 size
则调整至当前位置)。
当前的流位置不变。
这个调整操作可扩展或减小当前文件大小。
在扩展的情况下,新文件区域的内容取决于具体平台(在大多数系统上,额外的字节会填充为零)。
返回新的文件大小。
在 3.5 版更改: 现在Windows在扩展时将文件填充为零。
writable()
如果流支持写入则返回 True
。
如为 False
,则 write()
和 truncate()
将引发 OSError
。
writelines(lines)
将行列表写入到流。
不会添加行分隔符,因此通常所提供的每一行都带有末尾行分隔符。
__del__()
为对象销毁进行准备。
IOBase
提供了此方法的默认实现,该实现会调用实例的 close()
方法。
class io.RawIOBase
Base class for raw binary streams. It inherits IOBase
.
原始二进制流通常会提供对下层 OS 设备或 API 的低层级访问,而不是尝试将其封装到高层级的基元中(此功能是在更高层级的缓冲二进制流和文本流中实现的,将在下文中描述)。
RawIOBase
在 IOBase
的现有成员以外还提供了下列方法:
read(size=-1)
从对象中读取 size
个字节并将其返回。
作为一个便捷选项,如果 size
未指定或为 -1,则返回所有字节直到 EOF。
在其他情况下,仅会执行一次系统调用。
如果操作系统调用返回字节数少于 size
则此方法也可能返回少于 size
个字节。如果返回 0 个字节而 size
不为零 0,这表明到达文件末尾。 如果处于非阻塞模式并且没有更多字节可用,则返回 None
。默认实现会转至 readall()
和 readinto()
。
readall()
从流中读取并返回所有字节直到 EOF,如有必要将对流执行多次调用。
readinto(b)
将字节数据读入预先分配的可写 bytes-like object
b,并返回所读取的字节数。
例如,b 可以是一个 bytearray
。
如果对象处理非阻塞模式并且没有更多字节可用,则返回 None
。
write(b)
将给定的 bytes-like object
b 写入到下层的原始流,并返回所写入的字节数。
这可以少于 b 的总字节数,具体取决于下层原始流的设定,特别是如果它处于非阻塞模式的话。
如果原始流设为非阻塞并且不能真正向其写入单个字节时则返回 None
。
调用者可以在此方法返回后释放或改变 b,因此该实现应该仅在方法调用期间访问 b。
class io.BufferedIOBase
Base class for binary streams that support some kind of buffering. It inherits IOBase
.
与 RawIOBase
的主要差别在于 read()
, readinto()
和 write()
等方法将(分别)尝试按照要求读取尽可能多的输入或是耗尽所有给定的输出,其代价是可能会执行一次以上的系统调用。
除此之外,那些方法还可能引发 BlockingIOError
,如果下层的原始数据流处于非阻塞模式并且无法接受或给出足够数据的话;不同于对应的 RawIOBase
方法,它们将永远不会返回 None
。
并且,read()
方法也没有转向 readinto()
的默认实现。
典型的 BufferedIOBase
实现不应当继承自 RawIOBase
实现,而要包装一个该实现,正如 BufferedWriter
和 BufferedReader
所做的那样。
BufferedIOBase
在 IOBase
的现有成员以外还提供或重载了下列数据属性和方法:
raw
由 BufferedIOBase
处理的下层原始流 (RawIOBase
的实例)。 它不是 BufferedIOBase
API 的组成部分并且不存在于某些实现中。
detach()
从缓冲区分离出下层原始流并将其返回。在原始流被分离之后,缓冲区将处于不可用的状态。
某些缓冲区例如 BytesIO
并无可从此方法返回的单独原始流的概念。
它们将会引发 UnsupportedOperation
read(size=-1)
读取并返回最多 size
个字节。 如果此参数被省略、为 None
或为负值,则读取并返回所有数据直到 EOF。
如果流已经到达 EOF 则返回一个空的 bytes
对象。
如果此参数为正值,并且下层原始流不可交互,则可能发起多个原始读取以满足字节计数(直至先遇到 EOF)。
但对于可交互原始流,则将至多发起一个原始读取,并且简短的结果并不意味着已到达 EOF。
BlockingIOError
会在下层原始流不处于阻塞模式,并且当前没有可用数据时被引发。
read1([size])
通过至多一次对下层流的 read()
(或 readinto()
) 方法的调用读取并返回至多 size
个字节。
这适用于在 BufferedIOBase
对象之上实现你自己的缓冲区的情况。
如果 size
为 -1
(默认值),则返回任意数量的字节(多于零字节,除非已到达 EOF)。
readinto(b)
将字节数据读入预先分配的可写 bytes-like object
b 并返回所读取的字节数。
例如,b 可以是一个 bytearray
。类似于 read()
,可能对下层原始流发起多次读取,除非后者为交互式。
BlockingIOError
会在下层原始流不处于阻塞模式,并且当前没有可用数据时被引发。
readinto1(b)
将字节数据读入预先分配的可写 bytes-like object b,其中至多使用一次对下层原始流 read()
(或 readinto()
) 方法的调用。
返回所读取的字节数。
BlockingIOError
会在下层原始流不处于阻塞模式,并且当前没有可用数据时被引发。3.5 新版功能.
write(b)
写入给定的 bytes-like object
b,并返回写入的字节数 (总是等于 b 的字节长度,因为如果写入失败则会引发 OSError
)。
根据具体实现的不同,这些字节可能被实际写入下层流,或是出于运行效率和冗余等考虑而暂存于缓冲区。
当处于非阻塞模式时,如果需要将数据写入原始流但它无法在不阻塞的情况下接受所有数据则将引发 BlockingIOError
。
调用者可能会在此方法返回后释放或改变 b,因此该实现应当仅在方法调用期间访问 b。
2、原始文件 I/O
class io.FileIO
io.FileIO(name, mode=‘r’, closefd=True, opener=None)
代表一个包含字节数据的 OS 层级文件的原始二进制流。 它继承自 RawIOBase
。
name 可以是以下两项之一:
- 代表将被打开的文件路径的字符串或
bytes
对象。 在此情况下 closefd 必须为True
(默认值) 否则将会引发异常。 - 代表一个现有 OS 层级文件描述符的号码的整数,作为结果的
FileIO
对象将可访问该文件。 当 FileIO 对象被关闭时此 fd 也将被关闭,除非 closefd 设为False
。
mode 可以为 r
, w
, x
或 a
分别表示读取(默认模式)、写入、独占新建或添加。 如果以写入或添加模式打开的文件不存在将自动新建;当以写入模式打开时文件将先清空。 以新建模式打开时如果文件已存在则将引发 FileExistsError
。
以新建模式打开文件也意味着要写入,因此该模式的行为与 w
类似。 在模式中附带 +
将允许同时读取和写入。
该类的 read()
(当附带正值参数调用时), readinto()
和 write()
方法将只执行一次系统调用。
可以通过传入一个可调用对象作为 opener 来使用自定义文件打开器。 然后通过调用 opener 并传入 (name, flags) 来获取文件对象所对应的下层文件描述符。 opener 必须返回一个打开文件描述符(传入 os.open
作为 opener 的结果在功能上将与传入 None
类似)。
新创建的文件是 不可继承的。
有关 opener 参数的示例,请参见内置函数 open()
。
在 3.3 版更改: 增加了 opener 参数。增加了 x
模式。
在 3.4 版更改: 文件现在禁止继承。
FileIO
在继承自 RawIOBase
和 IOBase
的现有成员以外还提供了以下数据属性和方法:
mode
构造函数中给定的模式。
name
文件名。当构造函数中没有给定名称时,这是文件的文件描述符。
3、缓冲流
相比原始 I/O,缓冲 I/O 流提供了针对 I/O 设备的更高层级接口。
class io.BytesIO
io.BytesIO([initial_bytes])
一个使用内在字节缓冲区的二进制流。 它继承自 BufferedIOBase
。 在 close()
方法被调用时将会丢弃缓冲区。
可选参数 initial_bytes 是一个包含初始数据的 bytes-like object
。
BytesIO
在继承自 BufferedIOBase
和 IOBase
的成员以外还提供或重载了下列方法:
getbuffer()
返回一个对应于缓冲区内容的可读写视图而不必拷贝其数据。 此外,改变视图将透明地更新缓冲区内容:
>>> b = io.BytesIO(b"abcdef")
>>> view = b.getbuffer()
>>> view[2:4] = b"56"
>>> b.getvalue()
b'ab56ef'
注解:只要视图保持存在,BytesIO
对象就无法被改变大小或关闭。
getvalue()
返回包含整个缓冲区内容的 bytes
。
read1([size])
在 BytesIO
中,这与 read()
相同。在 3.7 版更改: size
参数现在是可选的。
readinto1(b)
在 BytesIO
中,这与 readinto()
相同。3.5 新版功能.
class io.BufferedReader
io.BufferedReader(raw, buffer_size=DEFAULT_BUFFER_SIZE)
一个提供对可读、不可查找的 RawIOBase
原始二进制流的高层级访问的缓冲二进制流。 它继承自 [BufferedIOBase
BufferedIOBase)。
当从此对象读取数据时,可能会从下层原始流请求更大量的数据,并存放到内部缓冲区中。 接下来可以在后续读取时直接返回缓冲数据。
根据给定的可读 raw 流和 buffer_size 创建 BufferedReader
的构造器。 如果省略 buffer_size,则会使用 [DEFAULT_BUFFER_SIZE
DEFAULT_BUFFER_SIZE)。
BufferedReader
在继承自 BufferedIOBase
和 IOBase
的成员以外还提供或重载了下列方法:
peek([size])
从流返回字节数据而不前移位置。 完成此调用将至多读取一次原始流。 返回的字节数量可能少于或多于请求的数量。
read([size])
读取并返回 size
个字节,如果 size
未给定或为负值,则读取至 EOF 或是在非阻塞模式下读取调用将会阻塞。
read1([size])
在原始流上通过单次调用读取并返回至多 size
个字节。 如果至少缓冲了一个字节,则只返回缓冲的字节。 在其他情况下,将执行一次原始流读取。在 3.7 版更改: size
参数现在是可选的。
class io.BufferedWriter
io.BufferedWriter(raw, buffer_size=DEFAULT_BUFFER_SIZE)
一个提供对可写、不可查找的RawIOBase
原始二进制流的高层级访问的缓冲二进制流。 它继承自 [BufferedIOBase
BufferedIOBase)。
当写入到此对象时,数据通常会被放入到内部缓冲区中。 缓冲区将在满足某些条件的情况下被写到下层的 RawIOBase
对象,包括:
- 当缓冲区对于所有挂起数据而言太小时;
- 当 [
flush()
BufferedWriter.flush) 被调用时 - 当(为
BufferedRandom
对象)请求seek()
时; - 当
BufferedWriter
对象被关闭或销毁时。
该构造器会为给定的可写 raw 流创建一个 BufferedWriter
。 如果未给定 buffer_size,则使用默认的 [DEFAULT_BUFFER_SIZE
DEFAULT_BUFFER_SIZE)。
BufferedWriter
在继承自 BufferedIOBase
和 IOBase
的成员以外还提供或重载了下列方法:
flush()
将缓冲区中保存的字节数据强制放入原始流。 如果原始流发生阻塞则应当引发 BlockingIOError
。
write(b)
写入 bytes-like object
b 并返回写入的字节数。 当处于非阻塞模式时,如果缓冲区需要被写入但原始流发生阻塞则将引发 BlockingIOError
。
class io.BufferedRandom
io.BufferedRandom(raw, buffer_size=DEFAULT_BUFFER_SIZE)
一个提供对不可查找的 RawIOBase
原始二进制流的高层级访问的缓冲二进制流。 它继承自 BufferedReader
和 BufferedWriter
。
该构造器会为在第一个参数中给定的可查找原始流创建一个读取器和写入器。 如果省略 buffer_size 则使用默认的 [DEFAULT_BUFFER_SIZE
DEFAULT_BUFFER_SIZE)。
BufferedRandom
能做到 BufferedReader
或 BufferedWriter
所能做的任何事。 此外,还会确保实现 seek()
和 tell()
。
class io.BufferedRWPair
io.BufferedRWPair(reader, writer, buffer_size=DEFAULT_BUFFER_SIZE)
一个提供对两个不可查找的 RawIOBase
原始二进制流的高层级访问的缓冲二进制流 — 一个可读,另一个可写。 它继承自 [BufferedIOBase
BufferedIOBase)。
reader 和 writer 分别是可读和可写的 RawIOBase
对象。 如果省略 buffer_size 则使用默认的 [DEFAULT_BUFFER_SIZE
DEFAULT_BUFFER_SIZE)。
BufferedRWPair
实现了 BufferedIOBase
的所有方法,但 detach()
除外,调用该方法将引发 UnsupportedOperation
)。
警告 :
BufferedRWPair
不会尝试同步访问其下层的原始流。 你不应当将传给它与读取器和写入器相同的对象;而要改用BufferedRandom
。
4、文本 I/O
class io.TextIOBase
Base class for text streams. This class provides a character and line based interface to stream I/O. It inherits IOBase
.
TextIOBase
在来自 IOBase
的成员以外还提供或重载了以下数据属性和方法:
encoding
用于将流的字节串解码为字符串以及将字符串编码为字节串的编码格式名称。
errors
解码器或编码器的错误设置。
newlines
一个字符串、字符串元组或者 None
,表示目前已经转写的新行。 根据具体实现和初始构造器旗标的不同,此属性或许会不可用。
buffer
由 TextIOBase
处理的下层二进制缓冲区(为一个 BufferedIOBase
的实例)。 它不是 TextIOBase
API 的组成部分并且不存在于某些实现中。
detach()
从 TextIOBase
分离出下层二进制缓冲区并将其返回。在下层缓冲区被分离后,TextIOBase
将处于不可用的状态。某些 TextIOBase
的实现,例如 StringIO
可能并无下层缓冲区的概念,因此调用此方法将引发 UnsupportedOperation
)。3.1 新版功能.
read(size=-1)
从流中读取至多 size
个字符并以单个 str
的形式返回。
如果 size
为负值或 None
,则读取至 EOF。
readline(size=-1)
读取至换行符或 EOF 并返回单个 str
。 如果流已经到达 EOF,则将返回一个空字符串。如果指定了 size
,最多将读取 size
个字符。
seek(offset, whence=SEEK_SET)
将流位置改为给定的偏移位置 offset
。 具体行为取决于 whence
形参。 whence
的默认值为 SEEK_SET
。SEEK_SET
或 0
: 从流的开始位置起查找(默认值);offset
必须为 [TextIOBase.tell()
TextIOBase.tell) 所返回的数值或为零。 任何其他 offset
值都将导致未定义的行为。SEEK_CUR
或 1
: “查找” 到当前位置;offset
必须为零,表示无操作(所有其他值均不受支持)。SEEK_END
或 2
: 查找到流的末尾;offset
必须为零(所有其他值均不受支持)。以不透明数字形式返回新的绝对位置。3.1 新版功能: SEEK_*
常量.
tell()
以不透明数字形式返回当前流的位置。 该数字通常并不代表下层二进制存储中对应的字节数。
write(s)
将字符串 s 写入到流并返回写入的字符数。
class io.TextIOWrapper
io.TextIOWrapper(buffer, encoding=None, errors=None, newline=None, line_buffering=False, write_through=False)
一个提供对 BufferedIOBase
缓冲二进制流的高层级访问的缓冲文本流。 它继承自 TextIOBase
。
encoding
给出流的解码和编码将会使用的编码格式的名称。 它默认为 locale.getpreferredencoding(False)
。 encoding="locale"
可被用来地指定当前语言区域的编码格式。 请参阅 文本编码格式 了解更多信息。
errors 是一个可选的字符串,它指明编码格式和编码格式错误的处理方式。 传入 strict
将在出现编码格式错误时引发 ValueError
(默认值 None
具有相同的效果),传入 ignore
将忽略错误。 (请注意忽略编码格式错误会导致数据丢失。) replace
会在出现错误数据时插入一个替换标记 (例如 ?
)。 backslashreplace
将把错误数据替换为一个反斜杠转义序列。 在写入时,还可以使用 xmlcharrefreplace
(替换为适当的 XML 字符引用) 或 namereplace
(替换为 \N{...}
转义序列)。 任何其他通过 codecs.register_error()
注册的错误处理方式名称也可以被接受。
newline
控制行结束符处理方式。 它可以为 None
, ``, \n
, \r
和 \r\n
。 其工作原理如下:
- 当从流读取输入时,如果
newline
为None
,则将启用universal newlines
模式。 输入中的行结束符可以为\n
,\r
或\r\n
,在返回给调用者之前它们会被统一转写为\n
。 如果newline
为 ``,也会启用通用换行模式,但行结束符会不加转写即返回给调用者。 如果newline
具有任何其他合法的值,则输入行将仅由给定的字符串结束,并且行结束符会不加转写即返回给调用者。 - 将输出写入流时,如果
newline
为None
,则写入的任何\n
字符都将转换为系统默认行分隔符os.linesep
。 - 如果
newline
是 空 或\n
,则不进行翻译。 - 如果
newline
是任何其他合法值,则写入的任何\n
字符将被转换为给定的字符串。
如果 line_buffering 为 True
,则当一个写入调用包含换行符或回车时将会应用 flush()
。
如果 write_through 为 True
,对 write()
的调用会确保不被缓冲:在 TextIOWrapper
对象上写入的任何数据会立即交给其下层的 buffer 来处理。
在 3.3 版更改: 已添加 write_through 参数
在 3.3 版更改: 默认的 encoding
现在将为 locale.getpreferredencoding(False)
而非 locale.getpreferredencoding()
。 不要使用 locale.setlocale()
来临时改变区域编码格式,要使用当前区域编码格式而不是用户的首选编码格式。
在 3.10 版更改: encoding
参数现在支持 "locale"
作为编码格式名称。
TextIOWrapper
在继承自 TextIOBase
和 IOBase
的现有成员以外还提供了以下数据属性和方法:
line_buffering
是否启用行缓冲。
write_through
写入是否要立即传给下层的二进制缓冲。3.7 新版功能.
reconfigure([, encoding][, errors][, newline][, line_buffering][, write_through])
使用 encoding, errors, newline, line_buffering 和 write_through 的新设置来重新配置此文本流。
未指定的形参将保留当前设定,例外情况是当指定了 encoding
但未指定 errors 时将会使用 errors='strict'
。
如果已经有数据从流中被读取则将无法再改变编码格式或行结束符。
另一方面,在写入数据之后再改变编码格式则是可以的。
此方法会在设置新的形参之前执行隐式的流刷新。
class io.StringIO
`io.StringIO(initial_value=‘’, newline=‘\n’)
一个使用内存文本缓冲的文本流。
它继承自 TextIOBase
。
当 close()
方法被调用时将会丢弃文本缓冲区。
缓冲区的初始值可通过提供 initial_value 来设置。
如果启用了行结束符转写,换行将以 write()
所用的方式被编码。
数据流位置将被设为缓冲区的开头。
newline
参数的规则与 TextIOWrapper
所用的一致,不同之处在于当将输出写入到流时,如果 newline
为 None
,则在所有平台上换行符都会被写入为 \n
。
StringIO
在继承自 TextIOBase
和 IOBase
的现有成员以外还提供了以下方法:
getvalue()
返回一个包含缓冲区全部内容的 str
。 换行符会以与 [read()
TextIOBase.read) 相同的方式被编码,但是流的位置不会被改变。
用法示例:
import io
f = io.StringIO("123abc")
print(f) # <_io.StringIO object at 0x7f8efa328cd0>
print(f.getvalue()) # 123abc
f.close()
f = io.StringIO()
f.write('abc123')
print(f.getvalue()) # abc123
f.close()
class io.IncrementalNewlineDecoder
用于在 universal newlines
模式下解码换行符的辅助编解码器。
它继承自 codecs.IncrementalDecoder
。
五、性能
本节讨论所提供的具体 I/O 实现的性能。
1、二进制 I/O
即使在用户请求单个字节时,也只读取和写入大块数据。
通过该方法,缓冲 I/O 隐藏了操作系统调用和执行无缓冲 I/O 例程时的任何低效性。增益取决于操作系统和执行的 I/O 类型。
例如,在某些现代操作系统上(例如 Linux),无缓冲磁盘 I/O 可以与缓冲 I/O 一样快。
但最重要的是,无论平台和支持设备如何,缓冲 I/O 都能提供可预测的性能。
因此,对于二进制数据,应首选使用缓冲的 I/O 而不是未缓冲的 I/O 。
2、文本 I/O
二进制存储(如文件)上的文本 I/O 比同一存储上的二进制 I/O 慢得多,因为它需要使用字符编解码器在Unicode和二进制数据之间进行转换。
这在处理大量文本数据(如大型日志文件)时会变得非常明显。
此外,由于使用的重构算法 TextIOWrapper.tell()
和 TextIOWrapper.seek()
都相当慢。
StringIO
是原生的内存 Unicode 容器,速度与 BytesIO
相似。
3、多线程
FileIO
对象是线程安全的,只要它们封装的操作系统调用(比如Unix下的 read(2)
)也是线程安全的。
二进制缓冲对象(例如 BufferedReader
, BufferedWriter
, BufferedRandom
和 BufferedRWPair
)使用锁来保护其内部结构;因此,可以安全地一次从多个线程中调用它们。
TextIOWrapper
对象不再是线程安全的。
4、可重入性
二进制缓冲对象( BufferedReader
, BufferedWriter
, BufferedRandom
和 BufferedRWPair
的实例)不是可重入的。虽然在正常情况下不会发生可重入调用,但仍可能会在 signal
处理程序执行 I/O 时产生。如果线程尝试重入已经访问的缓冲对象,则会引发 RuntimeError
。注意,这并不禁止其他线程进入缓冲对象。
上面的内容隐式地扩展到文本文件中,因为 open()
函数将把缓冲对象封装在 TextIOWrapper
中。 这包括标准流,因而也会影响内置的 print()
函数。
使用示例
StringIO 的使用
cache_file = StringIO()
print(cache_file.write('hello world!')) # 12, 字符长度
print(cache_file.seek(0)) # 0
print(cache_file.read()) # hello world!
print(cache_file.close()) # 释放缓冲区
- StringIO 经常被用来作字符串的缓存,因为StringIO的一些接口和文件操作是一致的,也就是说同样的代码,可以同时当成文件操作或者StringIO操作;
- 要读取StringIO,可以用一个str初始化StringIO,然后像读文件一样读取;
- 当使用read()方法读取写入的内容时,则需要先用seek()方法让指针移动到最开始的位置,否则读取不到内容(写入后指针在最末尾);
- getvalue()方法:直接获得写入后的str;
- close()方法:在关闭文件的缓冲区之后就不能再进行读写操作了;
bytes_file = BytesIO()
bytes_file.write(b'hello world')
bytes_file.seek(3)
print(bytes_file.read()) # b'lo world'
bytes_file.close()
- StringIO 操作的只能是 str,如果要操作二进制数据,就需要使用 BytesIO;
- BytesIO 实现了在内存中读写bytes,写入的不是str,而是经过 UTF-8 编码的 bytes;
- 要读取 BytesIO,可以用一个 bytes 初始化 BytesIO,然后像读文件一样读取;
2022-05-25