Python:如何读取和写入文件
原文: https://thepythonguru.com/python-how-to-read-and-write-files/
于 2020 年 1 月 7 日更新
在本文中,我们将学习如何在 Python 中读取和写入文件。
处理文件包括以下三个步骤:
- 打开文件
- 执行读或写操作
- 关闭文件
让我们详细了解每个步骤。
文件类型
有两种类型的文件:
- 文本文件
- 二进制文件
文本文件只是使用 utf-8,latin1 等编码存储字符序列的文件,而对于二进制文件,数据以与计算机内存相同的格式存储。
以下是一些文本和二进制文件示例:
文本文件:Python 源代码,HTML 文件,文本文件,降价文件等。
二进制文件:可执行文件,图像,音频等
重要的是要注意,在磁盘内部,两种类型的文件都以 1 和 0 的顺序存储。 唯一的区别是,当打开文本文件时,将使用与编码相同的编码方案对数据进行解码。但是,对于二进制文件,不会发生这种情况。
打开文件 - open()
函数
open()
内置函数用于打开文件。 其语法如下:
open(filename, mode) -> file object
成功时,open()
返回文件对象。 如果失败,它将引发IOError
或它的子类。
参数 | 描述 |
---|---|
filename |
要打开的文件的绝对或相对路径。 |
mode |
(可选)模式是一个字符串,表示处理模式(即读取,写入,附加等)和文件类型。 |
以下是mode
的可能值。
模式 | 描述 |
---|---|
r |
打开文件进行读取(默认)。 |
w |
打开文件进行写入。 |
a |
以附加模式打开文件,即在文件末尾添加新数据。 |
r+ |
打开文件以进行读写 |
x |
仅在尚不存在的情况下,打开文件进行写入。 |
我们还可以将t
或b
附加到模式字符串以指示将要使用的文件的类型。 t
用于文本文件,b
用于二进制文件。 如果未指定,则默认为t
。
mode
是可选的,如果未指定,则该文件将作为文本文件打开,仅供读取。
这意味着对open()
的以下三个调用是等效的:
# open file todo.md for reading in text mode
open('todo.md')
open('todo.md', 'r')
open('todo.md', 'rt')
请注意,在读取文件之前,该文件必须已经存在,否则open()
将引发FileNotFoundError
异常。 但是,如果打开文件进行写入(使用w
,a
或r+
之类的模式),Python 将自动为您创建文件。 如果文件已经存在,则其内容将被删除。 如果要防止这种情况,请以x
模式打开文件。
关闭文件 - close()
方法
处理完文件后,应将其关闭。 尽管程序结束时该文件会自动关闭,但是这样做仍然是一个好习惯。 无法在大型程序中关闭文件可能会出现问题,甚至可能导致程序崩溃。
要关闭文件,请调用文件对象的close()
方法。 关闭文件将释放与其相关的资源,并将缓冲区中的数据刷新到磁盘。
文件指针
通过open()
方法打开文件时。 操作系统将指向文件中字符的指针关联。 文件指针确定从何处进行读取和写入操作。 最初,文件指针指向文件的开头,并随着我们向文件读取和写入数据而前进。 在本文的后面,我们将看到如何确定文件指针的当前位置,并使用它来随机访问文件的各个部分。
使用read()
,readline()
和readlines()
读取文件
要读取数据,文件对象提供以下方法:
方法 | 参数 |
---|---|
read([n]) |
从文件读取并返回n 个字节或更少的字节(如果没有足够的字符读取)作为字符串。 如果未指定n ,它将以字符串形式读取整个文件并将其返回。 |
readline() |
读取并返回字符,直到以字符串形式到达行尾为止。 |
readlines() |
读取并返回所有行作为字符串列表。 |
当到达文件末尾(EOF)时,read()
和readline()
方法返回一个空字符串,而readlines()
返回一个空列表([]
)。
这里有些例子:
poem.txt
The caged bird sings
with a fearful trill
of things unknown
but longed for still
示例 1 :使用read()
>>>
>>> f = open("poem.txt", "r")
>>>
>>> f.read(3) # read the first 3 characters
'The'
>>>
>>> f.read() # read the remaining characters in the file.
' caged bird sings\nwith a fearful trill\nof things unknown\nbut longed for still\n'
>>>
>>> f.read() # End of the file (EOF) is reached
''
>>>
>>> f.close()
>>>
示例 2 :使用readline()
>>>
>>> f = open("poem.txt", "r")
>>>
>>> f.read(4) # read first 4 characters
'The '
>>>
>>> f.readline() # read until the end of the line is reached
'caged bird sings\n'
>>>
>>> f.readline() # read the second line
'with a fearful trill\n'
>>>
>>> f.readline() # read the third line
'of things unknown\n'
>>>
>>> f.readline() # read the fourth line
'but longed for still'
>>>
>>> f.readline() # EOF reached
''
>>>
>>> f.close()
>>>
示例 3 :使用readlines()
>>>
>>> f = open("poem.txt", "r")
>>>
>>> f.readlines()
['The caged bird sings\n', 'with a fearful trill\n', 'of things unknown\n', 'but longed for still\n']
>>>
>>> f.readlines() # EOF reached
[]
>>>
>>> f.close()
>>>
批量读取文件
read()
(不带参数)和readlines()
方法立即将所有数据读入内存。 因此,请勿使用它们读取大文件。
更好的方法是使用read()
批量读取文件,或使用readline()
逐行读取文件,如下所示:
示例:读取文件块
>>>
>>> f = open("poem.txt", "r")
>>>
>>> chunk = 200
>>>
>>> while True:
... data = f.read(chunk)
... if not data:
... break
... print(data)
...
The caged bird sings
with a fearful trill
of things unknown
but longed for still
>>>
示例:逐行读取文件
>>>
>>> f = open("poem.txt", "r")
>>>
>>> while True:
... line = f.readline()
... if not line:
... break
... print(line)
...
The caged bird sings
with a fearful trill
of things unknown
but longed for still
>>>
除了使用read()
(带有参数)或readline()
方法之外,您还可以使用文件对象一次遍历一行的文件内容。
>>>
>>> f = open("poem.txt", "r")
>>>
>>> for line in f:
... print(line, end="")
...
The caged bird sings
with a fearful trill
of things unknown
but longed for still
>>>
该代码与前面的示例等效,但是更加简洁,易读且易于键入。
警告:
提防readline()
方法,如果您在打开没有任何换行符的大文件时遇到不幸,那么readline()
并不比read()
好(无参数)。 当您使用文件对象作为迭代器时,也是如此。
使用write()
和writelines()
写入数据
为了写入数据,文件对象提供了以下两种方法:
方法 | 描述 |
---|---|
write(s) |
将字符串s 写入文件并返回写入的数字字符。 |
writelines(s) |
将序列s 中的所有字符串写入文件。 |
以下是示例:
>>>
>>> f = open("poem_2.txt", "w")
>>>
>>> f.write("When I think about myself, ")
26
>>> f.write("I almost laugh myself to death.")
31
>>> f.close() # close the file and flush the data in the buffer to the disk
>>>
>>>
>>> f = open("poem_2.txt", "r") # open the file for reading
>>>
>>> data = f.read() # read entire file
>>>
>>> data
'When I think about myself, I almost laugh myself to death.'
>>>
>>> print(data)
When I think about myself, I almost laugh myself to death.
>>>
>>> f.close()
>>>
请注意,与print()
函数不同,write()
方法不会在行尾添加换行符(\n
)。 如果需要换行符,则必须手动添加它,如下所示:
>>>
>>>
>>> f = open("poem_2.txt", "w")
>>>
>>> f.write("When I think about myself, \n") # notice newline
27
>>> f.write("I almost laugh myself to death.\n") # notice newline
32
>>>
>>> f.close()
>>>
>>>
>>> f = open("poem_2.txt", "r") # open the file again
>>>
>>> data = f.read() # read the entire file
>>>
>>> data
'When I think about myself, \nI almost laugh myself to death.\n'
>>>
>>> print(data)
When I think about myself,
I almost laugh myself to death.
>>>
>>>
您还可以使用print()
函数将换行符附加到该行,如下所示:
>>>
>>> f = open("poem_2.txt", "w")
>>>
>>> print("When I think about myself, ", file=f)
>>>
>>> print("I almost laugh myself to death.", file=f)
>>>
>>> f.close()
>>>
>>>
>>> f = open("poem_2.txt", "r") # open the file again
>>>
>>> data = f.read()
>>>
>>> data
'When I think about myself, \nI almost laugh myself to death.\n'
>>>
>>> print(data)
When I think about myself,
I almost laugh myself to death.
>>>
>>>
这是writelines()
方法的示例。
>>>
>>> lines = [
... "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod",
... "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,"
... "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo",
... "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse",
... "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non",
... "proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
... ]
>>>
>>>
>>> f = open("lorem.txt", "w")
>>>
>>> f.writelines(lines)
>>>
>>> f.close()
>>>
writelines()
方法在内部调用write()
方法。
def writelines(self, lines):
self._checkClosed()
for line in lines:
self.write(line)
这是另一个以附加模式打开文件的示例。
>>>
>>> f = open("poem_2.txt", "a")
>>>
>>> f.write("\nAlone, all alone. Nobody, but nobody. Can make it out here alone.")
65
>>> f.close()
>>>
>>> data = open("poem_2.txt").read()
>>> data
'When I think about myself, \nI almost laugh myself to death.\n\nAlone, all alone. Nobody, but nobody. Can make it out here alone.'
>>>
>>> print(data)
When I think about myself,
I almost laugh myself to death.
Alone, all alone. Nobody, but nobody. Can make it out here alone.
>>>
假设文件poem_2.txt
对于使用非常重要,并且我们不希望其被覆盖。 为了防止在x
模式下打开文件
>>>
>>> f = open("poem_2.txt", "x")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileExistsError: [Errno 17] File exists: 'poem.txt'
>>>
如果x
模式不存在,则仅打开该文件进行写入。
缓冲和刷新
缓冲是在将数据移到新位置之前临时存储数据的过程。
对于文件,数据不会立即写入磁盘,而是存储在缓冲存储器中。
这样做的基本原理是,将数据写入磁盘需要花费时间,而不是将数据写入物理内存。 想象一下,每当调用write()
方法时一个程序正在写入数据。 这样的程序将非常慢。
当我们使用缓冲区时,仅当缓冲区已满或调用close()
方法时,才将数据写入磁盘。 此过程称为刷新输出。 您也可以使用文件对象的flush()
方法手动刷新输出。 请注意,flush()
仅将缓冲的数据保存到磁盘。 它不会关闭文件。
open()
方法提供了一个可选的第三个参数来控制缓冲区。 要了解更多信息,请访问官方文档。
读写二进制数据
通过将b
附加到模式字符串来完成读写二进制文件。
在 Python 3 中,二进制数据使用称为bytes
的特殊类型表示。
bytes
类型表示介于 0 和 255 之间的数字的不可变序列。
让我们通过阅读poem.txt
文件来创建诗歌的二进制版本。
>>>
>>> binary_poem = bytes(open("poem.txt").read(), encoding="utf-8")
>>>
>>> binary_poem
b'The caged bird sings\nwith a fearful trill\nof things unknown\nbut longed for still'
>>>
>>>
>>> binary_poem[0] # ASCII value of character T
84
>>> binary_poem[1] # ASCII value of character h
104
>>>
请注意,索引bytes
对象将返回int
。
让我们将二进制诗写在一个新文件中。
>>>
>>> f = open("binary_poem", "wb")
>>>
>>> f.write(binary_poem)
80
>>>
>>> f.close()
>>>
现在,我们的二进制诗已写入文件。 要读取它,请以rb
模式打开文件。
>>>
>>> f = open("binary_poem", "rb")
>>>
>>> data = f.read()
>>>
>>> data
b'The caged bird sings\nwith a fearful trill\nof things unknown\nbut longed for still'
>>>
>>> print(data)
b'The caged bird sings\nwith a fearful trill\nof things unknown\nbut longed for still'
>>>
>>> f.close()
>>>
重要的是要注意,在我们的情况下,二进制数据碰巧包含可打印的字符,例如字母,换行符等。但是,在大多数情况下并非如此。 这意味着对于二进制数据,由于文件中可能没有换行符,因此我们无法可靠地使用readline()
和文件对象(作为迭代器)来读取文件的内容。 读取二进制数据的最佳方法是使用read()
方法分块读取它。
>>>
>>> # Just as with text files, you can read (or write) binary files in chunks.
>>>
>>> f = open("binary_poem", "rb")
>>>
>>> chunk = 200
>>>
>>> while True:
... data = f.read(chunk)
... if not data:
... break
... print(data)
...
b'The caged bird sings\nwith a fearful trill\nof things unknown\nbut longed for still'
>>>
>>>
使用fseek()
和ftell()
的随机访问
在本文的前面,我们了解到打开文件时,系统将一个指针与它相关联,该指针确定从哪个位置进行读取或写入。
到目前为止,我们已经线性地读写文件。 但是也可以在特定位置进行读写。 为此,文件对象提供了以下两种方法:
方法 | 描述 |
---|---|
tell() |
返回文件指针的当前位置。 |
seek(offset, [whence=0]) |
将文件指针移动到给定的offset 。 offset 引用字节计数,whence 确定offset 将相对于文件指针移动的位置。 whence 的默认值为 0,这意味着offset 将使文件指针从文件的开头移开。 如果wherece 设置为1 或2 ,则偏移量将分别将文件的指针从当前位置或文件的末尾移动。 |
现在让我们举一些例子。
>>>
>>> ###### binary poem at a glance #######
>>>
>>> for i in open("binary_poem", "rb"):
... print(i)
...
b'The caged bird sings\n'
b'with a fearful trill\n'
b'of things unknown\n'
b'but longed for still'
>>>
>>> f.close()
>>>
>>> #####################################
>>>
>>> f = open('binary_poem', 'rb') # open binary_poem file for reading
>>>
>>> f.tell() # initial position of the file pointer
0
>>>
>>> f.read(5) # read 5 bytes
b'The c'
>>>
>>> f.tell()
5
>>>
读取 5 个字符后,文件指针现在位于字符a
(用字caged
表示)。 因此,下一个读取(或写入)操作将从此处开始。
>>>
>>>
>>> f.read()
b'aged bird sings\nwith a fearful trill\nof things unknown\nbut longed for still'
>>>
>>> f.tell()
80
>>>
>>> f.read() # EOF reached
b''
>>>
>>> f.tell()
80
>>>
现在,我们已到达文件末尾。 此时,我们可以使用fseek()
方法将文件指针后退到文件的开头,如下所示:
>>>
>>> f.seek(0) # rewind the file pointer to the beginning, same as seek(0, 0)
0
>>>
>>> f.tell()
0
>>>
文件指针现在位于文件的开头。 从现在开始的所有读取和写入操作将从文件的开头再次进行。
>>>
>>> f.read(14) # read the first 14 characters
b'The caged bird'
>>>
>>>
>>> f.tell()
14
>>>
要将文件指针从当前位置从 12 个字节向前移动,请按以下步骤操作:seek()
:
>>>
>>> f.seek(12, 1)
26
>>>
>>> f.tell()
26
>>>
>>>
文件指针现在位于字符a
(在单词with
之后),因此将从此处进行读取和写入操作。
>>>
>>>
>>> f.read(15)
b'a fearful trill'
>>>
>>>
我们还可以向后移动文件指针。 例如,以下对seek()
的调用将文件指针从当前位置向后移 13 个字节。
>>>
>>> f.seek(-13, 1)
28
>>>
>>> f.tell()
28
>>>
>>> f.read(7)
b'fearful'
>>>
假设我们要读取文件的最后 16 个字节。 为此,将文件指针相对于文件末尾向后移 16 个字节。
>>>
>>> f.seek(-16, 2)
64
>>>
>>> f.read()
b'longed for still'
>>>
fseek()
的whence
自变量的值在os
模块中也定义为常量。
值 | 常量 |
---|---|
0 |
SEEK_SET |
1 |
SEEK_CUR |
2 |
SEEK_END |
with
语句
使用with
语句可使我们在完成处理后自动关闭文件。 其语法如下:
with expression as variable:
# do operations on file here.
必须像for
循环一样,将with
语句内的语句缩进相等,否则将引发SyntaxError
异常。
这是一个例子:
>>>
>>> with open('poem.txt') as f:
... print(f.read()) # read the entire file
...
The caged bird sings
with a fearful trill
of things unknown
but longed for still
>>>