URLhttps://learnscript.net/zh/python/file-system/files/file-objects/
    复制链接转到说明  示例

    如何使用 Python 文件对象读写文件?以及文件指针,关闭文件,获取文件对象信息等问题

    我被代码海扁署名-非商业-禁演绎
    阅读 27:52·字数 8363·更新 

    本节主要讲述如何使用 Python 文件对象读写文件,至于如何打开文件,以及编码,换行,缓冲等问题,请参考如何使用 open 函数打开文件?以及编码,解码,换行,缓冲等问题一节。

    Python 文件对象

    Python 文件对象负责文件的读取,写入,关闭等操作,通常作为内置函数open的返回值,根据所传递的参数的不同,open函数返回的 Python 文件对象可能是以下类型之一,TextIOWrapper(文本文件),BufferedReader(拥有缓冲区的只读二进制文件),BufferedWriter(拥有缓冲区的只写二进制文件),BufferedRandom(拥有缓冲区的可读写的二进制文件),FileIO(禁用缓冲区的二进制文件)。

    读取文件中的字符或字节

    当文件对象允许读取时,可以使用read方法读取本文文件中的字符或二进制文件中的字节,在读取的同时,文件指针将被移动。如果是读取字符,那么read方法返回str字符串,如果是读取字节,那么read方法返回bytes字节序列(你可以将其视为另类的“字符串”)。

    read(size=-1)

    size 参数

    size参数表示能够读取的最大字符数(文本文件)或最大字节数(二进制文件),如果该参数为None或小于0,那么将读取从当前指针位置到文件末尾的所有字符或字节。

    这里需要指出,英文字母和汉字均被视为一个字符。

    对于禁用缓冲区的二进制文件,文件对象的readall方法可以读取从当前指针位置到文件末尾的所有字符或字节,该函数的执行效果与调用read方法但不给出size参数的效果类似。

    readall()

    打开文件

    关于如何在 Pythonopen函数中设置缓冲策略,请查看指定缓冲策略一段。

    在下面的示例中,通过read方法读取的前 4 个字节,包括“你”字的 3 个字节,以及“好”字的第一个字节。

    read.py
    # 请将命令行跳转至脚本文件 read.py 所在的目录,然后运行他
    # 读取文件中的字符
    with open('read.txt', 'r', encoding='utf8') as file:
    	print(f'读取 4 个字符:{file.read(4)}')
    	print(f'读取剩余字符:{file.read()}')
    
    # 读取文件中的字节 with open('read.txt', 'rb') as file: print(f'读取 4 个字节:{file.read(4)}') print(f'读取剩余字节:{file.read()}')
    # 读取文件中的字节,但禁用缓冲区 with open('read.txt', 'rb', buffering=0) as file: print(f'读取 4 个字节:{file.read(4)}') print(f'读取剩余字节:{file.readall()}')
    read.txt
    你好,world!
    Yes,很好
    读取 4 个字符:你好,w
    读取剩余字符:orld!
    Yes,很好
    读取 4 个字节:b'\xe4\xbd\xa0\xe5'
    读取剩余字节:b'\xa5\xbd\xef\xbc\x8cworld\xef\xbc\x81\r\nYes\xef\xbc\x8c\xe5\xbe\x88\xe5\xa5\xbd'
    读取 4 个字节:b'\xe4\xbd\xa0\xe5'
    读取剩余字节:b'\xa5\xbd\xef\xbc\x8cworld\xef\xbc\x81\r\nYes\xef\xbc\x8c\xe5\xbe\x88\xe5\xa5\xbd'

    当指针位于文件末尾时,read 方法将返回空字符串或空字节序列

    当文件指针位于文件末尾时,调用文件对象的read方法将返回空字符串''(文本文件)或空字节序列b''(二进制文件),而不是空值None

    read_eof.py
    # 请将命令行跳转至脚本文件 read_eof.py 所在的目录,然后运行他
    with open('read.txt', 'r', encoding='utf8') as file:
    	file.read()
    	print(f'从文件末尾读取字符:{file.read()}')
    
    with open('read.txt', 'rb') as file: file.read() print(f'从文件末尾读取字节:{file.read()}')
    从文件末尾读取字符:
    从文件末尾读取字节:b''

    使用 Python 文件对象读取文件中的行

    当文件对象允许读取时,可使用readline方法读取从当前指针位置到行末尾或文件末尾的所有字符或字节(包括换行符和回车符,如果是文本文件,那么由open函数的newline参数决定),并可以指定能够读取的最大字符数或最大字节数。

    文件对象的readline方法的返回值是字符串或bytes字节序列,在读取字符的同时,文件指针将被移动。

    readline(size=-1)

    size 参数

    size参数为能够读取的最大字符数(文本文件)或最大字节数(二进制文件),默认为-1,表示没有最大字符数或最大字节数的限制。

    打开文件

    关于open函数的newline参数,你可以查看指定换行₁的处理方式一段。

    在下面的示例中,由于我们分别使用read方法读取了 4 个字符和 4 个字节,因此,接下来的readline方法会从第 5 个字符和第 5 个字节开始读取。

    readline.py
    # 请将命令行跳转至脚本文件 readline.py 所在的目录,然后运行他
    # 读取文本文件中的行
    with open('read.txt', 'r', encoding='utf8') as file:
    	print(f'读取 4 个字符:{file.read(4)}')
    	# 包括 \n
    	print(f'读取行:{file.readline()}')
    	print(f'读取行(最多 2 个字符):{file.readline(2)}')
    	print(f'读取行:{file.readline()}')
    
    # 读取二进制文件中的行 with open('read.txt', 'rb') as file: print(f'读取 4 个字节:{file.read(4)}') # 包括 \r\n print(f'读取行:{file.readline()}') print(f'读取行(最多 2 个字节):{file.readline(2)}') print(f'读取行:{file.readline()}')
    读取 4 个字符:你好,w
    读取行:orld!

    读取行(最多 2 个字符):Ye
    读取行:s,很好
    读取 4 个字节:b'\xe4\xbd\xa0\xe5'
    读取行:b'\xa5\xbd\xef\xbc\x8cworld\xef\xbc\x81\r\n'
    读取行(最多 2 个字节):b'Ye'
    读取行:b's\xef\xbc\x8c\xe5\xbe\x88\xe5\xa5\xbd'

    当指针位于文件末尾时,readline 方法将返回空字符串或空字节序列

    当文件指针位于文件末尾时,调用文件对象的readline方法将返回空字符串''(文本文件)或空字节序列b''(二进制文件),而不是空值None

    readline_eof.py
    # 请将命令行跳转至脚本文件 readline_eof.py 所在的目录,然后运行他
    with open('read.txt', 'r', encoding='utf8') as file:
    	file.read()
    	print(f'从文件末尾读取一行:{file.readline()}')
    
    with open('read.txt', 'rb') as file: file.read() print(f'从文件末尾读取一行:{file.readline()}')
    从文件末尾读取一行:
    从文件末尾读取一行:b''

    当文件对象允许读取时,可使用readlines方法读取从当前指针位置到文件末尾的所有行(包括换行符和回车符,如果是文本文件,由open函数的newline参数决定),并可以指定能够读取的最大字符数或最大字节数,在读取的同时,文件指针将被移动。

    文件对象的readlines方法的返回值是一个列表对象,列表中的元素对应从文件读取到的行(一个字符串或字节序列)。

    readlines(hint=-1)

    hint 参数

    hint参数为能够读取的最大字符数(文本文件)或最大字节数(二进制文件),默认为-1,表示没有最大字符数或最大字节数的限制。

    需要说明的是,即便已经超出了最大字符数或最大字节数,文件对象的readlines方法依然会读取至行的末尾或文件末尾。

    在下面的示例中,我们分别使用readlines方法读取了 12 个字符和 12 个字节,文件指针将到达第二行和留在第一行,对于这两种情况,文件对象均会读取至行末尾。

    readlines.py
    # 请将命令行跳转至脚本文件 readlines.py 所在的目录,然后运行他
    # 读取文本文件中的多个行
    with open('readlines.txt', 'r', encoding='utf8') as file:
    	print(f'读取多个行(最多 12 个字符):{file.readlines(12)}')
    	print(f'读取剩余的行:{file.readlines()}')
    
    # 读取二进制文件中的多个行 with open('readlines.txt', 'rb') as file: print(f'读取多个行(最多 12 个字节):{file.readlines(12)}') print(f'读取剩余的行:{file.readlines()}')
    readlines.txt
    你好,world!
    Yes,很好
    What a day!
    读取多个行(最多 12 个字符):['你好,world!\n', 'Yes,很好\n']
    读取剩余的行:['What a day!']
    读取多个行(最多 12 个字节):[b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8cworld\xef\xbc\x81\r\n']
    读取剩余的行:[b'Yes\xef\xbc\x8c\xe5\xbe\x88\xe5\xa5\xbd\r\n', b'What a day!']

    当指针位于文件末尾时,readlines 方法将返回空的列表

    当文件指针位于文件末尾时,调用文件对象的readlines方法将返回空的列表[],而不是空值None

    readlines_eof.py
    # 请将命令行跳转至脚本文件 readlines_eof.py 所在的目录,然后运行他
    with open('readlines.txt', 'r', encoding='utf8') as file:
    	file.read()
    	print(f'从文件末尾读取多个行:{file.readlines()}')
    
    with open('readlines.txt', 'rb') as file: file.read() print(f'从文件末尾读取多个行:{file.readlines()}')
    从文件末尾读取多个行:[]
    从文件末尾读取多个行:[]

    通过遍历 Python 文件对象读取文件中的行

    除了使用方法readlinereadlines来读取文件中的行,你还可以通过遍历文件对象的方式来读取行,因为文件对象也是迭代器对象。

    for.py
    # 请将命令行跳转至脚本文件 for.py 所在的目录,然后运行他
    # 使用 for 语句遍历文件
    with open('readlines.txt', 'r', encoding='utf8') as file:
    	for l in file:
    		print(l)
    你好,world!

    Yes,很好

    What a day!

    将二进制文件中的字节读取至类字节序列对象

    对于二进制文件,你可以使用文件对象的readinto方法,将文件中的字节读取至类字节序列(bytes-like)对象。readinto方法会尝试读取足够多的字节,以填充类字节序列对象,并将实际读取的字节数作为返回值(int类型)。

    readinto(b)

    b 参数

    b参数是一个可以写入的类字节序列对象。

    在下面的示例中,我们尝试将二进制文件中剩余的 23 个字节读取至bytearray,因此bytearray的最后 7 个字节为\x00

    readinto.py
    # 请将命令行跳转至脚本文件 readinto.py 所在的目录,然后运行他
    # 读取字节至 bytearray
    with open('read.txt', 'rb') as file:
    	ba8 = bytearray(8)
    	# 尝试读取 8 个字节以填充 ba
    	print(f'读取了 {file.readinto(ba8)} 个字节')
    	print(ba8)
    
    ba30 = bytearray(30) # 读取剩余的 23 个字节 print(f'读取了 {file.readinto(ba30)} 个字节') print(ba30)
    读取了 8 个字节
    bytearray(b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc')
    读取了 23 个字节
    bytearray(b'\x8cworld\xef\xbc\x81\r\nYes\xef\xbc\x8c\xe5\xbe\x88\xe5\xa5\xbd\x00\x00\x00\x00\x00\x00\x00')

    将字符或字节写入文件

    当文件对象允许写入时,可使用write方法将字符或字节写入本文文件或二进制文件,并返回写入的字符数或字节数。

    write(s|b)

    s,b 参数

    s参数为需要写入的字符串,b参数为包含需要写入的字节的类字节序列对象。

    utf8编码中的大部分汉字占用 3 个字节,字符串'第一行\n第二行'在解码后是一个包含 19 个字节的bytes对象。

    write.py
    # 请将命令行跳转至脚本文件 write.py 所在的目录,然后运行他
    # 将字符写入文件
    with open('write.txt', 'w', encoding='utf8') as file:
    	c = file.write('第一行\n第二行')
    	print(f'写入 {c} 个字符')
    
    # 将字节写入文件 with open('writeb.txt', 'wb') as file: bs = '第一行\n第二行'.encode() c = file.write(bs) print(f'写入 {c} 个字节')
    写入 7 个字符
    写入 19 个字节

    将行写入文件

    当文件对象允许写入时,可以使用writelines方法将多行数据(字符或字节)写入本文文件或二进制文件。

    writelines(lines)

    lines 参数

    lines参数是一个迭代器对象,迭代器中的元素是需要写入的字符串,或包含需要写入的字节的类字节序列(bytes-like)对象。

    writelines 方法不会自动为每一行添加换行符或回车符

    文件对象的writelines方法并不会自动为每一行添加换行符或回车符(或者换行符或回车符对应的字节),因此需要开发人员自行处理。

    在下面的示例中,我们自行为第一行添加换行符和其对应的字节。

    writelines.py
    # 请将命令行跳转至脚本文件 writelines.py 所在的目录,然后运行他
    # 将多个行写入文件
    with open('writelines.txt', 'w', encoding='utf8') as file:
    	file.writelines(('第一行\n', '第二行'))
    
    # 将多个行写入文件 with open('writelinesb.txt', 'wb') as file: file.writelines(('第一行\n'.encode(), '第二行'.encode()))

    将缓冲区中的内容写入文件

    对于支持写入操作并使用了缓冲区的文件对象,其方法flush可用于立即将缓冲区中的内容写入文件,当然,很多情况下,调用该方法是没有必要的。

    对于不支持写入操作或禁用了缓冲区的文件对象,调用flush方法不会产生任何效果。

    flush()

    在下面的示例中,由于缓冲区的大小为4b'abc'会保留在缓冲区中,因为他们仅占用 3 个字节。

    flush.py
    # 请将命令行跳转至脚本文件 flush.py 所在的目录,然后运行他
    with open('flush.txt', 'wb', buffering=4) as file:
    	file.write(b'abc')
    	# 将缓冲区中的 abc 写入文件
    	file.flush()

    获取和设置文件指针的位置

    在绝大多数情况下,文件指针的位置是文件对象从文件读取内容或将内容写入文件的开始位置,并且会在读取或写入内容后发生变化,以方便下一次的读取和写入。在打开文件之后,文件指针的位置可能在文件开头或末尾。

    无论是文本文件还是二进制文件,文件指针的位置总是以字节为单位,因此,对于文本文件对应的文件对象,需要得知字符对应的字节数才能正确的移动文件指针,否则可能导致错误的发生。

    文件对象提供了名称为tell的方法,用于获得当前文件指针的位置,该方法返回一个整数,如果返回值为0,则表示指针位于文件的开头。

    tell()

    文件对象提供了名称为seek的方法,用于改变文件指针的位置。

    seek(offset, whence)

    offset 参数

    offset参数是一个表示文件指针偏移值的整数,当offset大于0时,文件指针将向文件末尾移动指定的字节数,当offset小于0时,文件指针将向文件开头移动指定的字节数。

    whence 参数

    whence参数是一个整数,如果为0(默认值,os.SEEK_SET),则从文件的开头位置计算offset表示的指针偏移值,如果为1os.SEEK_CUR),则从当前文件指针位置计算offset表示的指针偏移值,如果为2os.SEEK_END),则从文件的末尾位置计算offset表示的指针偏移值。

    在下面的示例中,我们打开了文本文件read.txt,因此要移动文件指针的位置,需要知道字符所占用的字节数,这里文件开头处的“你好”两字占用了 6 个字节。

    pointer.py
    # 请将命令行跳转至脚本文件 pointer.py 所在的目录,然后运行他
    file = open('read.txt', 'r', encoding='utf8')
    
    # 开始时指针位于文件开头 print(f'指针位置:{file.tell()}') # 将指针移动到“你好”之后,“你好”占用了 6 个字节 file.seek(6, 0) # 读取下一个字符“,” print(file.read(1)) # 由于“,”占用 3 个字节,因此指针位置现在为 9 print(f'指针位置:{file.tell()}')
    指针位置:0

    指针位置:9

    文件对象不能将文件指针的位置设置为负数

    无论如何,文件对象无法将文件指针的位置设置为负数,否则将导致异常。不过,使文件指针的位置超出文件末尾并没有问题。

    在下面的示例中,我们将文件指针移动到文件末尾后 100 的位置,但之后的read方法并没有抛出异常。

    pointer_err.py
    # 请将命令行跳转至脚本文件 pointer_err.py 所在的目录,然后运行他
    file = open('read.txt', 'r+b')
    
    # 文件指针位置将超出文件末尾 file.seek(100, 2) print(f'指针位置:{file.tell()}') # 读取文件不会产生异常 print(file.read())
    # ERROR 文件指针的位置不能是负数 file.seek(-100, 0)
    指针位置:131
    b''
    OSError: [Errno ] Invalid argument

    判断 Python 文件对象是否支持随机访问

    当文件对象不支持文件的随机访问时,你将不能获取或设置文件指针的位置,即不能调用文件对象的tellseek方法,否则会引发异常OSError

    通过文件对象的seekable方法,可以判断文件对象是否支持随机访问,如果该方法返回True,那么表示支持随机访问。

    seekable()

    判断 Python 文件对象是否可以读取或写入

    文件对象的readablewritable方法返回一个布尔值,用于表示文件对象是否支持读取和写入操作(主要由open函数的mode参数决定),返回True表示支持。

    readable()
    writable()

    打开文件

    关于open函数的mode参数,你可以查看指定文件打开模式一段。

    info.py
    # 请将命令行跳转至脚本文件 info.py 所在的目录,然后运行他
    with open('read.txt', 'r', encoding='utf8') as file:
    	print(f'读取:{file.readable()},写入:{file.writable()}')
    
    with open('read.txt', 'r+b') as file: print(f'读取:{file.readable()},写入:{file.writable()}')
    读取:True,写入:False
    读取:True,写入:True

    关闭文件

    文件对象拥有一个名称为close的方法,用于关闭文件对象对应的文件,一旦该方法被调用,你将无法通过文件对象对文件实施操作,比如,读取和写入。

    close()

    确保在代码中调用文件对象的 close 方法

    虽然文件对象会在销毁时尝试关闭文件,但在代码中明确的调用文件对象的close方法,依然是保险的做法,否则通过文件对象写入的内容存在无法保存至文件的可能。

    如果使用with语句和open函数打开文件,那么不需要调用文件对象的close方法,因为,该方法会被文件对象的__exit__方法直接或间接调用。

    with 语句

    关于with语句,你可以查看Python with 语句,with 语句上下文管理器介绍一节。

    在下面的示例中,由于文件对象已经调用了close方法,因此,再次通过文件对象写入数据将导致异常。

    close.py
    # 请将命令行跳转至脚本文件 close.py 所在的目录,然后运行他
    file = open('close.txt', 'w', encoding='utf8')
    file.write('写入一些数据!')
    # 关闭文件
    file.close()
    
    # ERROR 文件已经关闭 file.write('再写入一些数据?')
    ValueError: I/O operation on closed file.

    判断 Python 文件对象对应的文件是否已经关闭

    通过文件对象的closed属性,可以检查文件对象对应的文件是否已经关闭,如果closed属性为True,那么表明文件已经关闭。

    closed

    在下面的示例中,我们通过with语句打开文件,在with语句结束时,文件对象的__exit__方法将被调用,而__exit__方法将调用close方法关闭文件。

    closed.py
    # 请将命令行跳转至脚本文件 closed.py 所在的目录,然后运行他
    # with 语句结束之后,文件将被关闭
    with open('close.txt', 'w', encoding='utf8') as file:
    	file.write('写入一些数据!')
    
    # 检查文件是否被关闭 if file.closed: print('文件 close.txt 已经关闭')
    文件 close.txt 已经关闭

    截断文件

    对于支持写入的文件对象,可以使用truncate方法将文件截断,并返回截断后的大小,该方法不会改变文件指针的位置。

    truncate(size=None)

    size 参数

    size参数是一个整数(大于等于0),表示在第几个字节处截断文件。如果该参数被忽略或者为None,则在当前文件指针的位置截断文件(注意,实际效果可能并非如此)。

    事实上,size参数所指示的位置可以超出文件末尾,这会导致文件的末尾被填充若干字节\x00

    在下面的示例中,“真是美好”占用了 12 个字节,调用方法truncate之后文件指针没有发生变化。

    truncate.py
    # 请将命令行跳转至脚本文件 truncate.py 所在的目录,然后运行他
    with open('truncate.txt', 'r+', encoding="utf8") as file:
    	# 显示文件内容
    	print(file.read())
    	# 保留“真是美好”
    	print(f'从第 12 个字节处截断:{file.truncate(12)}')
    	# 文件指针位置不会被 truncate 方法改变
    	print(f'当前指针位置:{file.tell()}')
    truncate.txt
    真是美好的一天
    真是美好的一天
    从第 12 个字节处截断:12
    当前指针位置:21

    获取 Python 文件对象的其他信息

    在使用内置函数open创建文件对象时,我们会使用一些参数来设置文件对象的某些功能,而文件对象则提供了以下变量,属性,方法来说明他们。

    文件对象的name属性是文件路径或文件描述器(file descriptor)对应的整数,这与传递给open函数的file参数有关。

    文件对象的fileno方法返回文件描述器对应的整数,是的,即便你向open函数传递文件路径,但最终使用的是文件描述器。

    文件对象的mode属性表示打开模式,这与传递给open函数的mode参数有关。

    name
    fileno()
    mode

    对于文本文件,文件对象的encoding变量表示编码格式,errors变量表示编码或解码错误的处理方式,他们均与传递给open函数的同名参数有关。

    对于文本文件,文件对象的line_buffering属性是一个布尔值,表示是否使用了行缓冲,如果为True,那么表示使用了行缓冲。

    encoding
    errors
    line_buffering

    对于禁用缓冲区的二进制文件,文件对象的closefd属性表示当文件关闭时是否同时关闭文件描述器,这与传递给open函数的closefd参数有关。

    closefd

    info.py
    # …
    with open('read.txt', 'r', encoding='utf8') as file:
    	print(f'name {file.name}')
    	print(f'fileno {file.fileno()}')
    	print(f'mode {file.mode}')
    	print(f'encoding {file.encoding}')
    	print(f'errors {file.errors}')
    	print(f'line_buffering {file.line_buffering}')
    
    import os fd = os.open('read.txt', os.O_RDONLY | os.O_BINARY) with open(fd, 'rb', buffering=0, closefd=False) as file: print(f'name {file.name}') print(f'closefd {file.closefd}')
    name read.txt
    fileno 3
    mode r
    encoding utf8
    errors strict
    line_buffering False
    name 3
    fileno 3
    closefd False

    源码

    src/zh/file_system/files/file_objects·codebeatme/python·GitHub