文件最重要的功能是提供和接收数据。
如果有一个名为 f
的类似于文件的对象,
可使用 f.write
来写入数据,还可使用 f.read
来读取数据。
与Python的其他大多数功能一样,在哪些东西可用作数据方面,也存在一定的灵活性,
但在文本和二进制模式下,基本上分别将 str
和 bytes
类用作数据。
在熟悉了处理文件夹和相对路径后,就可以指定文件的位置,
进行读写。接下来几节介绍的函数适用于纯文本文件。
“纯文本文件”只包含基本文本字符,不包含字体、
大小和颜色信息。带有 .txt
扩展名的文本文件,
以及带有 .py
扩展名的 Python 脚本文件,
都是纯文本文件的例子。
它们可以被 Windows 的 Notepad
或 OS X 的 TextEdit
应用打开。程序可以轻易地读取纯文本文件的内容,
将它们作为普通的字符串值。
r
以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。rb
以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。r+
打开一个文件用于读写。文件指针将会放在文件的开头。rb+
以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。w
打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。wb
以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。w+
打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。wb+
以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。a
打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。换言之,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。ab
以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。换言之,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。a+
打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。ab+
以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
with open('xx_hello.txt', 'w') as fo:
fo.write("Hello.")
开始:
helloFile = open('xx_hello.txt')
以上代码以相对路径方式打开当前路径下的文件。可以通过绝对路径定位系统中的文件。
在Python中打开文件时,读模式是默认的模式。
但如果不希望依赖于Python的默认值,也可以明确指明该模式,向 open()
传入字符串 'r'
,作为第二个参数。
所以 open('/Users/asweigart/hello.txt','r')
和 open('/Users/asweigart/hello.txt')
做的事情一样。
调用 open()
将返回一个File
对象。
File
对象代表计算机中的一个文件,
它只是Python中另一种类型的值,
就像已熟悉的列表和字典。
在前面的例子中,将 File
对象保存在helloFile
变量中。
现在,当需要读取或写入该文件,就可以调用 helloFile
变量中的 File
对象的方法。
读取文件内容
既然有了一个 File
对象,就可以开始从它读取内容。
如果希望将整个文件的内容读取为一个字符串值,
就使用 File
对象的 read()
方法。
继续使用保存在 helloFile
中的
hello.txt File
对象,在交互式环境中输入以下代码:
helloContent = helloFile.read()
helloContent
'Hello.'
如果将文件的内容看成是单个大字符串,read()
方法就返回保存在该文件中的这个字符串。
或者,可以使用readlines()
方法,从该文件取得一个字符串的列表。
列表中的每个字符串就是文本中的每一行。
import pprint
pprint.pprint(open(r'xx_somefile.txt').readlines())
['First line\n', 'Second line\n', 'Third and final line\n']
请注意,这里利用了文件对象将被自动关闭这一事实。
写入文件
Python 允许将内容写入文件,
方式与 print()
函数将字符串“写”到屏幕上类似。
但是,如果打开文件时用读模式,就不能写入文件。
需要以“写入纯文本模式”或“添加纯文本模式”
打开该文件,或简称为“写模式”和“添加模式”。
写模式将覆写原有的文件,从头开始,就像用一个新值覆写一个变量的值。
将 'w'
作为第二个参数传递给 open()
,以写模式打开该文件。
不同的是,添加模式将在已有文件的末尾添加文本。
可以认为这类似向一个变量中的列表添加内容,
而不是完全覆写该变量。将 'a'
作为第二个参数传递给 open()
,以添加模式打开该文件。
如果传递给 open()
的文件名不存在,
写模式和添加模式都会创建一个新的空文件。
在读取或写入文件后,
调用 close()
方法,然后才能再次打开该文件。
整合这些概念,在交互式环境中输入以下代码:
baconFile = open('xx_bacon.txt','w')
baconFile.write('Hello world!\n')
13
baconFile.close()
baconFile = open('xx_bacon.txt', 'a')
baconFile.write('Bacon is not a vegetable.')
25
baconFile.close()
baconFile = open('xx_bacon.txt')
content = baconFile.read()
baconFile.close()
print(content)
Hello world! Bacon is not a vegetable.
首先,以写模式打开 bacon.txt
。
因为还没有 bacon.txt
,Python 就创建了一个。
在打开的文件上调用 write()
,
并向 write()
传入字符串参数 'Hello world! \n'
,
将字符串写入文件,并返回写入的字符个数,
包括换行符。然后关闭该文件。
为了将文本添加到文件已有的内容,
而不是取代刚刚写入的字符串,
就以添加模式打开该文件。
向该文件写入'Bacon is not a vegetable.'
,
并关闭它。最后,为了将文件的内容打印到屏幕上,
以默认的读模式打开该文件,调用 read()
,
将得到的内容保存在 content
中,
关闭该文件,并打印 content
。
请注意, write()
方法不会像 print()
函数那样,
在字符串的末尾自动添加换行字符。必须自己添加该字符。
在 Python 中,读写文件有3个步骤:
- 调用
open()
函数,返回一个File
对象。 - 调用
File
对象的read()
或write()
方法。 - 别忘了调用方法
close
将文件关闭。
通常,程序退出时将自动关闭文件对象(也可能在退出程序前这样做), 因此是否将读取的文件关闭并不那么重要。 然而,关闭文件没有坏处,在有些操作系统和设置中,还可避免无意义地锁定文件以防修改。 另外,这样做还可避免用完系统可能指定的文件打开配额。
对于写入过的文件,一定要将其关闭,因为Python可能缓冲写入的数据(将数据暂时存储在某个地方,以提高效率)。
因此如果程序因某种原因崩溃,数据可能根本不会写入到文件中。
安全的做法是,使用完文件后就将其关闭。
如果要重置缓冲,让所做的修改反映到磁盘文件中, 但又不想关闭文件,可使用方法 flush
。
然而,需要注意的是,根据使用的操作系统和设置,flush
可能出于锁定考虑而禁止其他正在运行的程序访问这个文件。
只要能够方便地关闭文件,就应将其关闭。
要确保文件得以关闭,可使用一条 try/finally
语句,并在 finally
子句中调用 close
。
# 在这里打开文件
try:
# 将数据写入到文件中
finally:
file.close()
实际上,有一条专门为此设计的语句,那就是 with
语句。
with open("somefile.txt") as somefile:
do_something(somefile)
with
语句能够打开文件并将其赋给一个变量(这里是 somefile
)。
在语句体中,将数据 写入文件(还可能做其他事情)。
到达该语句末尾时,将自动关闭文件,即便出现异常亦如此。
with
语句实际上是一个非常通用的结构,允许使用所谓的上下文管理器。
上下文管理器是支持两个方法的对象: enter
和 exit
。
方法 enter
不接受任何参数,在进入 with
语句时被调用,其返回值被赋给关键字 as
后面的变量。
方法 exit
接受三个参数:异常类型、异常对象和异常跟踪。
它在离开方法时被调用。如果 exit
返回 False
,将抑制所有的异常。
文件也可用作上下文管理器。它们的方法 enter
返回文件对象本身,而方法 exit
关闭文件。
下面来尝试写入,首先是 write(string)
。
f = open(r'xx_somefile.txt', 'w')
f.write('this\nis no\nhaiku')
f.close()
!more somefile.txt
more: cannot open somefile.txt: No such file or directory
最后是 writelines(list)
:
f = open(r'xx_somefile.txt')
lines = f.readlines()
f.close()
lines[1] = "isn't a\n"
f = open(r'xx_somefile.txt', 'w')
f.writelines(lines)
f.close()
查看这个文件:
!more somefile.txt
more: cannot open somefile.txt: No such file or directory
又例如,在 hello.txt
文件相同的目录下,
创建一个名为 sonnet29.txt
的文件,
并在其中写入以下文本:
When, in disgrace with fortune and men's eyes ,
I all alone beweep my outcast state,
And trouble deaf heaven with my bootless cries,
And look upon myself and curse my fate,
确保用换行分开这4行。 然后在交互式环境中输入以下代码:
sonnetFile = open('xx_sonnet29.txt')
sonnetFile.readlines()
["When, in disgrace with fortune and men's eyes ,\n", 'I all alone beweep my outcast state,\n', 'And trouble deaf heaven with my bootless cries, \n', 'And look upon myself and curse my fate,']
请注意,每个字符串值都以一个换行字符 \n
结束。
除了文件的最后一行。与单个大字符串相比,
字符串的列表通常更容易处理。
在 Bash
等 Shell
中,可依次输入多个命令,并使用管道将它们链接起来,如下所示:
$ cat somefile.txt | python somescript.py | sort
这条管道线包含三个命令:
- cat
somefile.txt
:将文件somefile.txt
的内容写入到标准输出(sys.stdout
)。 - python
somescript.py
:执行Python脚本somescript
。这个脚本从其标准输入中读取,并将结果写入到标准输出。 - sort:读取标准输入(
sys.stdin
)中的所有文本,将各行按字母顺序排序,并将结果写入到标准输出。
但这些管道字符( |
)有何作用呢?
脚本 somescript.py
的作用是什么呢?
管道将一个命令的标准输出链接到下一个命令的标准输入。
很聪明吧? 因此可以认为, somescript.py
从其 sys.stdin
中读取数据(这些数据是 somefile.txt
写入的),
并将结果写入到其 sys.stdout
( sort
将从这里获取数据)。
代码示例1:计算 sys.stdin
中包含多少个单词的简单脚本使用 sys.stdin
的简单脚本( somescript.py
):
somescript.py
import sys
text = sys.stdin.read()
words = text.split()
wordcount = len(words)
print('Wordcount:', wordcount)
Wordcount: 0
代码示例2:一个内容荒谬的文本文件显示了文件 somefile.txt
的内容:
Your mother was a hamster and your
father smelled of elderberries.
cat somefile.txt | python somescript.py 的结果如下:
Wordcount: 11
在本章中,将文件都视为流,只能按顺序从头到尾读取。
实际上,可在文件中移动, 只访问感兴趣的部分(称为随机存取)。
为此,可使用文件对象的两个方法:seek()
和 tell()
。
方法 seek(offset[, whence])
将当前位置(执行读取或写入的位置)移到 offset
和 whence
指定的地方。
参数 offset
指定了字节(字符)数,
而参数 whence
默认为 io.SEEK_SET(0)
,
这意味着偏移量是相对于文件开头的(偏移量不能为负数)。
参数 whence
还可设置为 io.SEEK_CUR(1)
或 io.SEEK_END(2)
,
其中前者表示相对于当前位置进行移动(偏移量可以为负),
而后者表示相对于文件末尾进行移动。请看下面的示例:
thefile = 'xx_somefile.txt'
f = open( thefile , 'w')
f.write('01234567890123456789')
f.seek(5)
5
f.write('Hello, World!')
13
f.close()
f = open( thefile )
f.read()
'01234Hello, World!89'
方法 tell()
返回当前位于文件的什么位置,如下例所示:
f = open( thefile )
f.read(3)
f.read(2)
'34'
f.tell()
5