Python教学课程分享7-文件操作
7.1 文件基本概念
计算机文件属于文件的一种,与普通文件载体不同,它是以计算机硬盘位载体存储在计算机上的信息合集。文件还是长久保存信息并允许重复使用和反复修改的重要方式,同时也是信息交换的重要途径。数据库文件、图像文件、音频和视频文件、可执行文件、Office文档、动态链接等,都以不同的形式存储在不同形式的存储设备上。按文件中数据的组织形式可以把文件分为文本文件和二进制文件两大类。
(1)文本文件
文本文件是一种计算机文件,它是一种典型的顺序文件,其文件的逻辑结构又属于流式文件,是一种由很多行字符构成的计算机文件。它是以ASCII码方式(也称文本方式)存储的文件,更确切地说,英文、数字等字符存储的是ASCII码,而汉字存储的是机内码。文本文件中除了存储文件有效字符信息(包括能用ASCII码字符表示的回车、换行等信息)外,不能存储其他任何信息。通常,通过在文本文件最后一行后放置文件结束标志来指明文件的结束。
(2)二进制文件
广义的二进制文件即指文件,由文件在外部设备的存放形式为二进制而得名。狭义的二进制文件即除文本文件以外的文件。常见的如图形图像文件、音频和视频文件、可执行文件、资源文件、各种数据库文件、各类Office文档等都属于二进制文件。二进制文件把信息以字节串存储,无法用记事本或其他普通字处理软件直接进行编辑,通常也无法被人直接阅读和理解,需要使用对应的软件进行解码后的读取、显示、修改或执行。
(3)两者的区别
从本质上来说,他们之间没有什么区别,因为他们在硬盘上都有一种的存放方式-二进制,但是如果要对他们做区分的话,可以这样来进行理解。每个字符由一个或多个字节组成,每个字节都是用的-128到127之间的部分数值来表示的,也就是说,-128到127之间还有一些数据没有对应任何字符的任何字节。如果一个文件中的每个字节的内容都是可以表示成字符的数据,就可以称这个文件为文本文件。由此可见,文本文件只是二进制文件中的一种特例,为了与文本文件相区别,人们才把除了文本文件以外的文件称之为二进制文件。由于很难严格区分文本文件和二进制文件的概念,所以可以简单的认为,如果一个文件专门用于存储文本字符的数据,没有包含字符以外的其他数据,就称之为文本文件,除此之外的文件就是二进制文件。
7.2 打开与关闭文件
7.2.1 open()函数与close()方法
open()函数的功能是以指定模式打开指定文件并创建文件对象,该函数的语法为:
l open(file,mode = ‘r’,buffering = -1,encoding = None,errors = None,newline = None,closefd = True,opener = None)
在上面的语法格式中,参数file指定要打开或者创建的文件名称。如果该文件不在当前目录下,则需要指定完整路径。为了减少完整路径中的“\”符号的输入,可以使用原始字符串。
参数mode指定打开文件后的处理方式,如“只读”、“只写”、“读写”、“追加”、“二进制只读”、“二进制读写”等,默认为“文本只读模式”。以不同方式打开文件时,文件指针的初始位置略有不同,例如,以“只读”和“只写”模式打开文件时文件指针的初始位置是文件头,而已“追加”模式打开文件时则文件指针的初始位置是文件尾。mode参数的取值范围如表7.1mode参数取值范围表。
表7.1 mode参数取值范围
模式 |
功能 |
r |
读模式(默认模式,可省略),若文件不存在则抛出异常 |
w |
写模式,若文件已存在则先清空原有内容 |
x |
写模式,创建新文件,若文件已存在则抛出异常 |
a |
追加模式,不覆盖文件中的原有内容 |
b |
二进制模式(可与其他模式组合使用) |
t |
文本模式(默认模式,可省略) |
+ |
读、写模式(可与其他模式组合使用) |
参数buffering指定读写文件的缓存模式,数值0(只在二进制模式中可以使用)表示不缓存,数值1(只在文本模式中可以使用)表示使用行缓存模式,大于1的数字则表示缓冲区的大小,默认值为-1。当只用默认值-1时,二进制文件和非交互式文本文件以固定大小的块为缓存单位,等价于io.DEFAULT_BUFFER_SIZE,交互式文本文件(isatty()函数方法返回True)采用行缓存模式。
参数encoding指定对文本进行编码和解码方式,只适用于文本模式,可以使用Python支持的任何格式。
参数newline只适用于文本模式,取值可以是None、’\n’、’\r’,’\r\n’中的任何一个,表示文件中新行的形式。
如果执行正常,open()函数返回一个可迭代的文件对象,通过该文件对象可以对文件进行读写操作,如果指定文件不存在、访问权限不够、磁盘空间不够或者其他原因导致创建文件对象失败则抛出异常。下面的代码分别以读、写方式打开了两个文件并创建了与之对应的文件对象。
l f1 = open(‘file1.txt’,’r’)
l f2 = open(‘file2.txt’,’w’)
当对文件内容操作完以后,一定要记得关闭文件对象,这样才能保证所做的任何修改都确实被保存到文件中。关闭文件所用到的函数为close()方法,close()函数的功能为把缓冲区的内容写入文件,同时关闭文件并释放文件对象。它的语法格式如下:
l f1.close()
7.2.2 with关键字
文件操作一般都要遵循“打开文件—读写文件—关闭文件”的标准套路,但是如果文件读写操作代码引发了异常,很难保证文件能够被正确关闭。使用上下文管理关键字with可以避免这个问题。关键字with可以自动管理资源,不论因为什么原因(即使是代码引发了异常)跳出with块,总能保证文件被正确关闭,并且可以在代码块执行完毕以后自动还原进入该代码块时的现场。with常用于文件操作、数据库连接、网络通信连接等场合。有了关键字with,再也不用担心文件未关闭的问题。关键字with的使用方式示例如下:
s = ‘Hello world\n 文本文件的读取方式\n 文本文件的写入方式\n’
with open(‘sample.txt’,’a+’)
f.write(s)
另外,上下文管理语句with还支持下面的用法:
l with open(‘test.txt’,’r’)as src,open(‘test_new.txt’,’w’)as dst:
dst.write(src.read())
在交互模式下使用文件对象的with()方法写入文件时,会显示成功写入的字符数量。如果不想显示这个数字,可以先导入sys模块,然后执行语句sys.stdout = (‘null’,’w’),这样再写入文件时就不会显示写入的字符数量了。
7.3 文件对象基本方法
7.3.1 read()、readline()、readlines()
用于读取文件的方法有三个,分别是read()函数,readline()函数和readlines()函数。这三个函数均可以接受一个变量用以限制每次读取的数据量,通常不使用该变量。
read()函数的功能是读取整个文件并将文件的内容放到一个字符串变量中,如果文件大于可用内存则不可使用这种处理方式。它的使用方式示例如下:
file_object = open("test.py",'r') #创建一个文件对象,也是一个可迭代对象
try:
all_the_text = file_object.read() #结果为str类型
print(type(all_the_text))
print("all_the_text=",all_the_text)
finally:
file_object.close()
readline()函数用于每次读取一行,读取完毕后返回的是一个字符串对象,用于保存当前行的内容。此种方式要比readlines()函数慢得多。它的使用格式示例如下:
file_object1 = open("test.py",'r')
try:
while True:
line = file_object1.readline()
if line:
print("line=",line)
else:
break
finally:
file_object1.close()
readlines()函数用于一次性读取整个文件并自动将文件内容分析成一个行的列表。它的使用格式示例如下:
file_object2 = open("test.py",'r')
try:
lines = file_object2.readlines()
print("type(lines)=",type(lines)) #type(lines)= <type 'list'>
for line in lines:
print("line=",line)
finally:
file_object2.close()
7.3.2 write()、writelines()
write()函数用于向文件中写入指定字符串,此方法的参数是一个字符串,并且此方法没有返回值。由于在缓冲,在调用flush()函数时或者close()方法之前,字符串可能不会写入到打开的相关联文件中。此方法的使用方式如下:
data = ["abc","def"]
f = open('2.txt','w')
f.write(data)
f.close()
writelines()方法的功能也是向文件中写入指定字符串,与write()方法不同,用此方法向文件中写入内容时,使用的参数除了可以是字符串以外,还可以是序列(例如列表),它会迭代帮你写入文件。此种方法的使用格式示例如下:
>>> f=open(r"C:\Users\Administrator\Desktop\test.txt",'w')
>>> f.writelines(['love\n','python\n','love python\n'])
#有3个换行符\n,向文件中写入3行数据
>>> f.writelines(['love','python','love python'])
#没有换行符\n,相当于写入了一行数据
>>> f.close()
7.4 数据序列化与反序列化
7.4.1 序列化和反序列化的作用
所谓序列化,简单的理解就是把内存中的数据在不丢失其类型信息的情况下转换成对象的二进制形式的过程,对象序列化后的数据经过正确的反序列化过程应该能够准确无误地恢复为原来的对象。
在Python中,序列化可以理解为把Python的对象编码转换为JSON格式的字符串,反序列化可以理解为把JSON格式字符串解码为Python数据对象。在Python中,序列化主流有两种方式,一种是使用pickle模块,一种是JSON模块。两者之间的区别主要体现在以下几个方面:
① JSON的序列化输出是文本对象(Unicode),而pickle序列化的输出是二进制字节(bytes);
② JSON可读性更好;
③ JSON拥有广泛的兼容性,能够在Python以外的很多地方使用。而pickle只针对Python;
④ JSON只能表示一部分Python内置的类型,不能表示用户自定义的类对象。当你尝试序列化一个自定义的类对象时,会抛出一个 TypeError 。而pickle则能表示大多数对象,包括用户自定义的类(绝大部分);
⑤ pickle不是用于多种语言之间的数据传输,它仅作为Python对象的持久化或者Python程序之间进行互相传输对象的方法,因此它支持了Python的所有数据类型。pickle反序列化后的对象与原对象是等值的副本对象,类似于deepcopy。
7.4.2 使用pickle进行序列化和反序列化
Python标准库pickle提供的dump()方法可以用于将数据进行序列化并写入文件(dump()方法的protocol参数为True时可以实现压缩的效果),而load()函数用于读取二进制文件中的内容并进行反序列化,还原为原来的信息。
使用pickle模块中的dump()方法进行序列化的使用方法示例如下:
import pickle
n = 7
i = 12000
a = 66.05
s = ‘中华儿女’
lst = [[1,2,3],[4,5,6],[7,8,9]]
tu = (-5,10,8)
coll = {4,5,6}
dic = {‘a’:’apple’,’b’:’banana’,’g’:’grape’,’o’:’orange’}
f = open(‘sample_pickle.dat’,’wb’) #以写模式打开二进制
try:
pickle.dump(n,f) #对象个数
pickle.dump(i,f) #写入整数
pickle.dump(a,f) #写入实数
pickle.dump(s,f) #写入字符串
pickle.dump(lst,f) #写入列表
pickle.dump(tu,f) #写入元组
pickle.dump(coll,f) #写入集合
pickle.dump(dic,f) #写入字典
except:
print(‘写入文件异常’)
finally:
f.close()
pickle模块还提供了一种dumps()方法,用于返回对象序列化之后的字节形式,示例如下:
>>> pickle.dumps([1,2,3]) #序列化列表
b’\x80\x03]q\x00(K\x01K\x02K\x03e.’
>>> pickle.dumps([1,2,3,4]) #序列化列表
b’\x80\x03]q\x00(K\x01K\x02K\x03K\x04e.’
>>> pickle.dumps({1,2,3,4}) #序列化集合
b’\x80\x03cbuiltins\nset\nq\x00]q\x01(K\x02K\x03K\x04e\x85q\x02Rq\x03.’
>>> pickle.dumps((1,2,3)) #序列化元组
b’\x80\x03K\x01K\x02K\x03\x87q\x00.’
>>> pickle.dumps(123) #序列化数字
b’\x80\x03K{.’
使用pickle模块中的load()函数进行反序列化的使用方法示例如下:
import pickle
f = open(‘sample_pickle.dat’,’rb’)
n = pickle.load(f)
for i in range(n):
x = pickle.load(f)
print(x)
f.close()
7.4.3 使用JSON进行序列化和反序列化
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于ECMAScript的一个子集。JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C、C++、Java、JavaScript、Perl、Python等)。这些特性使JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成(一般用于提升网络传输速率)。
JSON在python中分别由list和dict组成。Python类型数据转换和JSON数据格式互相转换关系如表7.2、7.3所示。
表7.2 JSON转换为Python形式
JSON |
Python |
Object |
dict |
array |
list |
string |
Unicode |
number(int) |
int,long |
number(real) |
float |
TRUE |
TRUE |
FALSE |
FALSE |
null |
None |
表7.3 Python转换为JSON形式
Python |
JSON |
dict |
Object |
list,tuple |
array |
str,Unicode |
string |
int,long,float |
number |
TRUE |
TRUE |
FALSE |
FALSE |
None |
null |
在JSON模块中存在dumps()方法,它的作用是将对象序列化,它的使用方式示例如下:
#coding:utf-8import json
# 简单编码
print(json.dumps(['foo',{'bar':('baz',None,1.0,2)}]))
#字典排序
print(json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True))
#自定义分隔符
print(json.dumps([1,2,3,{'4':5,'6':7}], sort_keys=True, separators=(',',':')))
print(json.dumps([1,2,3,{'4':5,'6':7}],sort_keys=True,separators=('/','-')))
#增加缩进,增强可读性,但缩进空格会使数据变大
print(json.dumps({'4':5,'6':7},sort_keys=True,indent=2,separators=(',',':')))
# 另一个比较有用的dumps参数是skipkeys,默认为False。
# dumps方法存储dict对象时,key必须是str类型,如果出现了其他类型的话,那么会产生TypeError异常,如果开启该参数,设为True的话,会忽略这个key。
data = {'a':1,(1,2):123}
print(json.dumps(data,skipkeys=True))
执行结果如下:
["foo",{"bar":["baz",null,1.0,2]}]
{"a": 0, "b": 0, "c": 0}
[1,2,3,{"4":5,"6":7}]
[1/2/3/{"4"-5/"6"-7}]
{
"4":5,
"6":7
}
{"a":1}
方法dump()的作用是将对象序列化并保存到文件中。它的使用方式示例如下:
obj = ['foo', {'bar': ('baz', None, 1.0, 2)}] #将对象序列化并保存到文件
with open(r"c:\json.txt","w+") as f:
json.dump(obj,f)
loads()函数的功能是将序列化字符串反序列化,它的使用方式示例如下:
import json
obj = ['foo', {'bar': ('baz', None, 1.0, 2)}]
a= json.dumps(obj)
print(json.loads(a))
执行结果如下所示:
[u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
load()函数的作用是将序列化字符串从文件中读取并反序列化,它的使用方式示例如下:
with open(r"c:\json.txt","r") as f:
print(json.load(f))
7.5 文件与文件夹基本操作
7.5.1 os模块
简单来说,os模块是一个Python的系统编程的操作模块,它可以用来处理文件和目录等需要人们日常手动进行的操作。在os模块中,比较常用的方法有mkdir()、rmdir()、listdir()、remove()、rename()、stat()等。
(1) mkdir()
os.mkdir()方法用于以数字权限模式创建目录。默认的模式为0777(八进制)。mkdir()方法语法格式如下:
l os.mkdir(path[,mode])
在此语法中,path为要创建的目录,参数mode为要为目录设置的权限数字模式。该方法没有返回值。此方法的使用方式示例如下:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os, sys
# 创建的目录
path = “/tmp/home/monthly/daily/hourly”
os.mkdir(path,0755)
print(“目录已创建”)
输出结果为:目录已创建。
(2) rmdir()
os.rmdir()函数用于删除指定路径的目录。仅当这文件夹是空的才可以,否则,抛出OSError。此方法没有返回值。它的语法格式如下:
l os.rmdir(path) #path为要删除的目录路径
它的使用方式示例如下:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os, sys
#列出目录,os.getcwd()函数功能为获取当前工作目录,即当前python脚本工作的目录路径
print(“目录为:%s”,%os.listdir(os.getwd()))
#删除路径
os.rmdir(“mydir”)
#列出重命名后的目录
print(“目录为:%s”,%os.listdir(os.getwd()))
执行结果如下:
目录为:
[ ‘a1.txt’,’resume.doc’,’a3.py’,’mydir’ ]
目录为:
[ ‘a1.txt’,’resume.doc’,’a3.py’ ]
(3) listdir()
os.listdir()函数方法用于返回指定的文件夹包含的文件或者文件夹的名字的列表。这个列表以字母顺序排列。它不包括’.’和’..’,即使这两个符号在文件夹中。listdir()方法的语法格式如下:
l os.listdir(path) #path为需要列出的目录路径
此方法的使用方式示例如下:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os, sys
# 打开文件
path = “/var/www/html/”
dirs = os.listdir(path)
#输出所有文件和文件夹
for file in dirs:
print(file)
执行以上程序后所得结果为:
test.htm
stamp
5d.txt
papers
(4) remove()
os.remove()方法用于删除指定路径的文件。如果指定的路径是一个目录,则将抛出OSError,此方法没有返回值,并在Unix和Windows中有效。它的语法格式如下:
l os.remove(path) #要移除的文件路径
此函数的使用方法示例如下:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os, sys
#列出目录
print(“目录为:%s”%os.listdir(os.getcwd()))
#移除
os.remove(“aa.txt”)
#移除后列出目录
print(“移除后的目录为:%s”%os.listdir(os.getcwd()))
执行后显示的输出结果为:
目录为:
[ ‘a1.txt’,’aa.txt’,’resume.doc’ ]
移除后的目录为:
[ ‘a1.txt’,’resume.doc’ ]
(5) rename()
os.rename()函数用于命名文件或目录,从src到dst,如果dst是一个存在的目录,则将抛出OSError。该方法没有返回值。它的语法格式如下:
os.rename(src,dst) #src为要修改的目录名,dst为修改后的目录名
它的使用方式示例如下:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os, sys
#列出目录
print(“目录为:%s”%os.listdir(os.getcwd()))
#重命名
os.rename(“test”,”test2”)
print(“重命名成功。\n”)
#列出重命名后的目录
print(“目录为:%s”%os.listdir(os.getcwd()))
执行后所得的输出结果为:
目录为:
[ ‘a1.txt’,’a3.py’,’test’ ]
重命名成功。
目录为:
[ ‘a1.txt’,’a3.py’,’test2’ ]
(6) stat()
os.stat()方法用于在给定的路径上执行一个系统stat的调用,它返回stat结构(见表7.4stat结构),它的语法格式如下:
l os.stat(path) #指定路径
它的使用方式示例如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os, sys
# 显示文件 "a2.py"信息
statinfo = os.stat('a2.py')
print(statinfo)
执行以上程序后所输出的结果为:
posix.stat_result(st_mode=33188,st_ino=3940649674337682L,st_dev=277923425L,st_nlink=1,st_uid=400,st_gid=401,st_size=335L,st_atime=1330498089,st_mtime=1330498089,st_ctime=1330498089)
表7.4 stat结构表
stat结构 |
功能 |
st_mode |
inode保护模式 |
st_ino |
inode节点号 |
st_dev |
inode驻留的设备 |
st_nlink |
inode的链接数 |
|
|
st_uid |
所有者的用户ID |
st_gid |
所有者的组ID |
st_size |
普通文件以字节为单位的大小;包含等待某些特殊文件的数据 |
st_atime |
上次访问的时间 |
st_mtime |
最后一次修改的时间 |
st_ctime |
由操作系统报告的”ctime”。在某些系统上是最新的元数据更改的时间,在其他系统上是创建时间 |
7.5.2 os.path模块
os.path模块主要用于文件属性的获取,在编程中经常用到,常用的方法有abspath()、split()、isfile()、isdir()、join()、exists()等。
(1) abspath()
abspath()函数用于返回path规范化的绝对路径,它的语法格式如下:
l os.path.abspath(path)
它的使用方式示例如下:
>>> os.path.abspath('test.csv')
'C:\\Python25\\test.csv'
>>> os.path.abspath('c:\\test.csv')
'c:\\test.csv'
>>> os.path.abspath('../csv\\test.csv')
'C:\\csv\\test.csv'
(2) split()
split()方法的功能是将path分割成目录和文件名并组成二元组进行返回。它的语法格式示例如下:
l os.path.split(path)
它的使用方式示例如下:
>>> os.path.split('c:\\csv\\test.csv')
('c:\\csv', 'test.csv')
>>> os.path.split('c:\\csv\\')
('c:\\csv', '')
(3) isfile()
isfile()方法用于判断path是否是一个存在的文件,如果是返回True,不存在则返回False。它的语法格式如下:
l os.path.isfile(path)
它的使用方式示例如下:
>>> os.path.isfile('c:\\boot.ini')
True
>>> os.path.isfile('c:\\csv\\test.csv')
False
>>> os.path.isfile('c:\\csv\\')
False
(4) isdir()
isdir()函数用于判断path是否是一个存在的目录,存在返回True,不存在则返回False。它的语法格式如下:
l os.path.isdir(path)
它的使用方式示例如下:
>>> os.path.isdir('c:\\')
True
>>> os.path.isdir('c:\\csv\\')
False
>>> os.path.isdir('c:\\windows\\test.csv')
False
(5) join()
join()函数的作用是将多个路径组合后返回,返回时第一个绝对路径之前的参数将被忽略。它的语法格式示例如下:
l os.path.join(path1[, path2[, ...]])
它的使用方式示例如下:
>>> os.path.join('c:\\', 'csv', 'test.csv')
'c:\\csv\\test.csv'
>>> os.path.join('windows\temp', 'c:\\', 'csv', 'test.csv')
'c:\\csv\\test.csv'
>>> os.path.join('/home/aa','/home/aa/bb','/home/aa/bb/c')
'/home/aa/bb/c'
(6) exists()
exists()函数用于判断path是否存在,如果存在返回True,不存在则返回False。它的语法格式如下:
l os.path.exists(path)
它的使用方式示例如下:
>>> os.path.exists('c:\\')
True
>>> os.path.exists('c:\\csv\\test.csv')
False