### 6.4.2 文件操作
常用计算机的人都知道,许多应用软件(如 Word、媒体播放器等)都需要处理文件, 并且都需要经过打开文件、读写文件、关闭文件的步骤,这其实是程序设计中文件处理的一 般过程的反映。
打开文件
在读写文件之前首先需要“打开”文件,这个步骤可以简单地理解为对磁盘文件进行必 要的初始化,至于其底层细节则无需了解。
Python 提供了函数 open 用于文件打开,用法如下:
```
f = open(<文件名>,<打开方式>)
```
其含义是按指定的<打开方式>打开由<文件名>标识的磁盘文件,创建一个文件对象作为函 数的返回值,并使变量 f 引用这个文件对象。常用的打开方式包括"r"和"w",它们分别表示 “读”方式和“写”方式。
顺便强调一下,Python 中的文件处理是面向对象风格的,即文件是一个对象,通过文 件对象的方法来实现文件操作。我们在第 5 章中初步介绍了对象概念,并且将在第 7 章详细 讨论面向对象。
为了读取一个文件的内容,需要以读方式打开文件。例如:
```
f = open("oldfile.dat","r")
```
成功执行后,就可以通过文件对象 f 来读取文件 oldfile.dat 的内容了。若指定的文件不存在, 则 Python 将报错(IOError)。
为了向一个文件中写入内容,需要以写方式打开文件。例如:
```
f = open("newfile.txt","w")
```
成功执行后,就可以通过文件对象 f 来向文件 oldfile.dat 中写入内容了。注意,以写方式打 开文件时,如果指定的文件不存在,则创建该文件;如果指定的文件已经存在,则会清除该 文件原来的内容,即相当于创建新文件。所以,以写方式打开文件时一定要小心,不要把现 有文件破坏了。
读文件
在介绍文件读写之前,先要理解文件“当前读写位置”的概念。读者应该了解老式的录 放机的录放过程吧:录放机有一个磁头,用于读取或录入磁带信息,随着磁带的转动,磁头 也就不断改变着录放位置。Python 中的文件采用类似的顺序读写过程:打开文件后,当前 读写位置就是文件开始处;随着读写命令的执行,当前读写位置不断改变,直至到达文件末 尾。
Python 中的文件对象提供了 read()、readline()和 readlines()方法用于读取文件内容。 read()的用法如下:
```
<变量> = <文件对象>.read()
```
含义是读取从当前位置直到文件末尾的内容,并作为字符串返回。如果是刚打开的文件对象, 则返回的字符串包含文件的所有内容。
read()方法也可以带有参数:
```
<变量> = <文件对象>.read(n)
```
含义是读取从当前位置开始的 n 个字符,并以此字符串作为返回值。如果指定的 n 大于文件中从当前位置到末尾的字符数,则仅返回这些字符。如果当前位置已到达文件末尾,则 read 返回空串。
假设有一个文件 rhyme.txt,其文本内容是:
```
Good, better, best, Never let it rest, Till good is better, And better, best.
```
下面的语句序列对此文件进行读取
```
>>> f = open("rhyme.txt","r")
>>> s = f.read(8)
>>> s
'Good, be'
>>> f.read(20)
'tter, best,\nNever le'
>>> print f.read()
t it rest,
Till good is better, And better, best.
>>> f.close()
```
readline()的用法如下:
```
<变量> = <文件对象>.readline()
```
含义是读取从当前位置到行末(即下一个换行字符)的所有字符,并以此字符串作为返回值, 赋值给变量。通常用此方法来读取文件的当前行。如果当前处于文件末尾,则 readline 返回 空串。例如:
```
>>> f = open("rhyme.txt","r")
>>> s = f.readline()
>>> s
'Good, better, best,\n'
>>> f.readline()
'Never let it rest,\n'
>>> print f.readline()
Till good is better,
>>> f.close()
```
readlines()的用法如下:
```
<变量> = <文件对象>.readlines()
```
其含义是读取从当前位置直到文件末尾的所有行,并将这些行构成一个字符串列表作为返回 值,列表中的每个元素都是文件的一行。如果当前处于文件末尾,则 readlines 返回空列表。 例如:
```
>>> f = open("rhyme.txt","r")
>>> f.readline()
'Good, better, best,\n'
>>> f.readline()
'Never let it rest,\n'
>>> f.readlines()
['Till good is better,\n', 'And better, best.\n']
>>> f.readlines()
[]
```
写文件
当文件以写方式打开时,可以向文件中写入文本内容。与读文件一样,写入位置也是由 “当前读写位置”决定的。Python 文件对象提供两种写文件的方法:
```
<文件对象>.write(<字符串>)
<文件对象>.writelines(<字符串列表>)
```
其中,write 的含义是在文件当前位置处写入字符串,writelines 的含义是在文件当前位置处依次写入列表中的所有字符串。 下面的语句序列创建了一个新文件,并向其中写入了李白的名诗:
```
>>> f = open("d:/libai.txt","w")
>>> f.write("窗前明月光")
>>> f.write("疑是地上霜\n")
>>> f.write("举头望明月\n 低头思故乡")
>>> f.close()
```
注意每一次 f.write()都是紧接着上次写入的内容继续的,并不会因为是另一条 f.write()就另 起一行。为了写多行文本,必须人工添加换行字符“\n”。那么,上述语句序列所创建的文 件 libai.txt 有几行文本呢?没错,只有 3 行,因为第一次调用 f.write 时并没有写入换行符, 这导致诗的前两句被写在同一行上了。如图 6.2 所示。
![](https://box.kancloud.cn/2016-02-22_56cafce274d38.png)
图 6.2 写入多行文本
再次强调,写方式打开文件会导致要么创建一个新文件,要么清除一个旧文件,总之文件的内容是全新的。那么有没有办法在现有文件内容基础上再写入一些新内容呢?答案是肯 定的。Python 还提供一种文件打开方式"a",表示“追加”。以追加方式打开文件后,当前位 置被定位在文件末尾,可以继续写入文本而不改变原有的文件内容。例如:
```
>>> f = open("d:/libai.txt","a")
>>> f.write("\n---- 李白《静夜思》")
>>> f.close()
```
结果如图 6.3 所示。
![](https://box.kancloud.cn/2016-02-22_56cafce28978b.png)
图 6.3 向文件追加写入内容
关闭文件
文件处理结束后需要关闭文件,这个步骤大体上涉及释放分配给文件的系统资源,以便 分配给其他文件使用。通过调用文件对象的 close 方法来关闭文件:
```
<文件对象>.close()
```
注意,即使程序中没有关闭文件,Python 程序结束时也会自动关闭所有打开的文件。
然而好的做法是由程序自己关闭文件,否则有可能因程序意外终止而导致文件数据丢失。例 如,以写方式打开文件时,如果向文件中写入了文本但还没有关闭文件,那么所写内容是不 会存盘的。这时再以读方式打开同一文件,read()命令返回的是空串。下面的语句序列演示 了这种情况。
```
>>> f = open("d:/test","w")
>>> f.write("some words")
>>> g = open("d:/test","r")
>>> g.read()
''
>>> f.close()
>>> g.seek(0)
>>> g.read()
'some words'
```
所以,强烈建议读者在程序中一旦结束对文件的读写,就立即关闭文件。
文件处理程序的常见结构
许多应用程序的算法结构都属于直接了当的 IPO(输入-处理-输出)模式,当输入输 出都是文件时,程序的结构大体如下:
```
infile = open("input.dat","r")
outfile = open("output.dat","w")
while True:
text = infile.readline()
if text == "":
break
do something with text ...
outfile.write(data)
infile.close()
outfile.close()
```
此代码的核心是一个 while 循环,循环的每一步利用 readline()读取输入文件的一行,然后对该行进行处理,并将处理结果写入输出文件。当某次循环读到空行(视为文件尾),则利用 break 跳出循环体,从而结束对文件的处理。
除了“while 循环+readline()”的结构,还可以利用“for 循环+readlines()”的结构。readlines() 一次性读出所有行,形成一个列表,然后针对这个列表进行循环。
```
for line in infile.readlines():
do something with line
...
```
实际上,Python 语言甚至允许直接将打开的文件与 for 循环结合使用,达到和“for 循 环+readlines()”同样的效果。代码如下:
```
infile = open("input.dat","r")
for line in infile:
do something with line
...
```
这种用法有个好处是无需考虑内存大小,而 readlines()要求内存足够大,以便容纳它返回的 列表。
向文件追加数据
前述读方式打开的文件只能读取不能写入,写方式打开的文件是新建文件(写打开现存文件的话将清除内容),只能写入不能读取。有没有办法保留现存文件的内容并加入新内容 呢?
一种做法是先将文件的现有数据利用 readlines()读出来存入一个列表,然后向该列表添 加数据,最后再把新列表写入文件。这种做法对小文件没有问题,但当文件大小为数百 MB 或若干 GB 时,为了保存所有行的列表需要消耗大量内存。
其实 Python 还提供了一种打开方式"a",称为“追加”方式,可以用于在现存文件的尾 部追加新数据。当然,如果请求打开的文件不存在,"a"方式就和"w"方式一样,创建一个新 文件。下面的语句演示了追加方式的用法:
```
>>> f = open("oldfile.txt","a")
>>> f.write("something new\n")
>>> f.close()
```
- 前言
- 第 1 章 计算与计算思维
- 1.1 什么是计算?
- 1.1.1 计算机与计算
- 1.1.2 计算机语言
- 1.1.3 算法
- 1.1.4 实现
- 1.2 什么是计算思维?
- 1.2.1 计算思维的基本原则
- 1.2.2 计算思维的具体例子
- 1.2.3 日常生活中的计算思维
- 1.2.4 计算思维对其他学科的影响
- 1.3 初识 Python
- 1.3.1 Python 简介
- 1.3.2 第一个程序
- 1.3.3 程序的执行方式
- 1.3.4 Python 语言的基本成分
- 1.4 程序排错
- 1.5 练习
- 第 2 章 用数据表示现实世界
- 2.1 数据和数据类型
- 2.1.1 数据是对现实的抽象
- 2.1.1 常量与变量
- 2.1.2 数据类型
- 2.1.3 Python 的动态类型*
- 2.2 数值类型
- 2.2.1 整数类型 int
- 2.2.2 长整数类型 long
- 2.2.3 浮点数类型 float
- 2.2.4 数学库模块 math
- 2.2.5 复数类型 complex*
- 2.3 字符串类型 str
- 2.3.1 字符串类型的字面值形式
- 2.3.2 字符串类型的操作
- 2.3.3 字符的机内表示
- 2.3.4 字符串类型与其他类型的转换
- 2.3.5 字符串库 string
- 2.4 布尔类型 bool
- 2.4.1 关系运算
- 2.4.2 逻辑运算
- 2.4.3 布尔代数运算定律*
- 2.4.4 Python 中真假的表示与计算*
- 2.5 列表和元组类型
- 2.5.1 列表类型 list
- 2.5.2 元组类型 tuple
- 2.6 数据的输入和输出
- 2.6.1 数据的输入
- 2.6.2 数据的输出
- 2.6.3 格式化输出
- 2.7 编程案例:查找问题
- 2.8 练习
- 第 3 章 数据处理的流程控制
- 3.1 顺序控制结构
- 3.2 分支控制结构
- 3.2.1 单分支结构
- 3.2.2 两路分支结构
- 3.2.3 多路分支结构
- 3.3 异常处理
- 3.3.1 传统的错误检测方法
- 3.3.2 传统错误检测方法的缺点
- 3.3.3 异常处理机制
- 3.4 循环控制结构
- 3.4.1 for 循环
- 3.4.2 while 循环
- 3.4.3 循环的非正常中断
- 3.4.4 嵌套循环
- 3.5 结构化程序设计
- 3.5.1 程序开发过程
- 3.5.2 结构化程序设计的基本内容
- 3.6 编程案例:如何求 n 个数据的最大值?
- 3.6.1 几种解题策略
- 3.6.2 经验总结
- 3.7 Python 布尔表达式用作控制结构*
- 3.8 练习
- 第 4 章 模块化编程
- 4.1 模块化编程基本概念
- 4.1.1 模块化设计概述
- 4.1.2 模块化编程
- 4.1.3 编程语言对模块化编程的支持
- 4.2 Python 语言中的函数
- 4.2.1 用函数减少重复代码 首先看一个简单的用字符画一棵树的程序:
- 4.2.2 用函数改善程序结构
- 4.2.3 用函数增强程序的通用性
- 4.2.4 小结:函数的定义与调用
- 4.2.5 变量的作用域
- 4.2.6 函数的返回值
- 4.3 自顶向下设计
- 4.3.1 顶层设计
- 4.3.2 第二层设计
- 4.3.3 第三层设计
- 4.3.4 第四层设计
- 4.3.5 自底向上实现与单元测试
- 4.3.6 开发过程小结
- 4.4 Python 模块*
- 4.4.1 模块的创建和使用
- 4.4.2 Python 程序架构
- 4.4.3 标准库模块
- 4.4.4 模块的有条件执行
- 4.5 练习
- 第 5 章 图形编程
- 5.1 概述
- 5.1.1 计算可视化
- 5.1.2 图形是复杂数据
- 5.1.3 用对象表示复杂数据
- 5.2 Tkinter 图形编程
- 5.2.1 导入模块及创建根窗口
- 5.2.2 创建画布
- 5.2.3 在画布上绘图
- 5.2.4 图形的事件处理
- 5.3 编程案例
- 5.3.1 统计图表
- 5.3.2 计算机动画
- 5.4 软件的层次化设计:一个案例
- 5.4.1 层次化体系结构
- 5.4.2 案例:图形库 graphics
- 5.4.3 graphics 与面向对象
- 5.5 练习
- 第 6 章 大量数据的表示和处理
- 6.1 概述
- 6.2 有序的数据集合体
- 6.2.1 字符串
- 6.2.2 列表
- 6.2.3 元组
- 6.3 无序的数据集合体
- 6.3.1 集合
- 6.3.2 字典
- 6.4 文件
- 6.4.1 文件的基本概念
- 6.4.2 文件操作
- 6.4.3 编程案例:文本文件分析
- 6.4.4 缓冲
- 6.4.5 二进制文件与随机存取*
- 6.5 几种高级数据结构*
- 6.5.1 链表
- 6.5.2 堆栈
- 6.5.3 队列
- 6.6 练习
- 第 7 章 面向对象思想与编程
- 7.1 数据与操作:两种观点
- 7.1.1 面向过程观点
- 7.1.2 面向对象观点
- 7.1.3 类是类型概念的发展
- 7.2 面向对象编程
- 7.2.1 类的定义
- 7.2.2 对象的创建
- 7.2.3 对象方法的调用
- 7.2.4 编程实例:模拟炮弹飞行
- 7.2.5 类与模块化
- 7.2.6 对象的集合体
- 7.3 超类与子类*
- 7.3.1 继承
- 7.3.2 覆写
- 7.3.3 多态性
- 7.4 面向对象设计*
- 7.5 练习
- 第 8 章 图形用户界面
- 8.1 图形用户界面概述
- 8.1.1 程序的用户界面
- 8.1.2 图形界面的组成
- 8.1.3 事件驱动
- 8.2 GUI 编程
- 8.2.1 UI 编程概述
- 8.2.2 初识 Tkinter
- 8.2.3 常见 GUI 构件的用法
- 8.2.4 布局
- 8.2.5 对话框*
- 8.3 Tkinter 事件驱动编程
- 8.3.1 事件和事件对象
- 8.3.2 事件处理
- 8.4 模型-视图设计方法
- 8.4.1 将 GUI 应用程序封装成对象
- 8.4.2 模型与视图
- 8.4.3 编程案例:汇率换算器
- 8.5 练习
- 第 9 章 模拟与并发
- 9.1 模拟
- 9.1.1 计算机建模
- 9.1.2 随机问题的建模与模拟
- 9.1.3 编程案例:乒乓球比赛模拟
- 9.2 原型法
- 9.3 并行计算*
- 9.3.1 串行、并发与并行
- 9.3.2 进程与线程
- 9.3.3 多线程编程的应用
- 9.3.4 Python 多线程编程
- 9.3.5 小结
- 9.4 练习
- 第 10 章 算法设计和分析
- 10.1 枚举法
- 10.2 递归
- 10.3 分治法
- 10.4 贪心法
- 10.5 算法分析
- 10.5.1 算法复杂度
- 10.5.2 算法分析实例
- 10.6 不可计算的问题
- 10.7 练习
- 第 11 章 计算+X
- 11.1 计算数学
- 11.2 生物信息学
- 11.3 计算物理学
- 11.4 计算化学
- 11.5 计算经济学
- 11.6 练习
- 附录
- 1 Python 异常处理参考
- 2 Tkinter 画布方法
- 3 Tkinter 编程参考
- 3.1 构件属性值的设置
- 3.2 构件的标准属性
- 3.3 各种构件的属性
- 3.4 对话框
- 3.5 事件
- 参考文献