python笔记--5--文件操作
文件内容操作三部曲:打开、读写、关闭
open(file, mode='r', buffering=1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
文件名指定了被打开的文件名称。
打开模式指定了打开文件后的处理方式。
缓冲区指定了读写文件的缓存模式。0表示不缓存,1表示缓存,如大于1则表示缓冲区的大小。默认值是缓存模式。
参数encoding指定对文本进行编码和解码的方式,只适用于文本模式,可以使用Python支持的任何格式,如GBK、utf8、CP936等等。
open( )函数返回1个文件对象,该对象可以对文件进行各种操作。
在管理文件对象时推荐with关键字,可以有效地避免文件无法正常关闭的情况
用于文件内容读写时,with语句的用法如下:
with open(filename, mode, encoding) as fp:
#这里写通过文件对象fp读写文件内容的语句
另外,上下文管理语句with还支持下面的用法,进一步简化了代码的编写。
with open('test.txt', 'r') as src, open('test_new.txt', 'w') as dst:
dst.write(src.read())
文件打开方式
模式 |
说明 |
r |
读模式(默认模式,可省略),如果文件不存在则抛出异常 |
w |
写模式,如果文件已存在,先清空原有内容 |
x |
写模式,创建新文件,如果文件已存在则抛出异常 |
a |
追加模式,不覆盖文件中原有内容 |
b |
二进制模式(可与其他模式组合使用) |
t |
文本模式(默认模式,可省略) |
+ |
读、写模式(可与其他模式组合使用) |
文件对象常用属性
属性 |
说明 |
buffer |
返回当前文件的缓冲区对象 |
closed |
判断文件是否关闭,若文件已关闭则返回True |
fileno |
文件号,一般不需要太关心这个数字 |
mode |
返回文件的打开模式 |
name |
返回文件的名称 |
文件对象常用方法
方法 |
功能说明 |
close() |
把缓冲区的内容写入文件,同时关闭文件,并释放文件对象 |
detach() |
分离并返回底层的缓冲,底层缓冲被分离后,文件对象不再可用,不允许做任何操作 |
flush() |
把缓冲区的内容写入文件,但不关闭文件 |
read([size]) |
从文本文件中读取size个或字符(Python 3.x)的内容作为结果返回,或从二进制文件中读取指定数量的字节并返回,如果省略size则表示读取所有内容 |
readable() |
测试当前文件是否可读 |
readline() |
从文本文件中读取一行内容作为结果返回 |
readlines() |
把文本文件中的每行文本作为一个字符串存入列表中,返回该列表,对于大文件会占用较多内存,不建议使用 |
seek(offset[, whence]) |
把文件指针移动到新的位置,offset表示相对于whence的位置。whence为0表示从文件头开始计算,1表示从当前位置开始计算,2表示从文件尾开始计算,默认为0 |
seekable() |
测试当前文件是否支持随机访问,如果文件不支持随机访问,则调用方法seek()、tell()和truncate()时会抛出异常 |
tell() |
返回文件指针的当前位置 |
truncate([size]) |
删除从当前指针位置到文件末尾的内容。如果指定了size,则不论指针在什么位置都只留下前size个字节,其余的一律删除 |
write(s) |
把字符串s的内容写入文件 |
writable() |
测试当前文件是否可写 |
writelines(s) |
把字符串列表写入文本文件,不添加换行符 |
import random with open(r'e:/test/a123.txt', 'w') as fp: # 开启写模式,往文件中写入100个随机整数,一行一个整数 for i in range(100): fp.write(str(random.randint(0, 1000))) fp.write('\n') with open('e:/test/a123.txt', 'r') as fp: # 开启读模式,把文件中的数字读取出来,排序,输出 data = [int(line) for line in fp] data.sort() print(data) print(len(data)) ''' [7, 11, 16, 17, 44, 50, 55, 61, 67, 102, 113, 115, 116, 132, 157, 172, 175, 176, 177, 177, 213, 218, 221, 225, 244, 253, 272, 281, 289, 302, 324, 372, 383, 384, 410, 410, 424, 427, 433, 449, 453, 468, 469, 484, 492, 496, 507, 513, 514, 538, 542, 542, 542, 548, 574, 592, 605, 618, 619, 621, 623, 631, 632, 649, 653, 659, 661, 661, 669, 675, 682, 699, 699, 710, 718, 723, 726, 737, 738, 739, 741, 776, 788, 824, 835, 841, 850, 853, 857, 868, 883, 883, 933, 954, 956, 956, 960, 964, 975, 983] 100 '''
二进制文件操作
数据库文件、图像文件、可执行文件、音视频文件、Office文档等等均属于二进制文件。
对于二进制文件,不能使用记事本或其他文本编辑软件进行正常读写,也无法通过Python的文件对象直接读取和理解二进制文件的内容。必须正确理解二进制文件结构和序列化规则,才能准确地理解二进制文件内容并且设计正确的反序列化规则。
所谓序列化,简单地说就是把内存中的数据在不丢失其类型信息的情况下转成对象的二进制形式的过程,对象序列化后的形式经过正确的反序列化过程应该能够准确无误地恢复为原来的对象。
Python中常用的序列化模块有struct、pickle、marshal和shelve。
pickle模块
pickle.dump(obj, file[, protocol])
序列化对象,并将结果数据流写入到文件对象中。参数protocol是序列化模式,默认值为0,表示以文本的形式序列化。protocol的值还可以是1或2,表示以二进制的形式序列化。
pickle.load(file)
反序列化对象。将文件中的数据解析为一个Python对象。
import pickle i = 13000000 # 整数 a = 99.056 # 浮点数 s = '中国人民123abc' # 字符串 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'} # 字典 data = [i, a, s, lst, tu, coll, dic] # 列表 with open('e:/test/sample_pickle.dat', 'wb') as f: try: pickle.dump(len(data), f) # 表示后面将要写入的数据个数 for item in data: pickle.dump(item, f) except: print('写文件异常!') # 如果写文件异常则跳到此处执行 with open('e:/test/sample_pickle.dat', 'rb') as f: n = pickle.load(f) for i in range(n): x = pickle.load(f) print(x) ''' 13000000 99.056 中国人民123abc [[1, 2, 3], [4, 5, 6], [7, 8, 9]] (-5, 10, 8) {4, 5, 6} {'a': 'apple', 'b': 'banana', 'g': 'grape', 'o': 'orange'} '''
struct模块
struct模块中最主要的三个函数式pack()、unpack()、calcsize()。
pack(fmt, v1, v2, ...) ------ 根据所给的fmt描述的格式将值v1,v2,...转换为一个字符串。
unpack(fmt, bytes) ------ 根据所给的fmt描述的格式将bytes反向解析出来,返回一个元组。
calcsize(fmt) ------ 根据所给的fmt描述的格式返回该结构的大小。
格式字符有以下的定义:
Format | C Type | Python | 字节数 |
x | pad byte | no value | 1 |
c | char | bytes of length 1 | 1 |
b | signed char | integer | 1 |
B | unsigned char | integer | 1 |
? | _Bool | bool | 1 |
h |
short |
integer | 2 |
H | unsigned short | integer | 2 |
i | int | integer | 4 |
I | unsigned int | integer | 4 |
l | long | integer | 4 |
L | unsigned long | integer | 4 |
q | long long | integer | 8 |
Q | unsigned long long | integer | 8 |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | bytes | 1 |
p | char[] | bytes | 1 |
P | void * | integer |
注意: 1. c,s和p按照bytes对象执行转码操作,但是在使用UTF-8编码时,也支持str对象。
2. ‘?’按照C99中定义的_Bool类型转码。如果该类型不可用,可使用一个char冒充。
3. ‘q'和’Q‘仅在64位系统上有用。
import struct n = 1300000000 # 整型 x = 96.45 # 浮点型 b = True # 布尔型 s_w = 'a1@中国' # 字符串 sn_w = struct.pack('if?', n, x, b) # 以integer(i),float(f),Bool(?)这样的形式进行序列化,转换为一个字符串 with open('e:/test/sample_struct.dat', 'wb') as f: f.write(sn_w) # 写入字节串 f.write(s_w.encode()) # 将字符串以utf8格式编码 with open('e:/test/sample_struct.dat', 'rb') as f: sn_r = f.read(9) # 9字节 = 整型4字节 + 浮点型4字节 + 布尔型1字节 tu = struct.unpack('if?', sn_r) # 以integer(i),float(f),Bool(?)这样的格式将bytes反向解析出来,返回一个元组 print(tu) n, x, bl = tu print('n=', n) print('x=', x) print('bl=', bl) s_r = f.read(9).decode() # utf8编码,一个中文字符占3字节,加上前面a1@这三个字节,共9字节 print('s_r=', s_r) ''' (1300000000, 96.44999694824219, True) n= 1300000000 x= 96.44999694824219 bl= True s_r= a1@中国 '''
shelve模块
Python标准库shelve也提供了二进制文件操作的功能,可以像字典赋值一样来写入二进制文件,也可以像字典一样读取二进制文件。
import shelve zhangsan = {'age': 38, 'sex': 'Male', 'address': 'SDIBT'} lisi = {'age': 40, 'sex': 'Male', 'qq': '1234567', 'tel': '7654321'} with shelve.open('e:/test/shelve_test.dat') as fp: fp['zhangsan'] = zhangsan # 以字典形式把数据写入文件 fp['lisi'] = lisi for i in range(5): fp[str(i)] = str(i) with shelve.open('e:/test/shelve_test.dat') as fp: print(fp['zhangsan']) # 读取并显示文件内容 print(fp['zhangsan']['age']) print(fp['lisi']['qq']) print(fp['3']) ''' {'age': 38, 'sex': 'Male', 'address': 'SDIBT'} 38 1234567 3 '''
marshal模块
Python标准库marshal也可以进行对象的序列化和反序列化。
import marshal # 导入模块 x1 = 30 # 待序列化的对象 x2 = 5.0 x3 = [1, 2, 3] x4 = (4, 5, 6) x5 = {'a': 1, 'b': 2, 'c': 3} x6 = {7, 8, 9} x = [eval('x'+str(i)) for i in range(1, 7)] # 把需要序列化的对象放到一个列表中 with open('e:/test/marshal_test.dat', 'wb') as fp: # 创建二进制文件 marshal.dump(len(x), fp) # 先写入对象个数 for item in x: marshal.dump(item, fp) with open('e:/test/marshal_test.dat', 'rb') as fp: # 打开二进制文件 n = marshal.load(fp) # 获取对象个数 for i in range(n): print('x%d: %s' % (i+1, marshal.load(fp))) # 反序列化,输出结果 ''' x1: 30 x2: 5.0 x3: [1, 2, 3] x4: (4, 5, 6) x5: {'a': 1, 'b': 2, 'c': 3} x6: {8, 9, 7} '''
文件级操作
如果需要处理文件路径,可以使用os.path模块中的对象和方法;
如果需要使用命令行读取文件内容可以使用fileinput模块;
创建临时文件和文件夹可以使用tempfile模块;
另外,Python 3.4之后版本的pathlib模块提供了大量用于表示和处理文件系统路径的类。
os模块
os模块常用文件操作函数
方法 |
功能说明 |
access(path, mode) |
测试是否可以按照mode指定的权限访问文件 |
chdir(path) |
把path设为当前工作目录 |
chmod(path, mode, *, dir_fd=None, follow_symlinks=True) |
改变文件的访问权限 |
curdir |
当前文件夹 |
environ |
包含系统环境变量和值的字典 |
extsep |
当前操作系统所使用的文件扩展名分隔符 |
get_exec_path() |
返回可执行文件的搜索路径 |
getcwd() |
返回当前工作目录 |
listdir(path) |
返回path目录下的文件和目录列表 |
mkdir(path[, mode=0777]) |
创建目录,要求上级目录必须存在 |
makedirs(path1/path2…, mode=511) |
创建多级目录,会根据需要自动创建中间缺失的目录 |
open(path, flags, mode=0o777, *, dir_fd=None) |
按照mode指定的权限打开文件,默认权限为可读、可写、可执行 |
popen(cmd, mode='r', buffering=-1) |
创建进程,启动外部程序 |
rmdir(path) |
删除目录,目录中不能有文件或子文件夹 |
remove(path) |
删除指定的文件,要求用户拥有删除文件的权限,并且文件没有只读或其他特殊属性 |
removedirs(path1/path2…) |
删除多级目录,目录中不能有文件 |
rename(src, dst) |
重命名文件或目录,可以实现文件的移动,若目标文件已存在则抛出异常,不能跨越磁盘或分区 |
replace(old, new) |
重命名文件或目录,若目标文件已存在则直接覆盖,不能跨越磁盘或分区 |
scandir(path='.') |
返回包含指定文件夹中所有DirEntry对象的迭代对象,遍历文件夹时比listdir()更加高效 |
sep |
当前操作系统所使用的路径分隔符 |
startfile(filepath [, operation]) |
使用关联的应用程序打开指定文件或启动指定应用程序 |
stat(path) |
返回文件的所有属性 |
system() |
启动外部程序 |
truncate(path, length) |
将文件截断,只保留指定长度的内容 |
walk(top, topdown=True, onerror=None) |
遍历目录树,该方法返回一个元组,包括3个元素:所有路径名、所有目录列表与文件列表 |
write(fd, data) |
将bytes对象data写入文件fd |
os.path常用的文件操作函数
方法 |
功能说明 |
abspath(path) |
返回给定路径的绝对路径 |
basename(path) |
返回指定路径的最后一个组成部分 |
commonpath(paths) |
返回给定的多个路径的最长公共路径 |
commonprefix(paths) |
返回给定的多个路径的最长公共前缀 |
dirname(p) |
返回给定路径的文件夹部分 |
exists(path) |
判断文件是否存在 |
getatime(filename) |
返回文件的最后访问时间 |
getctime(filename) |
返回文件的创建时间 |
getmtime(filename) |
返回文件的最后修改时间 |
getsize(filename) |
返回文件的大小 |
isabs(path) |
判断path是否为绝对路径 |
isdir(path) |
判断path是否为文件夹 |
isfile(path) |
判断path是否为文件 |
join(path, *paths) |
连接两个或多个path |
realpath(path) |
返回给定路径的绝对路径 |
relpath(path) |
返回给定路径的相对路径,不能跨越磁盘驱动器或分区 |
samefile(f1, f2) |
测试f1和f2这两个路径是否引用的同一个文件 |
split(path) |
以路径中的最后一个斜线为分隔符把路径分隔成两部分,以列表形式返回 |
splitext(path) |
从路径中分隔文件的扩展名 |
splitdrive(path) |
从路径中分隔驱动器的名称 |
import os print(os.path.join('hello', 'world', '123')) # 把字符串组合成路径 # hello\\world\\123 print(os.path.split('hello\\world\\123')) # 相反,把路径拆分成前后两个独立的字符串 # ('hello\\world', '123') parent_path, name = os.path.split('hello\\world\\123') print(parent_path) # hello\world print(name) # 123 print(os.path.splitext('hello/world/123/test.txt')) # 可以分解出文件的扩展名 # ('hello/world/123/test', '.txt') print(os.path.splitext('hello/world/123/test.txt')[1]) # 直接获得扩展名 # '.txt' print(os.path.normpath('e:/user/my/../hello/world')) # 可以把文件路径简单化 # e:\user\hello\world print(os.path.abspath('123')) # 直接指向当前项目的路径下 # C:\Users\12696\PycharmProjects\Test\123 print(os.path.exists('e:/te1st.txt')) # 判断文件或文件夹是否存在 # False print(os.listdir('e:/test')) # 以列表的形式返回指定文件夹下所有的文件和文件夹 # ['111', '222', '333', '444.txt'] for name in os.listdir('e:/test'): print(os.path.join('e:/test', name)) ''' e: / test\111 e: / test\222 e: / test\333 e: / test\444.txt ''' print(os.path.isdir('e:/test')) # 判断路径指向的是否是一个文件夹 # True print(os.path.isfile('e:/test/a123.txt')) # 判断路径指向的是否是一个文件 # True print(os.path.getsize('e:/test')) # 可以在不必打开和扫描某个文件的情况下以字节为单位返回该文件的大小,如果路径指向一个文件夹,那么它将返回4096(Windows下) # 4096 print(os.path.getsize('e:/test/a123.txt')) # 2584
import os # 搜索当前目录下的.py文件 alist = [fname for fname in os.listdir(os.getcwd()) if os.path.isfile(fname) and fname.endswith('.py')] alist.sort() for i in alist: print(i) ''' marshal模块.py my_lambda.py os模块.py pickle模块.py property测试.py shelve模块.py struct模块.py 使用re模块对象.py 偏函数.py 函数高级.py 单参函数实现多参函数.py 可调用对象.py 多态.py 打印九九乘法表.py 文件操作.py 斐波那契数列.py 正则表达式匹配字符串中的电话号码.py 直接使用re模块方法.py 类中私有成员.py 类的各种方法.py 继承.py 装饰器.py 装饰器用户名检查.py ''' # 将当前目录下的.html文件改成.htm文件 file_list = [file_name for file_name in os.listdir('.') if file_name.endswith('.html')] for filename in file_list: newname = filename[:-4]+'htm' os.rename(filename, newname) print(filename+'更名为:'+newname) ''' 1.html更名为:1.htm 2.html更名为:2.htm 3.html更名为:3.htm '''
遍历指定路径下的所有文件和文件夹,并格式化输出文件路径文件名和文件夹名,文件大小,修改时间
import os
import datetime def print_tree(dir_path): for name in sorted(os.listdir(dir_path)): full_path = os.path.join(dir_path, name) file_size = os.path.getsize(full_path) modify_time = datetime.datetime.fromtimestamp(os.path.getmtime(full_path)) print('%s\t%s\t%s' % (full_path.ljust(26), str(file_size).ljust(6), modify_time)) if os.path.isdir(full_path): print_tree(full_path) if __name__ == '__main__': print_tree('e:/test') ''' e:/test\111 4096 2018-05-02 17:42:51.417505 e:/test\111\aaa 0 2018-05-02 17:43:05.443528 e:/test\111\aaa\efg.docx 0 2018-05-02 17:42:57.966647 e:/test\111\abc.xlsx 6610 2018-05-02 17:42:38.974835 e:/test\222 0 2018-05-02 17:21:28.839784 e:/test\333 0 2018-05-02 17:21:32.269475 e:/test\444.txt 0 2018-05-02 17:22:55.221821 e:/test\Hello.txt 0 2018-05-02 17:27:56.921054 e:/test\a123.txt 490 2018-05-06 18:42:55.688596 e:/test\marshal_test.dat 102 2018-05-06 20:32:17.170066 e:/test\sample_pickle.dat 235 2018-05-06 19:37:36.338858 e:/test\sample_struct.dat 18 2018-05-06 20:12:12.599015 e:/test\shelve_test.dat.bak 125 2018-05-06 20:15:30.979789 e:/test\shelve_test.dat.dat 3083 2018-05-06 20:15:30.979789 e:/test\shelve_test.dat.dir 125 2018-05-06 20:15:30.979789 '''
shutil模块
使用该模块的copyfile()方法复制文件
>>> import shutil
>>> shutil.copyfile('C:\\dir.txt', 'C:\\dir1.txt')
将C:\Python34\Dlls文件夹以及该文件夹中所有文件压缩至D:\a.zip文件
>>> shutil.make_archive('D:\\a', 'zip', 'C:\\Python34', 'Dlls')
将刚压缩得到的文件D:\a.zip解压缩至D:\a_unpack文件夹
>>> shutil.unpack_archive('D:\\a.zip', 'D:\\a_unpack')
删除刚刚解压缩得到的文件夹
>>> shutil.rmtree('D:\\a_unpack')