第8章 永久存储
8.1 文件:因为懂你,所以永恒
视频讲解
大多数的程序都遵循着“输入→处理→输出”的模型,首先接收输入数据,然后按照要求进行处理,最后输出数据。到目前为止,我们已经很好地了解了如何处理数据,然后打印出需要的结果。不过你可能已经“胃口大开”,不再只满足于使用input接收用户输入,使用print输出处理结果了。你迫切想要关注到系统的方方面面,需要自己的代码可以自动分析系统的日志,需要分析的结果可以保存为一个新的日志,甚至需要与外面的世界进行交流。
相信大家都曾经有这样的经历:在编写代码正起劲儿的时候,系统突然蓝屏崩溃了,重启之后发现刚才写入的代码都不见了,这时候你就会吐槽这破系统怎么这么不稳定。
在编写代码的时候,操作系统为了更快地做出响应,把所有当前的数据都放在内存中,因为内存和CPU数据传输的速度要比在硬盘和CPU之间传输的速度快很多倍。但内存有一个天生的不足,就是一旦断电就“没戏”,所以小甲鱼在这里再一次呼吁广大未来即将成为伟大程序员的读者们:请养成一个优雅的习惯,随时使用Ctrl+S快捷键保存数据。
Windows以扩展名来指出文件是什么类型,所以相信很多习惯使用Windows的朋友很快就反应过来了,.exe是可执行文件格式,.txt是文本文件,.ppt是PowerPoint的专用格式等,所有这些都称为文件。
8.1.1 打开文件
在Python中,使用open()这个内置函数来打开文件并返回文件对象:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline= None, closefd=True, opener=None)
open()这个函数有很多参数,但作为初学者,只需要先关注第一个和第二个参数即可。第一个参数是传入的文件名,如果只有文件名,不带路径的话,那么Python会在当前文件夹中去找到该文件并打开;第二个参数指定文件打开模式,如表8-1所示。
表8-1 文件的打开模式
使用open()成功打开一个文件之后,它会返回一个文件对象,拿到这个文件对象,就可以对这个文件“为所欲为”:
>>> # 先在桌面创建一个record.txt的文本文件,内容随意 >>> f = open(r"C:\Users\goodb\Desktop\record.txt") >>>
没有消息就是好消息,说明文件被成功打开了。如果出错了也不要急,是不是直接将上面的路径给代入了呢?记得要替换为自己桌面的路径,Python才能成功找到文件。
8.1.2 文件对象的方法
打开文件并取得文件对象之后,就可以利用文件对象的一些方法对文件进行读取、修改等操作。表8-2列举了平时常用的一些文件对象方法。
表8-2 文件对象方法
8.1.3 文件的关闭
close()方法用于关闭文件。如果是讲C语言编程教学,小甲鱼一定会一万次地强调文件的关闭非常重要。而Python拥有垃圾收集机制,会在文件对象的引用计数降至零的时候自动关闭文件,所以在Python编程里,如果忘记关闭文件并不会造成内存泄漏那么危险的结果。
但并不是说就可以不要关闭文件,如果对文件进行了写入操作,那么应该在完成写入之后关闭文件。因为Python可能会缓存写入的数据,如果中途发生类似断电之类的事故,那些缓存的数据根本就不会写入到文件中。所以,为了安全起见,要养成使用完文件后立刻关闭的好习惯。
8.1.4 文件的读取和定位
文件的读取方法很多,可以使用文件对象的read()和readline()方法,也可以直接list(f)或者直接使用迭代来读取。read()是以字节为单位读取,如果不设置参数,那么会全部读取出来,文件指针指向文件末尾。tell()方法可以告诉你当前文件指针的位置:
刚才提到的文件指针是啥?可以认为它是一个“书签”,起到定位的作用。使用seek()方法可以调整文件指针的位置。seek(offset, from)方法有两个参数,表示从from(0代表文件起始位置,1代表当前位置,2代表文件末尾)偏移offset字节。因此将文件指针设置到文件起始位置,使用seek(0, 0)即可:
>>> f.tell() 284 >>> f.seek(0, 0) 0 >>> f.read(5) '小客服:小' >>> f.tell() 9
注意:
因为1个中文字符占用2字节的空间,所以4个中文加1个英文冒号刚好到位置9。
readline()方法用于在文件中读取一整行,就是从文件指针的位置向后读取,直到遇到换行符(\n)结束:
>>> f.readline() '甲鱼,有个好评很好笑哈。\n'
此前介绍过列表的强大,什么都可以往里放,这不,也可以把整个文件的内容放到列表中:
对于迭代读取文本文件中的每一行,有些读者可能会这么写:
这样写并没有错,但给人的感觉就像是你拿酒精灯去烧开水,水是烧得开,不过效率不是很高。因为文件对象自身是支持迭代的,所以没必要绕圈子,直接使用for语句把内容迭代读取出来即可:
8.1.5 文件的写入
如果需要写入文件,请确保之前的打开模式有'w'或'a',否则会出错:
然而一定要小心的是:使用'w'模式写入文件,此前的文件内容会被全部删除,如图8-1所示,小甲鱼和小客服的对话备份已经不在了。
图8-1 'w'打开模式会删除原来的文件内容
如果要在原来的内容上追加,一定要使用'a'模式打开文件。这是血淋淋的教训,不要问我为什么(想想都是泪啊)!
8.1.6 一个任务
视频讲解
本节要求读者独立来完成一个任务,将文件(record2.txt)中的数据进行分割并按照以下规则保存起来:
(1)将小甲鱼的对话单独保存为boy_*.txt文件(去掉“小甲鱼:”)。
(2)将小客服的对话单独保存为girl_*.txt文件(去掉“小客服:”)。
(3)文件中总共有三段对话,分别保存为boy_1.txt、girl_1.txt、boy_2.txt、girl_2.txt、boy_3.txt、girl_3.txt这6个文件(提示:文件中不同的对话间已经使用“=======================================”分割)。
大家一定要自己先动动手再参考答案哦。
事实上可以利用函数封装得更好看一些: