文章目录
- 一. 什么是文件
- 二. 文件路径
- 三. 打开文件
- 四. 关闭文件
- 五. 写文件
- 1. 清空写
- 2. 追加写
- 六. 读文件
- 1. 使用 read 方法读取
- 2. 使用 for 循环按行读取
- 3. 使用 readlines 方法读取整个文件的内容
- 七. 上下文管理器
- 1. 什么是上下文管理器?
- 2. 为什么要有上下文管理器?
- 3. 怎么使用上下文管理器?
一. 什么是文件
数据因为表现形式的不同会被划分成各种各样的文件:
- 电影(.mp4)
- 歌曲(.mp3)
- 图片(.jpg)
- 文本(.txt)
- 表格(.xlsx)
我们知道冯诺依曼体系结构包括:输入输出设备、存储器、运算器和处理器。其中存储器的存储体系又分为内存和硬盘:
- 内存:变量就是存储在内存中
- 硬盘:文件就是存储在硬盘中
对比内存和硬盘:
- 内存的空间更小,硬盘的空间更大
- 内存访问更快,硬盘访问更慢
- 内存成本更高,硬盘成本更低
- 内存的数据易丢失,硬盘的数据能持久化存储
我们电脑中C盘、D盘的内容都是硬盘上的内容,也都是文件:
二. 文件路径
文件夹也叫目录,它也属于文件。我们把一层层目录构成的字符串称为“文件的路径”,比如在我的电脑中QQ可执行程序的路径就是:C:\Program Files (x86)\Tencent\QQ\Bin
文件路径可以视为是文件在硬盘上的身份标识,每个文件对应的路径都是唯一的。知道了文件路径,就可以知道这个文件在硬盘上的详细位置;也就可以进一步的知道这个文件里面都有些什么。
补充说明
目录名之间,使用反斜杠 \ 来分隔,其实使用斜杆 / 也可以。前者 Windows 用的更多,在其他系统中更多的是使用斜杆来分隔,因为反斜杠在字符串中有特殊的含义,它可以表示转意。
三. 打开文件
创建一个文档文件 test.txt 用来测试:
在 Python 中打开文件使用open
函数:
f = open('C:/Users/86158/Downloads/Python/test.txt', 'r')
print(f)
print(type(f))
-------运行结果-------
# 包含文件的路径、打开方式、编码方式这三个信息
<_io.TextIOWrapper name='C:/Users/86158/Downloads/Python/test.txt' mode='r' encoding='cp936'>
# Python内部给文件对象起的名字
<class '_io.TextIOWrapper'>
open 函数解析
补充说明
1)被打开的文件必须存在于给出的路径下,不然会抛异常
2)文件对象是个句柄
open 的返回值是一个文件对象,如何理解?
- 文件的内容,是存储在硬盘上的。
- 文件对象,是内存上的一个变量。后续对文件的读写,都是通过这个文件对象来进行操作的。
文件对象就像一个遥控器,而文件就好像一台电视机。我们要操作电视机(开关、调台、调音量),可以直接去按电视机上的按键,但是这样太麻烦了,我们通常是躺在沙发上通过遥控器来操作;类比文件,我们可直接去硬盘上去对文件进行读写等操作,同样为了方便我们更多的是通过文件对象去完成这些操作。
在计算机中,也把这样的远程操控的“遥控器”称为“句柄”。
四. 关闭文件
使用close
方法关闭已经打开的文件:
f.close()
文件在打开、使用完了之后一定要关闭!
打开文件,其实是在申请一定的系统资源,不再使用文件的时候,资源就该及时释放;否则就可能造成文件资源泄露,进一步导致其他部分的代码无法顺利打开文件。
正是因为一个系统的资源是有限的,因此一个程序能打开的文件个数也是有上限的,通过下面代码测试
# 1、创建一个列表,用来存储文件对象
fList = []
# 2、无限地打开文件
count = 0
while True:
f = open('C:/Users/86158/Downloads/Python/test.txt', 'r')
fList.append(f)
count = count + 1
print(f'打开的无间隔数:{count}')
-------运行结果-------
...
打开的无间隔数:8187
打开的无间隔数:8188
打开的无间隔数:8189
OSError: [Errno 24] Too many open files: 'C:/Users/86158/Downloads/Python/test.txt'
发现 PyCharm 最终允许打开文件个数上限是:8189
我们可以通过一些设置项,来配置能打开的最大文件数目。但不论配置多少个,都不是无穷无尽的,还是需要我们做到及时关闭,释放资源。
此外,Python 中还有一个重要的机制:垃圾回收机制(GC),会自动的吧不使用的变量给进行释放。
这次我们还是无限地去打开文件,但不再把文件对象保存到列表中了:
count = 0
while True:
f = open('C:/Users/86158/Downloads/Python/test.txt', 'r')
count = count + 1
print(f'打开的无间隔数:{count}')
这次运行后发现不会抛异常了,之前抛异常是因为我们把打开的文件对象都存储到了列表中,然后操作系统判定我们之后可能还会使用这些文件对象,所以没有把它们回收;而这次我们只是单纯地打开了文件,而没有对它们进行任何操作,循环结束后操作系统就会把前面打开的文件对象自动回收。
总结:虽然 Python 给了我们一个后手,让我们一定程度地避免内存泄漏问题。但是也不能完全依赖自动回收机制,因为它不能做到绝对及时地释放,因此还是要尽量手动释放,做到万无一失。
补充:为什么 Python 把文件打开数目的上限设置为 8189 ?
因为:8189 + 3 = 8192 => 2^13
在计算机中所有的数据都是用二进制数字来表示的,因此计算机里的很多数字都是按照 2 的 n 次方这样的方式表示的;即在程序员眼中,1000 不是一个很整齐的数字,1024 => 2^10 这才是一个比较整齐的数字。
回归正题,那么 8189 少了的 3 个文件是什么呢?其实在每个程序启动的时候,都会默认打开三个文件:
- 标准输入(stdin),代表键盘
- 标准输出(stdout),代表显示器
- 标准错误(stderr),代表显示器
少了的那三个文件就是它们,它三在程序启动之初就已经打开了。
五. 写文件
1. 清空写
使用write
方法可以对文件内容进行写入:
f = open('C:/Users/86158/Downloads/Python/test.txt', 'w')
f.write('hello')
f.close()
编译运行后,观察 test.txt 文件,成功写入字符串 “hello”:
注意如果要写文件,打开文件时需要使用 ‘w’ 的方式,如果是 ‘r’ 方式打开,则会抛出异常:
2. 追加写
写方式打开文件,其实有两种情况,直接写方式打开和追加写方式打开:
- ‘w’:对文件写入时,会清空原有文件的内容
- ‘a’:不会清空原有文件的内容,写的内容会追加到原有文件内容的末尾
# 用追加的方式对 test.txt 文件进行写入
f = open('C:/Users/86158/Downloads/Python/test.txt', 'a')
f.write('112233')
f.close()
观察 test.txt 文件的内容,发现新写入的内容追加到原内容的末尾:
这次用 ‘w’ 方式打开文件,然后进行写入:
f = open('C:/Users/86158/Downloads/Python/test.txt', 'w')
f.write('world')
f.close()
发现源文件内容被清空,然后在写入新内容:
最后还要注意一点:如果文件对象已经被关闭,那么意味着系统中和该文件相关的内存资源已经被释放了,这时强行去写,也会抛出异常:
f = open('C:/Users/86158/Downloads/Python/test.txt', 'w')
f.close()
# error:对已经关闭的文件进行写操作
f.write('hello')
抛出异常:
六. 读文件
1. 使用 read 方法读取
假设 test.txt 的文件内容如下:
接下来我们以 ‘r’ 方式打开文件,然后然后用 read 函数去读取文件内容;read 函数的参数是你要读取字符的个数,返回值是一个字符串:
f = open('C:/Users/86158/Downloads/Python/test.txt', 'r')
ret = f.read(2)
print(ret)
f.close()
编译后,抛出一个异常:
原因是文件内容对中文的编码方式是 utf8;而我们打开文件时,对文件的操作的编码方式默认是 gbk,二者不匹配,所以导致读取文件内容失败:
此处我们使用的办法,是让代码按照 utf8 的方式来进行处理。相比于 gbk,utf8 是使用更广泛的编码方式。
f = open('C:/Users/86158/Downloads/Python/test.txt', 'r', encoding='utf8')
ret = f.read(2)
print(ret)
f.close()
-------运行结果-------
床前
2. 使用 for 循环按行读取
使用 for 循环直接遍历文件对象,可以按行读到里面的内容:
f = open('C:/Users/86158/Downloads/Python/test.txt', 'r', encoding='utf8')
for line in f:
print(line)
f.close()
-------运行结果-------
床前看月光
疑是地上霜
举头望山月
低头思故乡
观察结果,发现每一句结束后,下面会多了一行,这是为什么呢?
之所以会多了一个空行,是因为本来读到的内容,每一行结束后末尾就带有一个 ‘\n’,此处再用 print 打印,又会自动加一个换行符。
我们可以给 print 再多设定个参数,修改 print 自动添加换行符的行为
重新编写代码,观察结果:
f = open('C:/Users/86158/Downloads/Python/test.txt', 'r', encoding='utf8')
for line in f:
print(line, end='')
f.close()
-------运行结果-------
床前看月光
疑是地上霜
举头望山月
低头思故乡
3. 使用 readlines 方法读取整个文件的内容
readlines 方法可以一次性读取整个文件的内容,不需要传参,它会按行读取文件的内容,然后把结果组织到一个列表当中作为返回值,返回给外界:
f = open('C:/Users/86158/Downloads/Python/test.txt', 'r', encoding='utf8')
ret = f.readlines()
print(ret)
f.close()
-------运行结果-------
['床前看月光 \n', '疑是地上霜\n', '举头望山月 \n', '低头思故乡']
PS:如果文件很大、内容很多的话,不建议使用 readlines 方法,这时读取文件内容的效率不高。
七. 上下文管理器
1. 什么是上下文管理器?
通过上下文管理器方式去打开、操作文件,我们可以不去担心文件最终是否忘了关闭了,从而导致资源泄露。
不同高级语言有不同的防止资源泄露的管理方式:
- Python:上下文管理器
- C++:智能指针
- Java:try with Resources
- Golang:defer
2. 为什么要有上下文管理器?
有些情况防不胜防,因为一些代码逻辑的原因,我们不能保证文件最终真的被关闭了:
3. 怎么使用上下文管理器?
打开文件之后,是容易忘记关闭的。Python 提供了上下文管理器,来帮助程序员自动关闭文件。
语法如下:
with open('d:/test.txt', 'r', encoding='utf8') as f:
# 这里进行文件的处理逻辑,比如
lines = f.readlines()
print(lines)
它有以下两个作用:
- 使用 with 语句打开文件
- 当 with 内部的代码块执行完毕后, 就会自动调用关闭方法
示例: