open() 返回一个 file object ,最常使用的是两个位置参数和一个关键字参数:open(filename, mode, encoding=None)
>>>
>>> f = open('workfile', 'w', encoding="utf-8")
第一个实参是文件名字符串。第二个实参是包含描述文件使用方式字符的字符串。mode 的值包括 'r'
,表示文件只能读取;'w'
表示只能写入(现有同名文件会被覆盖);'a'
表示打开文件并追加内容,任何写入的数据会自动添加到文件末尾。'r+'
表示打开文件进行读写。mode 实参是可选的,省略时的默认值为 'r'
。
通常情况下,文件是以 text mode 打开的,也就是说,你从文件中读写字符串,这些字符串是以特定的 encoding 编码的。如果没有指定 encoding ,默认的是与平台有关的(见 open() )。因为 UTF-8 是现代事实上的标准,除非你知道你需要使用一个不同的编码,否则建议使用 encoding="utf-8"
。在模式后面加上一个 'b'
,可以用 binary mode 打开文件。二进制模式的数据是以 bytes 对象的形式读写的。在二进制模式下打开文件时,你不能指定 encoding 。
在文本模式下读取文件时,默认把平台特定的行结束符(Unix 上为 \n
, Windows 上为 \r\n
)转换为 \n
。在文本模式下写入数据时,默认把 \n
转换回平台特定结束符。这种操作方式在后台修改文件数据对文本文件来说没有问题,但会破坏 JPEG
或 EXE
等二进制文件中的数据。注意,在读写此类文件时,一定要使用二进制模式。
在处理文件对象时,最好使用 with 关键字。优点是,子句体结束后,文件会正确关闭,即便触发异常也可以。而且,使用 with
相比等效的 try-finally 代码块要简短得多:
>>>
>>> with open('workfile', encoding="utf-8") as f:
... read_data = f.read()
>>> # We can check that the file has been automatically closed.
>>> f.closed
True
如果没有使用 with 关键字,则应调用 f.close()
关闭文件,即可释放文件占用的系统资源。
警告
调用 f.write()
时,未使用 with
关键字,或未调用 f.close()
,即使程序正常退出,也**可能** 导致 f.write()
的参数没有完全写入磁盘。
通过 with 语句,或调用 f.close()
关闭文件对象后,再次使用该文件对象将会失败。
>>>
>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.
7.2.1. 文件对象的方法
本节下文中的例子假定已创建 f
文件对象。
f.read(size)
可用于读取文件内容,它会读取一些数据,并返回字符串(文本模式),或字节串对象(在二进制模式下)。 size 是可选的数值参数。省略 size 或 size 为负数时,读取并返回整个文件的内容;文件大小是内存的两倍时,会出现问题。size 取其他值时,读取并返回最多 size 个字符(文本模式)或 size 个字节(二进制模式)。如已到达文件末尾,f.read()
返回空字符串(''
)。
>>>
>>> f.read()
'This is the entire file.\n'
>>> f.read()
''
f.readline()
从文件中读取单行数据;字符串末尾保留换行符(\n
),只有在文件不以换行符结尾时,文件的最后一行才会省略换行符。这种方式让返回值清晰明确;只要 f.readline()
返回空字符串,就表示已经到达了文件末尾,空行使用 '\n'
表示,该字符串只包含一个换行符。
>>>
>>> f.readline()
'This is the first line of the file.\n'
>>> f.readline()
'Second line of the file\n'
>>> f.readline()
''
从文件中读取多行时,可以用循环遍历整个文件对象。这种操作能高效利用内存,快速,且代码简单:
>>>
>>> for line in f:
... print(line, end='')
...
This is the first line of the file.
Second line of the file
如需以列表形式读取文件中的所有行,可以用 list(f)
或 f.readlines()
。
f.write(string)
把 string 的内容写入文件,并返回写入的字符数。
>>>
>>> f.write('This is a test\n')
15
写入其他类型的对象前,要先把它们转化为字符串(文本模式)或字节对象(二进制模式):
>>>
>>> value = ('the answer', 42)
>>> s = str(value) # convert the tuple to string
>>> f.write(s)
18
f.tell()
返回整数,给出文件对象在文件中的当前位置,表示为二进制模式下时从文件开始的字节数,以及文本模式下的意义不明的数字。
f.seek(offset, whence)
可以改变文件对象的位置。通过向参考点添加 offset 计算位置;参考点由 whence 参数指定。 whence 值为 0 时,表示从文件开头计算,1 表示使用当前文件位置,2 表示使用文件末尾作为参考点。省略 whence 时,其默认值为 0,即使用文件开头作为参考点。
>>>
>>> f = open('workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5) # Go to the 6th byte in the file
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2) # Go to the 3rd byte before the end
13
>>> f.read(1)
b'd'
在文本文件(模式字符串未使用 b
时打开的文件)中,只允许相对于文件开头搜索(使用 seek(0, 2)
搜索到文件末尾是个例外),唯一有效的 offset 值是能从 f.tell()
中返回的,或 0。其他 offset 值都会产生未定义的行为。
文件对象还有一些额外的方法,如使用频率较低的 isatty() 和 truncate() 等;有关文件对象的完整指南请查阅标准库参考。
7.2.2. 使用 json 保存结构化数据
字符串可以很容易地写入文件或从文件中读取。 数字则更麻烦一些,因为 read() 方法只返回字符串,而字符串必须传给 int() 这样的函数,它接受 '123'
这样的字符串并返回其数值 123。 当你想要保存嵌套列表和字典等更复杂的数据类型时,手动执行解析和序列化操作将会变得非常复杂。
Python 允许你使用流行的数据交换格式 JSON (JavaScript Object Notation),而不是让用户持续编写和调试代码来将复杂的数据类型存入文件中。 标准库模块 json 可以接受带有层级结构的 Python 数据,并将其转换为字符串表示形式;这个过程称为 serializing 。 根据字符串表示形式重建数据则称为 deserializing。 在序列化和反序列化之间,用于代表对象的字符串可以存储在文件或数据库中,或者通过网络连接发送到远端主机。
备注
JSON 格式通常用于现代应用程序的数据交换。程序员早已对它耳熟能详,可谓是交互操作的不二之选。
只需一行简单的代码即可查看某个对象的 JSON 字符串表现形式:
>>>
>>> import json
>>> x = [1, 'simple', 'list']
>>> json.dumps(x)
'[1, "simple", "list"]'
dumps() 函数还有一个变体, dump() ,它只将对象序列化为 text file 。因此,如果 f
是 text file 对象,可以这样做:
json.dump(x, f)
要再次解码对象,如果 f
是已打开、供读取的 binary file 或 text file 对象:
x = json.load(f)
备注
JSON文件必须以UTF-8编码。当打开JSON文件作为一个 text file 用于读写时,使用 encoding="utf-8"
。
这种简单的序列化技术可以处理列表和字典,但在 JSON 中序列化任意类的实例,则需要付出额外努力。json 模块的参考包含对此的解释。