Python输入输出-IO流

本文介绍了Python中的IO流,包括open模块的使用方法,如打开文件、读写模式、编码解码、缓冲策略等。文章详细讲解了文本I/O和二进制I/O的区别,并提到了如何处理编码错误。此外,还探讨了缓冲流、原始文件I/O和缓冲流的类层次结构,以及在多线程环境中的安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

open模块

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
  • 打开给定路径的 file文件, 并返回对应的一个file object

  • 如果要打开的文件,不能被打开,就会触发 OSError异常;

  • file 是一个路径对象(str),表示将要打开的文件路径(绝对路径或当前工作目录的相对路径),也可以是被封装的整数类型文件描述符;

    • 如果是文件描述符,它会随着返回的I/O对象关闭而关闭,除非closefd被设为False;
  • mode 是一个可选字符串,用于指定打开文件的模式。默认是r,也就是默认以文本模式打开并读取。

    • 字符含义
      ‘r’读取(默认)
      ‘w’写入,并先截断文件原本内容
      ‘x’排它性创建,如果文件已存在则失败
      ‘b’二进制模式
      ‘a’写入,如果文件存在则在末尾追加
      ‘t’文本模式(默认)
      ‘+’打开用于更新(读取与写入)
    • 默认模式为'r'(打开用于读取文本,与rt同义)。

    • 模式'w+''w+b'将打开文件并清空内容。

    • 模式'r+''r+b'将打开文件并不清空内容。

  • 在文本模式,如果encoding 没有指定,则根据系统平台来决定使用的编码。可以使用locale.getpreferredenconding(False)来获取自己平台的编码。

    >>> import locale
    >>> locale.getpreferredencoding(False)
    'cp936'  # 我的系统编码
    
  • buffering是一个可选的整数,用于设置缓冲策略。

    • 传递0以切换缓冲关闭(仅支持二进制模式下)

      >>> import os
      >>> os.getcwd()  # 查看当前工作目录
      'C:\\Users\\www13\\AppData\\Local\\Programs\\Python\\Python38-32'
      >>> ab = open('d:/data.txt', 'ab', buffering=0)  # 创建一个没有缓冲区的 file object
      >>> ab.write('hello 你好啊')  # 写入正常字符串,报错
      --------------------------Error-------------------------------
      Traceback (most recent call last):
        File "<pyshell#4>", line 1, in <module>
          ab.write('hello 你好啊')
      TypeError: a bytes-like object is required, not 'str'
      ---------------------------错误--------------------------------
      >>> ab.write(bytes('hello 你好啊'.encode()))  # 写入二进制字符串,响应正常为写入字节长度并且不需要close或flush就可以在文本中看到。
      15
      
    • 传递1选择行缓冲(仅支持文本模式下可用),并且>1的整数以指定固定大小的块缓冲区的大小(以字节为单位)。

      >>> ab = open('d:/data.txt', 'at', buffering=1)  # 创建行缓冲 file object
      
      >>> ab.write('你好,Vin\n')  # \n换行符结尾,自动刷新缓冲区
      7
      >>> ab.write('你好,Vin')  # 不会刷新缓冲区
      6
      >>> ab.write('你好,蟒蛇\n')  # 自动刷新缓冲区
      6
      >>> ab.write('你好,蟒蛇2')
      6
      >>> ab.write('你好,蟒蛇3')
      6
      >>> ab.write('你好蟒蛇4\n')
      6
      >>> 
      # 也就是说,buffering参数设置为1的情况下,想让它刷新缓冲区,它只认得 \n 这个东西。
      
      
      >>> ab = open('d:/data.txt', 'ab', buffering=5)  # 创建自定义字节大小缓冲 file object
      >>> ab.write(bytes('hello'.encode()))  # 刚好五个字节,不会刷新缓冲区。
      5
      >>> ab.flush()  # 先把之前的缓冲区刷新一下
      >>> ab.write(bytes('buffering=5'.encode()))  # 大于五个字节,自动刷新缓冲区。
      11
      
    • 如果没有给buffering参数,则默认缓冲策略的工作方式如下:

      • 二进制文件以固定大小的块进行缓冲;使用启发式方法选择缓冲区的大小,尝试确定底层设备的“块大小”或使用io.DEFAULT_BUFFER_SIZE。在许多系统上,缓冲区的长度通常为4096或8192字节。
      • “交互式”文本文件(isatty()返回True的文件)使用行缓冲。其它文本文件使用上述策略用于二进制文件。
      >>> ab = open('d:/data.txt', 'ab')  # 创建一个有缓冲区的 file object
      >>> ab.write(bytes('hi LiLa Vin'.encode()))  # 写入响应正常,但是文本却看不到,因为还在缓冲区中,没有到达默认的缓冲区长度。
      11
      >>> ab.close()  # 或 ab.flush
      
  • encoding 是用于解码或编码文件的编码名称。

    • 应该只在文本模式下使用(也就是官方不建议使用二进制模式下,给encoding传值)
    • 默认编码是依赖于平台的(不管locale.getpreferredencoding()返回任何值),但可以使用任何Python支持的 文本编码方式
  • errors是一个可选的字符串参数

    • 用于指定如何处理编码和解码错误(这不能在二进制模式下使用)。
    • 可以使用各种标准错误处理程序,但是使用codecs.register_error()注册的任何错误处理名称也是有效的。标准名称包括:
      • 如果存在编码错误,'strict'会引发ValueError异常。默认值None具有相同的效果。
      • 'ignore' 忽略错误。如果忽略错误可能会导致数据丢失。
      • 'replace' 会将替换标记(例如'?')插入有错误数据的地方。
      • 'surrogateescape' 将表示任何不正确的字节作为Unicode专用区中的代码点,范围从U+DC80到U+DCFF。当在写入数据时使用surrogateescape错误处理程序时,这些私有代码点将被转回到相同的字节中。这对于处理未知编码的文件很有用。
      • 'xmlcharrefreplace' 只支持写入文件的时候使用。编码不支持的字符串将替换为相应的XML字符串引用,如:&#nnn;
      • 'backslashreplace' 用Python的反向转义序列替换格式错误的数据。
      • 'namereplace' 同样也只支持写入的时候使用,用\N{...}转义序列替换不支持的字符。
  • newline控制 通用换行(universal newlines)如何生效(仅支持文本模式)。它可以使None'''\n''\r''\r\n'。它的工作原理:

    • 从流中读取输入时,如果newlineNone,则启用通用换行模式。
    • 输入中的行可以以'\n''\r''\r\n'结尾,这些行被翻译成'\n'在返回使用者之前。
    • 如果它是'',则启用通用换行模式,但行结尾将返回给调用者untranslated。
    • 如果它是有任何其它合法值,则输入行仅由给定字符串终止,并且行结尾将返回给未调用的调用者。
  • closefd

    • 如果closefdFalse并且给出了文件描述符而不是文件名,那么当文件关闭时,底层文件描述符将保持打开状态
    • 如果给出文件名则closefd必须为True(默认值),否则将引发报错。
  • opener自定义open

    • 可以通过传递可调用的function来使用自定义开启器。然后通过使用参数(file, flags)调用opener 获得文件对象的基础文件描述符。
    • opener必须返回一个打开的文件描述符(使用os.open as opener时与传递None的效果相同)。
    • 新创建的文件是不可继承的。
    • Windows不可用的方法
open()函数相应类型
  • 函数所返回的file object类型取决于所用模式。

    • 当使用open()以文本模式('w','r',wt,rt等)打开文件的时候,它将返回 io.TextIOBase(特别是io.TextIOWrapper)的一个子类。

      >>> open('D:/data.txt', 'w')
      <_io.TextIOWrapper name='D:/data.txt' mode='w' encoding='cp936'>
      
    • 当使用缓冲以二进制模式打开文件的时候,返回的类型是io.BufferedIOBase的一个子类。

      >>> open('D:/data2.txt', 'xb')
      <_io.BufferedWriter name='D:/data2.txt'>
      
    • 在只读的二进制模式下,它将返回io.BufferedReader

      >>> open('D:/data.txt', 'rb')
      <_io.BufferedReader name='D:/data.txt'>
      
    • 在写入二进制和追加二进制模式下,它将返回io.BufferedWriter,而在读/写模式下,它将返回io.BufferedRandom

      >>> open('D:/data.txt', 'ab+')
      <_io.BufferedRandom name='D:/data.txt'>
      
    • 当禁用缓冲时,则会返回原始流,即io.RawIOBase的一个子类io.FileIO

      >>> open('D:/data.txt', 'ab+', buffering=0)
      <_io.FileIO name='D:/data.txt' mode='ab+' closefd=True>
      
操作模式演示
  • 'r'(默认)读取

    '''
    先创建一个文本类型文件(*.txt),写入以下文本:
    
    open the file.
    this is the cecond row.
    this is the last row.
    '''
    >>> r = open("../data.txt", "r")
    >>> read = r.read()  # 调用读取方法
    >>> print(type(read))  # 查看文本类型
    >>> print(read)	 # 查看读取到的内容
    <class 'str'>
    'open the file.'
    'this is the cecond row.'
    'this is the last row.'
    
    • 可以看到,给一个file文件路径和mode 读取模式可以正常读取到英文文本。

    • 读取中文会引发一个异常:``UnicodeDecodeError,说’gbk’` 解码器无法

      UnicodeDecodeError: 'gbk' codec can't decode byte 0xb6 in position 14: illegal multibyte sequence
      'gbk'编码器不能解码 0xb6,在第14行。
      
    • 这种问题给open方法的第三个参数,也就是encoding形参赋值,如使用万能码:utf-8就可以解决!

      >>> r = open("../data.txt", "r", encoding="utf-8")
      >>> read = r.read()
      >>> print(type(read))
      >>> print(read)
      <class 'str'>
      open the file.
      this is the cecond row.
      this is the last row.
      你好啊,Python!  # 读取到中文,不会引发异常啦。
      
  • 'w'写入并截断文本原来的内容

    >>> w = open("../data.txt", "w")
    >>> print(w.write("写入一行新文本!"))  # 打印写入字符的长度
    >>> w.close()
    8
    
    • 写入一行新文本!写入同一个文本中,但是原本的文本内容会先被清空。
  • 'x' 排它性创建文本

    >>> x = open("../data.txt", "x", encoding="utf-8-sig")  # 创建已经存在的文本
    
    • 会抛出异常:FileExistsError

      FileExistsError: [Errno 17] File exists: '../data.txt'
      
  • 'b'二进制模式

    >>> r = open('../data.txt', 'r')
    >>> print(r.read())  # 正常读取字符串
    b'open the file'  # 得到的结果是bytes类型
    
    • 可以读取二进制内容,不可以写入,读取效率高
  • 'a'追加模式

    >>> a = open('../data.txt', 'r', encoding='utf-8')
    >>> lens = a.write('\n我来凑第二行!')
    >>> a.close()
    >>> print(lens)
    8
    
    • 可以看到写入成功,如果打印结果有你写入的字符长度但是文本内没有,重新打开也没有,那肯定是没有close()来关闭I/O流或者使用a.flush()来刷新I/O流
    • 如果a模式追加的文件没有,则创建新文件,如果有则在文本末尾追加。
  • 't' 文本模式,只是一个辅助作用,就等于'r'模式隐式的添加了t了'rt',其它模式也是一样,'wt','at'等等

  • '+' 读取和写入,这个'+'必须要与有读或者写能力的模式,来进行配合使用,等同于给读模式添加了写入功能'r+'

    • 单独的'b+',没有读写功能,要使用'b+w'或者'b+r'写入二进制和读取二进制,但是不建议使用读取模式写入,写入模式读取。因为会怪怪的,让人很不适应。
  • 'U' 模式已经启用,没有任何功能。之前它会在文本模式中启用通用换行,也就是universal newlines

Python区分二进制和文本I/O
  • 以二进制模式打开的文件(包括mode参数中的b)返回的内容为bytes对象,不进行任何解码。
  • 在文本模式下(默认情况下,或者在mode参数中包含't'时)文件内容返回为str,首先使用指定的encoding(如果给定)或者使用平台默认的字节编码解码。

io.IOBase

​ 所有 I/O类的抽象父类,作用于字节流。没有公共构造函数。

​ 此类为许多方法提供了空的抽象实现,派生类可以选择重写。默认实现代表无法读取、写入或查找的文件。

IOBase没有声明read() 或 write(),因为它们的签名会有所不同,但是实现和客户端应该将这些方法视为接口的一部分。此外,当调用不支持的操作时可能会引发ValueErrorUnsupportedOperation

​ 从文本读取或写入文件的二进制数据的基本类型为bytes。其它 bytes-like objects 也可以作为方法参数。文本 I/O类使用 str 数据。

​ 在关闭的流商调用任何方法(甚至查询)都是未定义的(undefined)。 在这种情况下,实现可能会引发 ValueError

IOBase(及其子类) 支持迭代器协议,这意味着可以迭代 IOBase对象以产生流中的行。根据流的二进制流(产生字节)还是文本流(产生字符串),行的定义略有不同。通过 readline()查看。

IOBase 也是一个上下文管理器,因此支持 with 语句。在这个实例中,file 将在 with 语句块执行完成后被关闭 。即使是发生了异常,也是会关闭。

io — 处理流的核心工具

源代码: Lib/io.py


概述

io 模块提供了 Python 用于处理各种 I/O 类型的主要工具。三种主要的 I/O类型分别为: 文本 I/O, 二进制 I/O原始 I/O。这些是泛型类型,有很多种后端存储可以用在他们上面。一个隶属于任何这些类型的具体对象被称作 file object。 其他同类的术语还有 类文件对象

独立于其类别,每个具体流对象也将具有各种功能:它可以是只读,只写或读写。它还可以允许任意随机访问(向前或向后寻找任何位置),或仅允许顺序访问(例如在套接字或管道的情况下)。

所有流对提供给它们的数据类型都很敏感。例如将 str 对象给二进制流的 write() 方法会引发 TypeError。将 bytes 对象提供给文本流的 write() 方法也是如此。

在 3.3 版更改: 由于 IOError 现在是 OSError 的别名,因此用于引发 IOError 的操作现在会引发 OSError

文本 I/O

文本I/O预期并生成 str 对象。这意味着,无论何时后台存储是由字节组成的(例如在文件的情况下),数据的编码和解码都是透明的,并且可以选择转换特定于平台的换行符。

创建文本流的最简单方法是使用 open(),可以选择指定编码:

f = open("myfile.txt", "r", encoding="utf-8")

内存中文本流也可以作为 StringIO 对象使用:

f = io.StringIO("some initial text data")

TextIOBase 的文档中详细描述了文本流的API

二进制 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() 的示例。

原始 I/O

原始 I/O(也称为 非缓冲 I/O)通常用作二进制和文本流的低级构建块。用户代码直接操作原始流的用法非常罕见。不过,可以通过在禁用缓冲的情况下以二进制模式打开文件来创建原始流:

f = open("myfile.jpg", "rb", buffering=0)

RawIOBase 的文档中详细描述了原始流的API

高阶模块接口

  • io.``DEFAULT_BUFFER_SIZE

    包含模块缓冲 I/O 类使用的默认缓冲区大小的 int。 在可能的情况下 open() 将使用文件的 blksize(由 os.stat() 获得)。

  • io.``open(file, mode=‘r’, buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

    这是内置的 open() 函数的别名。open 附带参数 pathmodeflags 会引发 审计事件

  • io.``open_code(path)

    'rb' 模式打开提供的文件。如果目的是将文件内容做为可执行代码,则应使用此函数。path 应当为 str 类型并且是一个绝对路径。此函数的行为可以由对 PyFile_SetOpenCodeHook() 的先期调用所重载。 但是,如果 pathstr 类型并且是一个绝对路径,open_code(path) 的行为应当总是与 open(path, 'rb') 一致。 重载此行为的目的是为了给文件附加额外的验证或预处理。3.8 新版功能.

  • exception io.``BlockingIOError

    这是内置的 BlockingIOError 异常的兼容性别名。

  • exception io.``UnsupportedOperation

    在流上调用不支持的操作时引发的继承 OSErrorValueError 的异常。

内存中的流

也可以使用 strbytes-like object 作为文件进行读取和写入。对于字符串, StringIO 可以像在文本模式下打开的文件一样使用。 BytesIO 可以像以二进制模式打开的文件一样使用。两者都提供完整的随机读写功能。

参见

类的层次结构

I/O 流被安排为按类的层次结构实现。 首先是 抽象基类 (ABC),用于指定流的各种类别,然后是提供标准流实现的具体类。

注解

抽象基类还提供某些方法的默认实现,以帮助实现具体的流类。例如 BufferedIOBase 提供了 readinto()readline() 的未优化实现。

I/O层次结构的顶部是抽象基类 IOBase 。它定义了流的基本接口。但是请注意,对流的读取和写入之间没有分离。如果实现不支持指定的操作,则会引发 UnsupportedOperation

抽象基类 RawIOBaseIOBase 的子类。它负责将字节读取和写入流中。 RawIOBase 的子类 FileIO 提供计算机文件系统中文件的接口。

抽象基类 BufferedIOBase 处理原始字节流( RawIOBase )上的缓冲。其子类 BufferedWriterBufferedReaderBufferedRWPair 缓冲流是可读、可写以及可读写的。 BufferedRandom 为随机访问流提供缓冲接口。 BufferedIOBase 的另一个子类 BytesIO 是内存中字节流。

抽象基类 TextIOBaseIOBase 的另一个子类,它处理字节表示文本的流,并处理字符串之间的编码和解码。其一个子类 TextIOWrapper 是原始缓冲流( BufferedIOBase )的缓冲文本接口。另一个子类 StringIO 用于文本的内存流。

参数名不是规范的一部分,只有 open() 的参数才用作关键字参数。

下表总结了抽象基类提供的 io 模块:

抽象基类继承抽象方法Mixin方法和属性
IOBasefileno, seek, 和 truncateclose, closed, __enter__, __exit__, flush, isatty, __iter__, __next__, readable, readline, readlines, seekable, tell, writablewritelines
RawIOBaseIOBasereadintowrite继承 IOBase 方法, read, 和 readall
BufferedIOBaseIOBasedetach, read, read1, 和 write继承 IOBase 方法, readinto, 和 readinto1
TextIOBaseIOBasedetach, read, readline, 和 write继承 IOBase 方法, encoding, errors, 和 newlines

I/O基类

class io.IOBase

所有 I/O 类的抽象基类,作用于字节流。没有公共构造函数。

此类为许多方法提供了空的抽象实现,派生类可以有选择地重写。默认实现代表一个无法读取、写入或查找的文件。

尽管 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提供以下数据属性和方法:
  • isatty()
    • 如果流是交互式的(即连接到终端/tty设备),则返回 True
  • fileno()
    • 返回文件底层描述符(整数)–如果存在的话。
    • 如果IO对象不使用文件描述符,会引发OSError
  • close()
    • 刷新并关闭流。
    • 如果文件已经关闭,此方法无效。文件关闭后,对文件的任何操作(例如读或写),都会引发ValueError
  • closed
    • 如果流已关闭,则返回True。如果流没有关闭则返回False。
  • flush()
    • 刷新流的写入缓冲区(如果适用)。这对只读和非阻塞流不起作用。
  • readable()
    • 如果可以读取流,则返回True。否则为False,且 read() 将引发OSError错误。
  • readline(size=-1)
    • 从流中读取并返回一行。如果指定了 size,将至多读取 size 个字节。
  • readline(hint=-1)
    • 从流中读取并返回包含多行的列表。可以指定 hint 来控制要读取的行数;
    • 如果(以字节/字符数表示的)所有行的总大小超出了 hint 则将不会读取更多的行。
  • seek(offset, whence=SEEK_SET)
    • 将流位置修改到给定的字节 offset。
    • offset 将相对于由 whence 指定的位置进行解析。
    • whence 的默认值为 SEEK_SET。whence 的可用值有:
      • SEEK_SET 或 0,等同于流的开头(默认值);offset 应为零或正值。
      • SEEK_CUR 或 1,等同于当前流的位置;offset 可以为负值
      • SEEK_END 或 2,等同于流的末尾;offset 通常为负值
    • 返回新的绝对位置。
  • seekable()
    • 如果流支持随机访问则返回True。如果为False,则使用 seek(),tell()truncate()将引发OSError
  • tell()
    • 返回当前流的位置。
  • truncate(size=None)
    • 将流的大小调整为给定的 size 个字节(如果未指定 size 则调整至当前位置)。当前的流位置不变。
    • 这个调整操作可扩展或减小当前文件大小。在扩展情况下,新文件区域的内容取决于系统平台(在大多数系统上,额外的字节会填充为0)。返回新的文件大小。
    • 3.5之后:Windows在扩展时将文件填充为0。
  • writelines(lines)
    • 将流列表写入到流,不会添加行分隔符,因此通常所提供的每一行都带有末尾行分隔符。
  • writable()
    • 如果流支持写入则返回True。如果为False,则 write()truncate() 将引发 OSError
  • __del__()
    • 为对象销毁进行准备。IOBase提供来此方法的默认实现,该实现会调用实例的 close()方法。
class io.RawIOBase
  • 原始二进制I/O的基类。它继承自 IOBase。 没有公共构造器。

  • 原始二进制 I/O 通常提供对下层 OS 设备或 API 的底层级访问,而不尝试将其封装到高层级的基元中。

  • IOBase的属性和方法之外,RawIOBase 还提供了下列方法:

    • read(size=-1)
      • 从对象中读取 size 个字节并将其返回。如果 size 未指定或为 -1,则返回所有字节直到 EOF(文件结束符)。在其他情况下,仅会执行一次系统调用。如果操作系统调用返回字节数少于 size,则此方法也可能返回少于 size 个字节。
      • 如果返回 0 个字节而 size 不为 0,这表明到达文件末尾。如果处于非阻塞模式并且没有更多字节可用,则会返回 None
      • read()默认调用的话实现会跳转至 readall()readinto()
    • readall()
      • 从流中读取并返回所有字节直到 EOF,如果有必要将对流执行多次调用。
    • readinto(b)
      • 将字节数据读入预先分配的可写 bytes-like object b,并返回读取的字节数。 例如,b可以使一个 bytesarry。 如果对象处理非阻塞模式并且没有更多字符可用,则返回None
    • write(b)
      • 将给定的 bytes-like object b 写入到下层的原始流,并返回所写入的字节数。这可以少于 b 的总字节数,具体取决于下层原始流的设定,特别是如果它处于非阻塞模式的话。
      • 如果原始流设定为非阻塞模式并且不能真正向其写入单个字节时则返回 None。调用者可以在此方法返回后释放或改变 b,因此该实现应该仅在方法调用期间访问 b。
class io.BufferedIOBase
  • 支持某种缓冲的二进制流的基类。它继承自 IOBase。 没有公共构造器。

  • RawIOBase的主要差别在于 read(),readinto() 和 write() 等方法将分别尝试按照要求读取尽可能多的输入或是耗尽所有给定的输出,其代价是可能会执行一次以上的系统调用。

  • 除此之外,那些方法还可能引发 BlockingIOError, 如果下层的原始数据流处于非阻塞模式并且无法接受或给出足够的数据的话;不同于对应的 RawIOBase 方法,它们将永远不会返回 None

  • 并且,read() 方法也没有转向 readinto()的默认实现。

  • 典型的 BufferedIOBase 实现不应当继承自 RawIOBbase 实现,而要包装一个该实现,正如 BufferedWriterBufferedReader 所做的那样。

  • BufferedIOBaseIOBase 的现有成员以外还提供了或重载了下列方法和属性

    • raw
      • BufferedIOBase 处理的下层原始流(RawIOBase的实例)。它不是 BufferedIOBase API 的组成部分并且不存在于某些实现中。
    • detach()
      • 从缓冲区分离出下层原始流并将其返回。
      • 在原始流被分离后,缓冲区将处于不可用的状态。
      • BytesIO 没有 detach()方法返回的单独原始流概念。它将引发 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 会在下层原始流开始不处于阻塞模式,并且当前没有可用数据时被引发。
    • write(b)
      • 写入给定的 bytes-like object b,并返回写入的字节数(总是等于 b 的字节长度),因为如果写入失败则会引发 OSError。根据具体实现的不同,这些字节可能被实际写入下层流,或是出于运行效率和冗余等考虑而暂存于缓冲区。
      • 当出于非阻塞模式时,如果需要将数据写入原始流但它无法在不阻塞的情况下接受所有数据则引发错误BlockingIOError
      • 调用者可能会在此方法返回后释放或改变 b,因此该实现应当仅在方法调用期间访问 b。

原始文件 I/O

class io.FileIO(name, mode='r', closefd=True, opener=None)
  • FileIO 代表在 OS 层级上包含文件的字节数据。它实现来 RawIOBase 接口(因而也实现了 IOBase 接口)。

  • 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 类似)。

  • 新创建的文件是 不可继承的

  • 在 3.3 版本:增加了 opener 参数。增加来 'x' 模式。

  • 在 3.4 版本:文件现在禁止继承。

  • 除了来自 IOBaseRawIOBase 的属性和方法之外,FileIO 还提供了下列数据属性:

    • mode
      • 构造函数中给定的模式
    • name
      • 文件名。当构造函数中没有给定名称时,这是文件的文件描述符。

缓冲流

相比原始 I/O,缓冲 I/O 流提供了针对 I/O 设备的更高层级接口。

  • class io.BytesIO([initial_bytes])

    一个使用内存字节缓冲区的流实现。 它继承自 BufferedIOBase。 在 close() 方法被调用时将会丢弃缓冲区。可选参数 initial_bytes 是一个包含初始数据的 bytes-like objectBytesIO 在继承自 BufferedIOBaseIOBase 的成员以外还提供或重载了下列方法:getbuffer()返回一个对应于缓冲区内容的可读写视图而不必拷贝其数据。 此外,改变视图将透明地更新缓冲区内容:>>>>>> b = io.BytesIO(b"abcdef") >>> view = b.getbuffer() >>> view[2:4] = b"56" >>> b.getvalue() b'ab56ef'注解 只要视图保持存在,BytesIO 对象就无法被改变大小或关闭。3.2 新版功能.getvalue()返回包含整个缓冲区内容的 bytesread1([size])在 BytesIO 中,这与 read() 相同。在 3.7 版更改: size 参数现在是可选的。readinto1(b)在 BytesIO 中,这与 readinto() 相同。3.5 新版功能.

  • class io.BufferedReader(raw, buffer_size=DEFAULT_BUFFER_SIZE)

    一个提供对可读的序列型 RawIOBase 对象更高层级访问的缓冲区。 它继承自 BufferedIOBase。 当从此对象读取数据时,可能会从下层原始流请求更大量的数据,并存放到内部缓冲区中。 接下来可以在后续读取时直接返回缓冲数据。根据给定的可读 raw 流和 buffer_size 创建 BufferedReader 的构造器。 如果省略 buffer_size,则会使用 DEFAULT_BUFFER_SIZEBufferedReader 在继承自 BufferedIOBaseIOBase 的成员以外还提供或重载了下列方法:peek([size])从流返回字节数据而不前移位置。 完成此调用将至多读取一次原始流。 返回的字节数量可能少于或多于请求的数量。read([size])读取并返回 size 个字节,如果 size 未给定或为负值,则读取至 EOF 或是在非阻塞模式下读取调用将会阻塞。read1([size])在原始流上通过单次调用读取并返回至多 size 个字节。 如果至少缓冲了一个字节,则只返回缓冲的字节。 在其他情况下,将执行一次原始流读取。在 3.7 版更改: size 参数现在是可选的。

  • class io.BufferedWriter(raw, buffer_size=DEFAULT_BUFFER_SIZE)

    一个提供对可读的序列型 RawIOBase 对象更高层级访问的缓冲区。 它继承自 BufferedIOBase。 当写入到此对象时,数据通常会被放入到内部缓冲区中。 缓冲区将在满足某些条件的情况下被写到下层的 RawIOBase 对象,包括:当缓冲区对于所有挂起数据而言太小时;当 flush() 被调用时当(为 BufferedRandom 对象)请求 seek() 时;当 BufferedWriter 对象被关闭或销毁时。该构造器会为给定的可写 raw 流创建一个 BufferedWriter。 如果未给定 buffer_size,则使用默认的 DEFAULT_BUFFER_SIZEBufferedWriter 在继承自 BufferedIOBaseIOBase 的成员以外还提供或重载了下列方法:flush()将缓冲区中保存的字节数据强制放入原始流。 如果原始流发生阻塞则应当引发 BlockingIOErrorwrite(b)写入 bytes-like object b 并返回写入的字节数。 当处于非阻塞模式时,如果缓冲区需要被写入但原始流发生阻塞则将引发 BlockingIOError

  • class io.BufferedRandom(raw, buffer_size=DEFAULT_BUFFER_SIZE)

    随机访问流的带缓冲的接口。 它继承自 BufferedReaderBufferedWriter。该构造器会为在第一个参数中给定的可查找原始流创建一个读取器和定稿器。 如果省略 buffer_size 则使用默认的 DEFAULT_BUFFER_SIZEBufferedRandom 能做到 BufferedReaderBufferedWriter 所能做的任何事。 此外,还会确保实现 seek()tell()

  • class io.BufferedRWPair(reader, writer, buffer_size=DEFAULT_BUFFER_SIZE)

    一个带缓冲的 I/O 对象,它将两个单向 RawIOBase 对象 – 一个可读,另一个可写 – 组合为单个双向端点。 它继承自 BufferedIOBasereaderwriter 分别是可读和可写的 RawIOBase 对象。 如果省略 buffer_size 则使用默认的 DEFAULT_BUFFER_SIZEBufferedRWPair 实现了 BufferedIOBase 的所有方法,但 detach() 除外,调用该方法将引发 UnsupportedOperation。警告 BufferedRWPair 不会尝试同步访问其下层的原始流。 你不应当将传给它与读取器和写入器相同的对象;而要改用 BufferedRandom

文本 I/O

  • class io.TextIOBase

    文本流的基类。 该类提供了基于字符和行的流 I/O 接口。 它继承自 IOBase。 该类无公有构造器。TextIOBase 在来自 IOBase 的成员以外还提供或重载了以下数据属性和方法:encoding用于将流的字节串解码为字符串以及将字符串编码为字节串的编码格式名称。errors解码器或编码器的错误设置。newlines一个字符串、字符串元组或者 None,表示目前已经转写的新行。 根据具体实现和初始构造器旗标的不同,此属性或许会不可用。bufferTextIOBase 处理的下层二进制缓冲区(为一个 BufferedIOBase 的实例)。 它不是 TextIOBase API 的组成部分并且不存在于某些实现中。detach()从 TextIOBase 分离出下层二进制缓冲区并将其返回。在下层缓冲区被分离后,TextIOBase 将处于不可用的状态。某些 TextIOBase 的实现,例如 StringIO 可能并无下层缓冲区的概念,因此调用此方法将引发 UnsupportedOperation3.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_SETSEEK_SET0: 从流的开始位置起查找(默认值);offset 必须为 TextIOBase.tell() 所返回的数值或为零。 任何其他 offset 值都将导致未定义的行为。SEEK_CUR1: “查找” 到当前位置;offset 必须为零,表示无操作(所有其他值均不受支持)。SEEK_END2: 查找到流的末尾;offset 必须为零(所有其他值均不受支持)。以不透明数字形式返回新的绝对位置。3.1 新版功能: SEEK_* 常量.tell()以不透明数字形式返回当前流的位置。 该数字通常并不代表下层二进制存储中对应的字节数。write(s)将字符串 s 写入到流并返回写入的字符数。

  • class io.TextIOWrapper(buffer, encoding=None, errors=None, newline=None, line_buffering=False, write_through=False)

    一个基于 BufferedIOBase 二进制流的缓冲文本流。 它继承自 TextIOBaseencoding 给出流被解码或编码时将使用的编码格式。 它默认为 locale.getpreferredencoding(False)errors 是一个可选的字符串,它指明编码格式和编码格式错误的处理方式。 传入 'strict' 将在出现编码格式错误时引发 ValueError (默认值 None 具有相同的效果),传入 'ignore' 将忽略错误。 (请注意忽略编码格式错误会导致数据丢失。) 'replace' 会在出现错误数据时插入一个替换标记 (例如 '?')。 'backslashreplace' 将把错误数据替换为一个反斜杠转义序列。 在写入时,还可以使用 'xmlcharrefreplace' (替换为适当的 XML 字符引用) 或 'namereplace' (替换为 \N{...} 转义序列)。 任何其他通过 codecs.register_error() 注册的错误处理方式名称也可以被接受。newline 控制行结束符处理方式。 它可以为 None, '', '\n', '\r''\r\n'。 其工作原理如下:当从流中读取输入时,如果 newlineNone,则会启用 universal newlines 模式。 输入中的行结束符可以为 '\n', '\r''\r\n',在返回给调用者之前它们会被统一转写为 '\n'。 如果参数为 '',也会启用通用换行模式,但行结束符会不加转写即返回给调用者。 如果它具有任何其他合法的值,则输入行将仅由给定的字符串结束,并且行结束符会不加转写即返回给调用者。将输出写入流时,如果 newlineNone,则写入的任何 '\n' 字符都将转换为系统默认行分隔符 os.linesep。如果 newline'''\n',则不进行翻译。如果 newline 是任何其他合法值,则写入的任何 '\n' 字符将被转换为给定的字符串。如果 line_bufferingTrue,则当一个写入调用包含换行符或回车时将会应用 flush()。如果 write_throughTrue,对 write() 的调用会确保不被缓冲:在 TextIOWrapper 对象上写入的任何数据会立即交给其下层的 buffer 来处理。在 3.3 版更改: 已添加 write_through 参数在 3.3 版更改: 默认的 encoding 现在将为 locale.getpreferredencoding(False) 而非 locale.getpreferredencoding()。 不要使用 locale.setlocale() 来临时改变区域编码格式,要使用当前区域编码格式而不是用户的首选编码格式。TextIOWrapper 在继承自 TextIOBase 及其父类的现有成员以外还提供了下列成员:line_buffering是否启用行缓冲。write_through写入是否要立即传给下层的二进制缓冲。3.7 新版功能.reconfigure(**[, encoding][, errors][, newline][, line_buffering][, write_through]*)使用 encoding, errors, newline, line_bufferingwrite_through 的新设置来重新配置此文本流。未指定的形参将保留当前设定,例外情况是当指定了 encoding 但未指定 errors 时将会使用 errors='strict'。如果已经有数据从流中被读取则将无法再改变编码格式或行结束符。 另一方面,在写入数据之后再改变编码格式则是可以的。此方法会在设置新的形参之前执行隐式的流刷新。3.7 新版功能.

  • class io.StringIO(initial_value=’’, newline=’\n’)

    用于文本 I/O 的内存数据流。 当调用 close() 方法时将会丢弃文本缓冲区。缓冲区的初始值可通过提供 initial_value 来设置。 如果启用了行结束符转写,换行将以 write() 所用的方式被编码。 数据流位置将被设为缓冲区的开头。newline 参数的规则与 TextIOWrapper 所用的一致。 默认规则是仅将 \n 字符视为行结束符并且不执行换行符转写。 如果 newline 设为 None,在所有平台上换行符都将被写入为 \n,但当读取时仍然会执行通用换行编码格式。StringIO 在继承自 TextIOBase 及其父类的现有成员以外还提供了以下方法:getvalue()返回一个包含缓冲区全部内容的 str。 换行符会以与 read() 相同的方式被编码,但是流的位置不会被改变。用法示例:

    import io
    >>> output = io.StringIO()
    >>> output.write('First line.\n')
    >>> print('Second line.', file=output)
    # Retrieve file contents -- this will be
    # 'First line.\nSecond line.\n' 
    >>> contents = output.getvalue()
    # Close object and discard memory buffer -- 
    # .getvalue() will now raise an exception. 
    >>> output.close() `
    
  • class io.IncrementalNewlineDecoder

    用于在 universal newlines 模式下解码换行符的辅助编解码器。 它继承自 codecs.IncrementalDecoder

性能

本节讨论所提供的具体 I/O 实现的性能。

二进制 I/O

即使在用户请求单个字节时,也只读取和写入大块数据。通过该方法,缓冲 I/O 隐藏了操作系统调用和执行无缓冲 I/O 例程时的任何低效性。增益取决于操作系统和执行的 I/O 类型。例如,在某些现代操作系统上(例如 Linux),无缓冲磁盘 I/O 可以与缓冲 I/O 一样快。但最重要的是,无论平台和支持设备如何,缓冲 I/O 都能提供可预测的性能。因此,对于二进制数据,应首选使用缓冲的 I/O 而不是未缓冲的 I/O 。

文本 I/O

二进制存储(如文件)上的文本 I/O 比同一存储上的二进制 I/O 慢得多,因为它需要使用字符编解码器在Unicode和二进制数据之间进行转换。这在处理大量文本数据(如大型日志文件)时会变得非常明显。此外,由于使用的重构算法 TextIOWrapper.tell()TextIOWrapper.seek() 都相当慢。

StringIO 是原生的内存 Unicode 容器,速度与 BytesIO 相似。

多线程

FileIO 对象是线程安全的,只要它们封装的操作系统调用(比如Unix下的 read(2) )也是线程安全的。

二进制缓冲对象(例如 BufferedReader, BufferedWriter, BufferedRandomBufferedRWPair)使用锁来保护其内部结构;因此,可以安全地一次从多个线程中调用它们。

TextIOWrapper 对象不再是线程安全的。

可重入性

二进制缓冲对象( BufferedReaderBufferedWriterBufferedRandomBufferedRWPair 的实例)不是可重入的。虽然在正常情况下不会发生可重入调用,但仍可能会在 signal 处理程序执行 I/O 时产生。如果线程尝试重入已经访问的缓冲对象,则会引发 RuntimeError 。注意,这并不禁止其他线程进入缓冲对象。

上面的内容隐含地扩展到文本文件,因为 open() 函数会把缓冲对象封装在 TextIOWrapper 中。这包括标准流,因此也会影响内置函数 print()

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值