在上一篇《手把手陪您学Python》31——文件的打开中,我们学习了文件打开的方法。今天我们继续学习文件的操作,会重点讲解几种文件读取的方法。
这里先多说一句。当我们以后学习了更高级的文件操作方法后,我们可能更多地会使用pandsa.read_csv这类读取文件的命令。但是,理解python处理文件的基础原理也是很重要的,也就是这篇文章要介绍的内容。
Python中有多种读取文件的方式,下面我们逐个介绍。
1、读取整个文件
在上一篇中我们看到,open()函数返回的只是一个迭代器,这时我们就需要read()方法来读取文件了,使用的方法就和我们之前使用的各种方法一样,就在对象后面加上“.”和方法名即可,此时我们就可以读取整个文件了。
In [1]: path = 'lesson/text/contents.txt'
file_object = open(path, encoding = 'utf-8').read()
print(file_object)
Out[1]: 《手把手陪您学Python》1——为什么要学Python?
《手把手陪您学Python》2——Python的安装
《手把手陪您学Python》3——PyCharm的安装和配置
《手把手陪您学Python》4——Hello World!
《手把手陪您学Python》5——Jupyter Notebook
《手把手陪您学Python》6——字符串的标识
《手把手陪您学Python》7——字符串的索引
《手把手陪您学Python》8——字符串的切片
《手把手陪您学Python》9——字符串的运算
《手把手陪您学Python》10——字符串的函数
打开的目标文件经过read()方法读取,就可以打印出我们期待的效果了。
虽然这样的输出结果,看上去与原始文件没有什么区别,但实际上还是有点不同的,那就是输出的结果中,末尾多了一个空行。
为什么会多出这个空行呢?因为在我们的目标文件中,在最后一行文字后面有个换行,read()到达文件末尾时会自动返回一个空字符串,换行加上这个空字符串显示出来时就是一个空行。要删除这个多出来的空行,一种办法是在制作目标文件时,避免出现最后的换行,另一种可以使用我们之前学到的string.rstrip(str)方法——用来截取掉字符串右边(结尾)的指定字符(str默认为空格)。
用这种方法再将程序修改一下,就可以得到和目标文件内容完全一致的结果了。
In [2]: path = 'lesson/text/contents.txt'
file_object = open(path, encoding = 'utf-8').read()
print(file_object.rstrip())
Out[2]: 《手把手陪您学Python》1——为什么要学Python?
《手把手陪您学Python》2——Python的安装
《手把手陪您学Python》3——PyCharm的安装和配置
《手把手陪您学Python》4——Hello World!
《手把手陪您学Python》5——Jupyter Notebook
《手把手陪您学Python》6——字符串的标识
《手把手陪您学Python》7——字符串的索引
《手把手陪您学Python》8——字符串的切片
《手把手陪您学Python》9——字符串的运算
《手把手陪您学Python》10——字符串的函数
另外我们可以看一下,如果在读取中文字符而又没有使用encoding参数时会出现什么情况。
In [3]: path = 'lesson/text/contents.txt'
file_object = open(path).read()
print(file_object)
Out[3]: ---------------------------------------------------------------------------
UnicodeDecodeError Traceback (most recent call last)
<ipython-input-10-38447936f978> in <module>
1 path = 'lesson/text/contents.txt'
----> 2 file_object = open(path).read()
3 print(file_object)
UnicodeDecodeError: 'gbk' codec can't decode byte 0xaa in position 14: illegal multibyte sequence
可以看到,当目标文件中包含中文字符,但是又没有使用encoding参数时,会出现gbk字符无法解码的错误。
由于大部分情况下我们的文件中多少都会有一些中文字符,包括中文的标点符号,所以建议在使用open()函数时,都习惯性地加上“encoding = 'utf-8'”的参数,确保不会因为编码问题而出错。
如果不清楚文件的编码,可以导入sys模块,使用getdefaultencoding()方法来检查文件的默认编码,但因为超出我们现在所学的太多,只是顺便提一句。
2、使用迭代器逐行读取文件
由于打开的文件返回的是一个迭代器,所以除了读取整个文件外,还可以利用for循环语句,逐行读取文件,就像遍历列表一样简单,输出的结果就是目标文件中的每一行。
In [4]: path = 'lesson/text/contents.txt'
file_object = open(path, encoding = 'utf-8')
for line in file_object:
print(line)
Out[4]: 《手把手陪您学Python》1——为什么要学Python?
《手把手陪您学Python》2——Python的安装
《手把手陪您学Python》3——PyCharm的安装和配置
《手把手陪您学Python》4——Hello World!
《手把手陪您学Python》5——Jupyter Notebook
《手把手陪您学Python》6——字符串的标识
《手把手陪您学Python》7——字符串的索引
《手把手陪您学Python》8——字符串的切片
《手把手陪您学Python》9——字符串的运算
《手把手陪您学Python》10——字符串的函数
可以看到,逐行读取并打印时,输出的结果空行就更多了,所以也需要rstrip()一下。
In [5]: path = 'lesson/text/contents.txt'
file_object = open(path, encoding = 'utf-8')
for line in file_object:
print(line.rstrip())
Out[5]: 《手把手陪您学Python》1——为什么要学Python?
《手把手陪您学Python》2——Python的安装
《手把手陪您学Python》3——PyCharm的安装和配置
《手把手陪您学Python》4——Hello World!
《手把手陪您学Python》5——Jupyter Notebook
《手把手陪您学Python》6——字符串的标识
《手把手陪您学Python》7——字符串的索引
《手把手陪您学Python》8——字符串的切片
《手把手陪您学Python》9——字符串的运算
《手把手陪您学Python》10——字符串的函数
如果用我们之前学习过的列表推导式,上面的程序可以更为简单。
In [6]: path = 'lesson/text/contents.txt'
print([x for x in open(path, encoding = 'utf-8')])
Out[6]: ['《手把手陪您学Python》1——为什么要学Python?\n', '《手把手陪您学Python》2——Python的安装\n', '《手把手陪您学Python》3——PyCharm的安装和配置\n', '《手把手陪您学Python》4——Hello World!\n', '《手把手陪您学Python》5——Jupyter Notebook\n', '《手把手陪您学Python》6——字符串的标识\n', '《手把手陪您学Python》7——字符串的索引\n', '《手把手陪您学Python》8——字符串的切片\n', '《手把手陪您学Python》9——字符串的运算\n', '《手把手陪您学Python》10——字符串的函数\n']
列表推导式将目标文件中的每一行的内容以及换行符作为元素,组成了一个列表。所以每个元素后面都有一个换行的转义字符。之所以之前的实例结果中都没有包括转义字符,是因为使用了print()函数,如果之前的实例都不使用print()函数打印,也会出现换行的转义符,大家可以试一下。
如果要去掉这种转义符,也可以使用rstrip()方法。
In [7]: path = 'lesson/text/contents.txt'
print([x.rstrip() for x in open(path, encoding = 'utf-8')])
Out[7]: ['《手把手陪您学Python》1——为什么要学Python?', '《手把手陪您学Python》2——Python的安装', '《手把手陪您学Python》3——PyCharm的安装和配置', '《手把手陪您学Python》4——Hello World!', '《手把手陪您学Python》5——Jupyter Notebook', '《手把手陪您学Python》6——字符串的标识', '《手把手陪您学Python》7——字符串的索引', '《手把手陪您学Python》8——字符串的切片', '《手把手陪您学Python》9——字符串的运算', '《手把手陪您学Python》10——字符串的函数']
在对open()后的文件进行逐行读取时要注意,生成的迭代器只能使用一次。因为逐行读取的过程中,指针会从文件的开头移动到末尾,而不会自动再回到开头。所以用for循环逐行读取的话,用且只能用一次。
3、使用readlines()方法逐行读取
除了用for循环实现逐行的读取外,还可以使用readlines()方法,此时返回的就是由各行内容组成的列表,比使用for循环生成列表的方式更为简单。
In [8]: path = 'lesson/text/contents.txt'
file_object = open(path, encoding = 'utf-8').readlines()
print(file_object)
Out[8]: ['《手把手陪您学Python》1——为什么要学Python?\n', '《手把手陪您学Python》2——Python的安装\n', '《手把手陪您学Python》3——PyCharm的安装和配置\n', '《手把手陪您学Python》4——Hello World!\n', '《手把手陪您学Python》5——Jupyter Notebook\n', '《手把手陪您学Python》6——字符串的标识\n', '《手把手陪您学Python》7——字符串的索引\n', '《手把手陪您学Python》8——字符串的切片\n', '《手把手陪您学Python》9——字符串的运算\n', '《手把手陪您学Python》10——字符串的函数\n']
使用readlines()方法时,也同样是通过指针的移动进行读取的,所以和上面的for循环一样,readlines()的方法也只能使用一次。
4、读取指定个字符
使用read()方法除了可以读取整个文件,当输入参数时,还可以读取从头开始的指定个数的字符,字符的个数就是参数。
In [9]: path = 'lesson/text/contents.txt'
file_object = open(path, encoding = 'utf-8').read(100) # 读取100个字符
print(file_object)
Out[9]: 《手把手陪您学Python》1——为什么要学Python?
《手把手陪您学Python》2——Python的安装
《手把手陪您学Python》3——PyCharm的安装和配置
《手把手陪您学Pytho
5、返回指针位置
刚才我们说了很多次指针的移动,文件的读取过程实际上就是通过指针或者叫做句柄的推进来实现的。
read()方法默认是从文件的第1个字符开始的(指针也在第一个字符的位置),结束时指针会自动移动到下一个字符的位置,并准备下一次的读取指令。
通过tell()方法,可以返回指针当前的位置。
In [10]: path = 'lesson/text/contents.txt'
file_object = open(path, encoding = 'utf-8')
print(file_object.read(8))
file_object.tell()
Out[10]: 《手把手陪您学P
22
在UTF-8格式中,每个中文字符和中文标点符号占用3字节的位置。所以在上面的例子中,当read读8个字符时,其中包括7个中文字符(含中文符号)占用21字节,和1个英文占用1字节,所以最后指针的位置在22字节处。
利用指针,我们就可以验证上面所说的for循环只能用一次的原理了。
In [11]: path = 'lesson/text/contents.txt'
file_object = open(path, encoding = 'utf-8')
for line in file_object:
print(line.rstrip())
file_object.tell()
Out[11]: 《手把手陪您学Python》1——为什么要学Python?
《手把手陪您学Python》2——Python的安装
《手把手陪您学Python》3——PyCharm的安装和配置
《手把手陪您学Python》4——Hello World!
《手把手陪您学Python》5——Jupyter Notebook
《手把手陪您学Python》6——字符串的标识
《手把手陪您学Python》7——字符串的索引
《手把手陪您学Python》8——字符串的切片
《手把手陪您学Python》9——字符串的运算
《手把手陪您学Python》10——字符串的函数
575
经过一次遍历后,指针如同上面所说的,移动到了文章的末尾,此时在575字节的位置,计算的方法和上面一样,所以就不能再使用for循环遍历,或者使用readlines()的方法了。
6、移动指针到指定位置
如果要自行控制指针的位置,可以使用seek()方法,将指针移动到指定位置,并配合read()方法读取文件的剩余部分或者指定个数的字符。
In [12]: path = 'lesson/text/contents.txt'
file_object = open(path, encoding = 'utf-8')
file = file_object.seek(22)
file_object.tell()
print(file_object.read())
Out[12]: ython》1——为什么要学Python?
《手把手陪您学Python》2——Python的安装
《手把手陪您学Python》3——PyCharm的安装和配置
《手把手陪您学Python》4——Hello World!
《手把手陪您学Python》5——Jupyter Notebook
《手把手陪您学Python》6——字符串的标识
《手把手陪您学Python》7——字符串的索引
《手把手陪您学Python》8——字符串的切片
《手把手陪您学Python》9——字符串的运算
《手把手陪您学Python》10——字符串的函数
在上面的实例中,先使用seek()方法将指针移动到22字节的位置(和之前的实例位置一样),之后用tell()方法进行了验证,最后使用read()方法读取指针位置后的剩余内容。
使用seek()移动指针时要注意,涉及到中文字符时,是不能把中文字符的字节拆开的。比如,目标文件中的第一个字符是中文的“《”,占用3字节的位置,如果只是seek()1或者2是没有问题的,但如果要将指针移动到1字节或者2字节的位置以后再打印,就会报错了。
In [13]: path = 'lesson/text/contents.txt'
file_object = open(path, encoding = 'utf-8')
file = file_object.seek(2)
print(file_object.read())
Out[13]: ---------------------------------------------------------------------------
UnicodeDecodeError Traceback (most recent call last)
<ipython-input-76-27e34aade1ea> in <module>
2 file_object = open(path, encoding = 'utf-8')
3 file = file_object.seek(2)
----> 4 print(file_object.read())
~\anaconda3\lib\codecs.py in decode(self, input, final)
320 # decode input (taking the buffer into account)
321 data = self.buffer + input
--> 322 (result, consumed) = self._buffer_decode(data, self.errors, final)
323 # keep undecoded input until the next call
324 self.buffer = data[consumed:]
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8a in position 0: invalid start byte
报错的内容就是当指针在中文字符所占用连续字节的位置的中间时,是无法解码的。
同样,利用seek(),可以在for循环遍历文件后,通过将指针移动到初始位置,就可以进行再一次的遍历了。
In [14]: path = 'lesson/text/contents.txt'
file_object = open(path, encoding = 'utf-8')
for line in file_object:
print(line.rstrip())
file_object.seek(0)
for line in file_object:
print(line.rstrip())
Out[14]: 《手把手陪您学Python》1——为什么要学Python?
《手把手陪您学Python》2——Python的安装
《手把手陪您学Python》3——PyCharm的安装和配置
《手把手陪您学Python》4——Hello World!
《手把手陪您学Python》5——Jupyter Notebook
《手把手陪您学Python》6——字符串的标识
《手把手陪您学Python》7——字符串的索引
《手把手陪您学Python》8——字符串的切片
《手把手陪您学Python》9——字符串的运算
《手把手陪您学Python》10——字符串的函数
《手把手陪您学Python》1——为什么要学Python?
《手把手陪您学Python》2——Python的安装
《手把手陪您学Python》3——PyCharm的安装和配置
《手把手陪您学Python》4——Hello World!
《手把手陪您学Python》5——Jupyter Notebook
《手把手陪您学Python》6——字符串的标识
《手把手陪您学Python》7——字符串的索引
《手把手陪您学Python》8——字符串的切片
《手把手陪您学Python》9——字符串的运算
《手把手陪您学Python》10——字符串的函数
以上就是Python读取文件的几种基础方法,希望大家能够好好理解,会有助于我们后面学习更高级的文件读取的操作的。
下一篇,我们将要介绍关闭文件以及写入文件的方法,敬请关注。
感谢阅读本文!如有任何问题,欢迎留言,一起交流讨论^_^
要阅读《手把手陪您学Python》系列文章的其他篇目,请关注公众号点击菜单选择,或点击下方链接直达。
《手把手陪您学Python》3——PyCharm的安装和配置
《手把手陪您学Python》5——Jupyter Notebook
For Fans:关注“亦说Python”公众号,回复“手32”,即可免费下载本篇文章所用示例语句。
