第十三章 文件
13.1 计算机眼中的文件
任务一:文件的概念和作用
知识点:
计算机的文件,就是存储在某种长期储存设备上的一段数据
存储设备包括:硬盘、U 盘、移动硬盘、光盘…
文件的作用:将数据长期保存下来,在需要的时候使用
任务二:计算机的文件储存方式
知识点:
在计算机中,文件是以二进制的方式保存在磁盘上的
文本文件:
可以使用文本编辑软件查看内容,本质上还是二进制文件
例如:python 的源程序
二进制文件:
保存的内容不是给人直接阅读的,而提供给计算机其他程序使用的
例如:图片文件、音频文件、视频文件等等
二进制文件不能使用文本编辑软件查看
任务三:在控制台中查看文件内容
知识点:
在Windows中,控制台指令type查看文件内容
在Linux或Mac中,控制台指令cat查看文件内容
在Jupyter中,cell中输入指令**%pycat**查看文件内容
中文字符可能会在控制台中乱码
13.2 文件操作
13.2.1 基本流程
任务一:文件操作的基本流程
知识点:在计算机中要操作文件方式是非常固定,共包含三个步骤:
1、打开文件
2、读或写文件
读将文件内容读入内存
写将内存内容写入文件
3、关闭文件
任务二:操作文件的基本流程
在 Python 中要操作文件需要记住1个函数和3个文件方法
open 函数负责打开文件,并且返回文件对象
read/write/close 三个方法都需要通过文件对象来调用
函数/方法 | 说明 |
---|---|
open | 打开文件,并且返回文件操作对象 |
read | 将文件内容读取到内存 |
write | 将指定内容写入文件 |
close | 关闭文件 |
知识点:文件操作的常用流程
调用**open()**函数打开要操作的文件
调用文件方法**read()或write()**方法对文件进行读写操作
调用**close()**方法关闭文件,释放资源
13.2.2 文件的open()函数与close()方法
任务一:open()函数用法
知识点:
语法:open(name[, mode, encoding])
描述:open()函数用于打开一个文件名为name的文件
如果文件存在,返回一个file类型的对象,后续通过该对象进行文件的读写操作
如果文件不存在,会抛出异常
参数:name为包含相对路径或绝对路径的文件名
文件名区分大小写
由于路径中会包含\字符,与转移标识符冲突。为此,需要使用r字符串前缀,告知Python当前字符串不进行转义
返回:file类型的对象fileObject,用于后续的文件读写操作
例子:相对路径和绝对路径的文件打开方式
在当前目录下创建一个名为README.txt文件
文件中输入几行字符串,然后保存关闭
1 | # 相对路径,打开当前根目录文件 |
任务二:open()函数的可选参数
知识点:
参数:mode可选,指定文件打开模式(后续将给出详解)。默认以只读模式打开
参数:encoding关键字参数,可选,指定文件编码方式
对于中文文件,如果编码不匹配,会造成乱码
目前国际上最通用的是**’utf-8’编码。因此,可以设置文件打开的编码为encoding=’utf-8’**
1 | # 使用默认只读"r"、'utf-8'编码打开"README.txt" |
*任务三:文件的关闭**close()*方法
知识点:关闭文件**close()**方法
语法:fileObject.close()
描述:**close()**方法用于关闭一个已打开的文件
关闭后的文件不能再进行读写操作,否则会触发ValueError异常
当文件不再需要使用时,用 **close()**方法关闭文件释放资源是一个好的习惯
参数和返回:无
注意:如果忘记关闭文件,会造成系统资源消耗,而且会影响到后续对文件的访问
1 | # 关闭的文件对象必须要在内存中存在,否则会出错 |
任务四:3种自定义文件路径方式(重要)
知识点:
本地文件的访问需要指定路径。如果文件不在当前目录下,就需要明确文件的详细路径
由于常用的路径使用\来标识路径的层级关系,但是这与\转移字符的标识冲突。为此,提供了三种解决方案
单个反斜杠:/
两个斜杠:\(第一个\是转义符)
r用于防止字符转义
1 | # 本地文件的界定:指向一个本地存储的文件,是一个链接或者一个映射 |
扩展:任何文件都可以使用鼠标右键->属性来查看具体文件的绝对路径
13.2.3 open()函数配置读写模式
任务一:文件的打开模式
知识点:
open()函数默认以只读方式打开文件,并且返回文件类型的对象
Python通过open()函数mode参数对文件的操作提供了丰富的控制模式
语法如下:
1 | f = open("文件名", "访问mode") |
任务二:6种文件读写模式
知识点:open()函数mode参数解析
r以只读模式打开文件,增强型 r+以可读写模式打开文件
文件的指针将会放在文件的开头
需要该文件必须存在,否则会出错
例如:f = open(“README.txt”, “r”)
w以只写模式打开文件,增强型w+以可读写模式打开文件
若文件存在则原内容会被清空
若文件不存在则建立该文件
例如:f = open(“README.txt”, “w”)
a以追加只写模式打开文件,增强型a+以追加可读写模式打开文件
如果该文件已存在,文件指针将会放在文件的结尾
如果文件不存在,新建文件进行写入
例如:f = open(“README.txt”, “a”)
注意:
频繁的移动文件指针,会影响文件的读写效率
开发中更多的时候会以只读、只写的方式来操作文件
任务三:文件对象的属性
知识点:文件对象的3个属性
fileObject.closed:返回True如果文件已被关闭,否则返回False
fileObject.mode:返回被打开文件的访问模式
fileObject.name:返回文件的名称
1 | f = open("README.txt") |
13.2.4 文件的读方法
任务一:文件的读方法
知识点:
语法:fileObject.read(size)
描述:read()从文件中一次性读入并返回文件的所有内容
参数:size为非负整数,可选
指定读取文件的字节数,包括 **”\n”**字符
默认值,尝试尽可能多地读取文件内容,甚至读取当前整个文件内容
返回:从文件中读取的字符串
注意:read()方法执行后,会把文件指针移动到文件的末尾
例子:文件读程序
首先在当前目录下手动创建一个名为README.txt的文本文件,里面随意写几行内容并保存关闭
然后使用**read()**读取文件
1 | # 1. 打开 - 文件名需要注意大小写 |
注意:在开发中,通常会先编写打开和关闭的代码,再编写中间针对文件的读/写 操作!
任务二:文件指针
知识点:
文件指针并非C语言里的指针
文件指针标记从文件的哪个位置开始读写数据
第一次打开文件时,通常文件指针会指向文件的开始位置
当执行了read()方法后,文件指针会随着读取的内容进行移动
对于当前文件对象,文件指针具有记忆性
即下次再次调用read()方法会从上次读取内容之后开始
默认情况下会移动到文件末尾,除非指定read()方法的size参数
思考:
如果执行了一次**read()**方法,读取了所有内容,那么再次调用 **read()**方法,还能够获得到内容吗
答案:不能。第一次读取之后,文件指针移动到了文件末尾,再次调用不会读取到任何的内容
13.2.5 按行读取文件
任务一:按行读取文件
需求分析:
read()方法默认会把文件的所有内容一次性读取到内存中
如果文件太大,对内存消耗会非常严重
知识点:
语法:fileObject.readline()
描述:**readline()**方法一次读取文件的一行内容
参数:无
返回:返回从文件当前行中读取的字符串
注意:该方法执行后,会把文件指针移动到下一行,准备下次读取。依次读取所有行,直到文件结束符 EOF
例子:大文件读取方式
1 | # 打开文件 |
实例:爬取POI数据并转换为JSON格式
需求:
利用百度POI小插件爬取一些POI数据,然后存成txt文档
再用Python读取,编写成一个json格式(列表嵌套字典):**[{‘name’:’…’,’lng’:…,’lat’:…,’address’:’…’},{…},…,]**
第一步:爬取数据地址:https://www.metrodata.cn/poi 。输入地区和关键字,稍等几分钟就会出现爬取的数据。可以自定义查询参数
第二步:使用Python将数据转换为JSON格式
1 | f = open('shaoxian.txt','r') # 读取txt文件 |
13.2.6 文件的写方法
任务一:文件写方法write()
知识点:
语法:fileObject.write(str)
描述:**write()**方法用于向文件中写入指定字符串
参数:str为向文件写入指定的字符串
返回:返回的是写入的字符长度
注意:write()方法不会向字符串尾部自定添加换行符,需要手动添加**”\n”**
例子:文件写操作
1 | # 需要以写模式"w"打开文件 |
任务二:将列表写入文件
通过**writelines()**函数,将列表作为参数,写入文件
1 | # 当前目录 |
通过遍历为列表的每个元素添加换行符,考虑列表推导式方式
1 | f = open('test_write2.txt', 'w', encoding = 'utf8') |
13.2.7 (重要) with as语句维护文件操作环境
需求分析:
任何一门编程语言中,文件的输入输出、数据库的连接断开等,都是很常见的资源管理操作。但资源都是有限的,在写程序时,必须保证这些资源在使用过后得到释放,不然就容易造成资源泄露,轻者使得系统处理缓慢,严重时会使系统崩溃
例如,前面在介绍文件操作时,一直强调打开的文件最后一定要关闭,否则会程序的运行造成意想不到的隐患
但是,即便使用close()做好了关闭文件的操作,如果在打开文件或文件操作过程中抛出了异常,还是无法及时关闭文件
知识点:
在Python中,对应的解决方式是使用 with as 语句操作上下文管理器(context manager),它能够帮助我们自动分配并且释放资源
通过使用 with as 语句,打开的文件对象,无论期间是否抛出异常,都能保证with as语句执行完毕后自动调用**close()**方法
知识点:with as 语句的基本语法格式为:
1 | with 表达式 as obj: |
表达式:可以是资源打开函数的返回对象。比如文件**open()**函数,数据库连接函数等
obj:资源对象。比如文件**open()**函数返回的文件对象
将下面的代码改为为with…as语句
1 | # 需要以写模式"w"打开文件 |
有时候不得不承认Python编程就是一种享受,只要掌握该语言的某些特性
1 | # 将open()函数的返回值通过as赋值给f文件对象 |
13.3 综合实例
13.3.1 实例:文件指针问题
下面程序功能,打开一个文件,然后先向文件**write()写入字符串,然后再read()**读取字符串,并输出
1 | # 请换一个文件名,"w+"为读写模式 |
程序解析:
**f = open(“Data.txt”, “w+”, encoding=’utf-8’),以增强型“w+”读写方式、‘utf-8’编码打开文件“Data.txt”,由于该文件不存在,所以“Data.txt”**文件会被创建
调用3次写文件**f.write(data_str)**方法,在文件中写入3行字符串
然后,再调用读文件**f.read()方法,希望读取之前写入的内容。但是发现,读取内容为空。但是打开“Data.txt”**后,发现字符串已经被写入到文件中
原因分析:
当写文件完成时,文件的当前文件指针会处于文件末尾处
如果继续执行读取操作,系统从该光标处向后读取,但该文件指针已处于文件的末尾,所以读出内容为空
解决方法一:规范代码,将写文件和读文件分开。在执行完一个操作后应及时关闭文件对象**f.close()**。使用这种方法可以是文件安全有效,不会被其他操作影响预计效果
1 | f = open("Data.txt", "w+", encoding='utf-8') |
或者使用文件的逐行遍历
1 | f = open("Data.txt", "r+", encoding='utf-8') |
解决方法二:使用**seek()**方法移动光标至指定位置
知识点:文件光标移动
语法:fileObject.seek(offset,whence=0)
参数:offset为偏移量,即需要移动偏移的字节数
参数:whence为要从哪个位置开始偏移,默认值为0;0代表从文件开始为起始点,1代表从当前位置为起始点,2代表从文件末尾为起始点
1 | f = open("Data.txt", "w+", encoding='utf-8') |
习题:
将上面文件操作程序修改为with as语句结构
1 | with open("Data.txt", "w+", encoding='utf-8') as f: |
13.3.2 实例:文件数据的数值求和
需求分析:
在while循环章节中,所有平均值计算程序都有一个缺点:数字都需要用户手动输入。试想用户正在尝试着求100个数字的平均值,而恰巧在接近尾声时发生了打字错误。如果用户使用上面编写的程序,就需要从头开始,想想就崩溃
为此,解决该问题的一个可行方法是首先将所有数字都输入到文件中,即使用户在文件中输错了数字,也可以很轻易地进行修改和编辑
因此,需要将之前设计的平均值计算程序从用户输入数据改写为从文件中读取数据
这种面向文件的程序数据处理方式在现实应用中非常常见
本节将学习如何从文件中读取数据,然后实现哨兵式循环来计算平均数
任务一:数据生成
创建一个数据文件,共100行,每一行为一个随机数
1 | import random as rd |
任务二:while哨兵式循环来计算平均值
下面将对文件中的数字进行求平均,使用文件结束符EOF或空字符**””**作为循环的哨兵
1 | # 输入之前生成数字的文件:Data.txt |
任务三:for循环来计算平均值
相对于其他语言,Python提供了简便的文件读取方式,使用**readlines()**方法读取整个文件,返回以行为元素的列表。然后,通过for循环遍历元素(行),并对其做运算
1 | # 输入之前生成数字的文件:Data.txt |
程序解析:
显然,for循环比while循环更简洁
这程序for循环之所以能用,是因为Python提供了readlines()方法将整个文件以行为元素,返回列表。然后,就可以使用Python的确定循环(for循环),来遍历和处理列表元素
对于非Python语言,并不一定提供readlines()这么方便的方法。因此,只能通过哨兵式的while循环,进行文件读和相关运算
习题:
将上面文件操作程序修改为with as语句结构
1 | import random as rd |
13.3.3 实例:复制文件
任务一:小文件复制
打开一个已有文件,读取完整内容,并写入到另外一个文件
1 | # 1. 打开文件 |
任务二:大文件复制
打开一个已有文件,逐行读取内容,并顺序写入到另外一个文件
1 | # 1. 打开文件 |
13.4 (常识) 文件和目录管理操作
13.4.1 常用文件和目录函数介绍
知识点:
在控制台终端或文件系统中可以执行常规的文件/目录管理操作,例如:创建、重命名、删除、改变路径、查看目录内容等
在Python中,实现上述功能需要导入 os 模块
注意:
Jupyter的cell支持IPython的文件和目录管理指令
文件查询命令:**%ls**
显示文件内容:**%cat**
任务三:文件操作
方法名 | 说明 | 示例 |
---|---|---|
rename | 重命名文件 | os.rename(源文件名, 目标文件名) |
remove | 删除文件 | os.remove(文件名) |
任务四:目录操作
方法名 | 说明 | 示例 |
---|---|---|
listdir | 目录列表 | os.listdir(目录名) |
mkdir | 创建目录 | os.mkdir(目录名) |
rmdir | 删除目录 | os.rmdir(目录名) |
getcwd | 获取当前目录 | os.getcwd() |
chdir | 修改工作目录 | os.chdir(目标目录) |
path.isdir | 判断是否是文件 | os.path.isdir(文件路径) |
13.4.2 相对路径和绝对路径
任务一:当前目录
知识点:什么是当前工作目录?
每个运行在计算机上的程序,都有一个“当前工作目录”( cwd)
使用**os.getcwd()**函数取得当前工作路径
使用os.chdir() 切换当前目录,类似控制台的cd指令
1 | # 导入os模块 |
注意:
如果使用os.chdir()修改当前目录,当指定目录不存在时,Python 解释器触发异常FileNotFoundError
例如,os.chdir(‘C:\error’),假设error目录不存在
任务二:相对路径和绝对路径 (重要)
知识点:明确一个文件所在的路径,有 2 种表示方式,分别是:
绝对路径:总是从根文件夹开始
Window 系统中以盘符(C:、D:)作为根文件夹
Mac或Linux系统中以**/**作为根文件夹
相对路径:指的是文件相对于当前工作目录所在的位置
例如,当前工作目录为”C:\Windows\System32”,若文件 demo.txt 就位于这个 System32 文件夹下,则demo.txt的相对路径表示为 “.\demo.txt”
其中 .\ 就表示当前所在目录
知识点(常识):常用的相对路径表示
.\ 表示当前所在目录。例如 .\data.txt,表示当前文件夹中data.txt文件
..\ 表示当前所在目录的父目录。例如 ..\data.txt,表示父文件夹中的data.txt文件
任务三:os.path模块
知识点:Python os.path 模块提供了一些函数,可以实现绝对路径和相对路径之间的转换,以及检查给定的路径是否为绝对路径,例如:
调用 os.path.abspath(path) 将返回 path 参数的绝对路径的字符串,这是将相对路径转换为绝对路径的简便方法
调用 **os.path.isabs(path)**,如果参数是一个绝对路径,就返回 True,如果参数是一个相对路径,就返回 False
调用 os.path.relpath(path, start) 将返回从 start 路径到 path 的相对路径的字符串。如果没有提供 start,就使用当前工作目录作为开始路径
调用 os.path.dirname(path) 将返回一个字符串,表示文件夹路径。它包含 path 参数中最后一个斜杠之前的所有内容
调用 os.path.basename(path) 将返回一个字符串,表示文件名。它包含 path 参数中最后一个斜杠之后的所有内容
注意:例子中的路径可以根据实际情况自定义,不必要完全遵照本节的例子,根据自己的系统环境对本节代码做适当调整即可
1 | # 获得当前路径 |
1 | # os.path.abspath('.')获得绝对路径 |
1 | # 获得文件名(不含路径),输出为'calc.exe' |
13.4.3 实例:文件目录操作
例子中的文件路径都需要根据具体文件位置进行自定义
1 | # os模块:系统模块 - 常用命令 |
13.5 (常识) UTF-8编码解决中文乱码问题
知识点:文本文件存储的内容是基于字符编码的文件,常见的编码有 ASCII 编码,UNICODE 编码等
Python 2默认使用 ASCII 编码格式
Python 3默认使用 UTF-8 编码格式
如果文件或网络爬虫数据中包含非 ASCII 编码字符,首先要将字符转换为 UTF-8 编码,才能被不会出现乱码问题
任务一:ASCII 编码和 UTF-8 编码
知识点:ASCII 编码
计算机中只有 256 个 ASCII 字符。
一个 ASCII 在内存中占用 1 个字节 的空间。
8 个 0/1 的排列组合方式一共有 256 种,也就是 2 ** 8
对于中文如果使用ASCII 码,将会出现乱码
知识点:UTF-8 编码格式
计算机中使用 1~6 个字节来表示一个UTF-8字符,涵盖了地球上几乎所有地区的文字
大多数汉字会使用3 个字节表示
UTF-8是UNICODE编码的一种编码格式
任务二:使用UTF-8 编码防止中文乱码
虽然Python 3.x默认使用 UTF-8 编码格式,但是对于中文文件也会出现乱码问题,特别是读取包含中文的文本
知识点:建议在源文件的第一行增加以下代码,解释器会以 utf-8 编码来处理 python文件
1 | #在源文件的第一行增加以下代码(二选一) |
在读取包含中文文件时,**open()**函数指定encoding参数为 utf-8 。例如:
1 | f = open("Data.txt", "r+", encoding='utf-8') |
任务三:使用UTF-8编码字符串
知识点:
在 Python 2 中,即使指定了文件使用 UTF-8 的编码格式,但是在遍历字符串时,仍然会以字节为单位遍历字符串
要能够正确的遍历字符串,在定义字符串时,需要在字符串的引号前,增加一个小写字母 u,告诉解释器这是一个 unicode 字符串
虽然Python 3的字符串都使用 UTF-8编码,但是如果字符串来自文件或网络爬虫数据(可能不是使用UTF-8),需要将它们先转换为UTF-8,程序才不会出现乱码问题
1 | # *-* coding:utf8 *-* |