目录
1.1 shutil.copy(source,destination)
1.2 shutil.copytree(source, destination)
本章的内容包括文件复制,移动,改名,压缩等。
1.shutil模块
shutil模块提供了一些函数,用于复制文件和整个文件夹
1.1 shutil.copy(source,destination)
将路径source处的文件复制到路径distination处的文件夹,source和destination都是字符串。如果destination是一个文件名,它将作为被复制文件的新名字。该函数返回一个字符串,即被复制文件的新路径。
注意,不能复制文件夹
1.2 shutil.copytree(source, destination)
将复制整个文件夹,以及它包含的文件夹和文件。将source处的文件夹,包括它的所有的文件夹和子文件夹,复制到路径destination处的文件夹。该函数返回一个字符串,是新复制的文件夹的路径。
注意:目标目录必须不存在
#!/bin/usr/python3
import os,shutil
def copone():
#crrpath = os.path.join(os.getcwd(),'testdir')
crrpath = os.getcwd()
copy_one = 'copy_one'
if not os.path.exists(os.path.join(crrpath,'copy_one')):
os.makedirs(os.path.join(crrpath, copy_one))
#创建文件夹以后就可以复制文件了
#遍历当前目录下文件一个一个地复制,注意,不能复制文件夹
filelist = os.listdir(crrpath) #列出的是当前目录下的所有文件和文件夹名字字符串列表,使用shutil.copy()时要过滤掉文件夹
for filename in filelist:
if os.path.isfile(filename):
shutil.copy(os.path.join(crrpath, filename), copy_one)
print('copy %s --> %s' %(filename, os.path.join(crrpath, copy_one) ) )
def copall():
#crrpath = os.path.join(os.getcwd(),'testdir')
crrpath = os.getcwd()
copy_all = 'copy_all'
destpath = os.path.join(crrpath,'copy_all')
#使用shutil.copytree()整体复制时,目标目录必须不存在,如果已经存在,会报错,晕---
#加上目录存在的判断,如果存在,删除目录树,危险操作
if os.path.exists(destpath):
shutil.rmtree(destpath)
print('copy all --> %s' %( os.path.join(crrpath, copy_all) ) )
shutil.copytree(crrpath, os.path.join(crrpath, copy_all) )
if __name__ == '__main__':
copall()
copone()
输出:
C:\Learning\PYTHON\python-auto\100-days\zipfiles>python zipfile.py
copy all --> C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all
copy bacon.txt --> C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_one
copy tar.txt --> C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_one
copy TAR_DIR.txt --> C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_one
copy zipfile.py --> C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_one
文件夹:
1.3 文件的移动和改名
# shuti.move(source, destination)
将路径source处的文件夹移动到路径destination,并返回新位置的绝对路径的字符串。
此处的source和distination有几点需要注意:
1> source和destination可以是文件或文件夹名字
2> source和destinaton指向的路径必须存在,如果destination不存在,分两组情况,一种是目标目录只有一层时,例如'C:\\eggs',此时,如果找不到目录eggs,source中bacon.txt文件文件会被改名为eggs,另一种情况,destination中间目录及构成目的的文件夹不存在,直接报错。
3> move 操作时,如果destination中已经存在一个与source中同名的文件,例如bacon.txt,会被覆盖,因此使用move时需要小心。(实际测试中,不能存在同名文件,否则会报错)
#假设当前目录0s.getcwd() = 'C:\\Learning\\PYTHON\\python-auto\\100-days\\zipfiles'
def movefile():
crrpath = os.getcwd()
if not os.path.exists(os.path.join(crrpath, 'movefile')):
os.makedirs(os.path.join(crrpath, 'movefile'))
#从前面的coly_all中move文件到movefile
src_filename = os.path.join(crrpath, 'copy_all', 'TAR_DIR.txt')
dest_filename = os.path.join(crrpath, 'movefile')
#删除已存在的同名文件
if os.path.exists( os.path.join(crrpath, 'copy_all', 'TAR_DIR.txt') ):
print('%s is exist , now delete it' %(os.path.join(crrpath, 'movefile', 'TAR_DIR.txt')) )
os.unlink(os.path.join(crrpath, 'movefile', 'TAR_DIR.txt'))
shutil.move(src_filename, dest_filename)
print('move %s -->%s' %(src_filename, dest_filename))
print('listdir %s' %(os.listdir(dest_filename)) )
1.4 永久删除文件和文件夹
利用os模块中的函数,可以删除一个文件或一个空文件夹。但利用shutil模块,可以删除一个文件夹及其所有的内容。
1> os.unlink(path)
该函数将删除path处的文件。
2> os.rmdir(path)
该函数将删除path处的空文件夹。
3>shutil.rmtree(path)
该函数将删除path处的文件夹,包括文件夹内的所有文件和文件夹都会被删除。在使用时需要小心。
def rmdirctorys():
#删除上一个函数创建出来的copy_all
crrpath = os.getcwd()
copy_all = 'copy_all'
rm_path = os.path.join(crrpath,'copy_all')
print('listdir %s:' %(rm_path))
print(os.listdir(rm_path))
shutil.rmtree(rm_path)
if os.path.exists(rm_path):
print('after shutil.rmtree, listdir :' )
print(listdir(rm_path))
else:
print('%s is not exist.' %(rm_path))
1.5 使用 send2trash 模块安全地删除
因为python内建的 shutil.rmtree()函数不可恢复地删除文件和文件夹,所以用起来了可能有危险。删除文件和文件夹的更好方法,是使用第三方的send2trash 模块。
def trashfile():
crrpath = os.getcwd()
copy_all = 'copy_one'
trash_path = os.path.join(crrpath,'copy_one')
print('current function--> trashfile')
if os.path.exists(os.path.join(trash_path, 'bacon.txt')):
print('listdir %s:' %(trash_path))
print(os.listdir(trash_path))
send2trash.send2trash(os.path.join(trash_path, 'bacon.txt'))
else:
print('%s is not exist.' %(os.path.join(trash_path, 'bacon.txt')))
return None
print('after trash listdir%s:' %(trash_path))
print(os.listdir(trash_path))
一般来说总是应该使用send2trash.send2trash(path) 函数来删除文件和文件夹。虽然它将文件发送到回收站,让你稍后能回复他们,但是这不想永久删除文件,不会释放磁盘空间。如果你希望释放磁盘空间,就要使用os和shutil来删除文件和文件夹。注意,send2trash()函数只能将文件发送到垃圾箱,不能从中回复文件。
2. 遍历目录树
假定你需要遍历目录树,并处理遇到的每个文件,写程序完成这件事可能需要一些技巧。好在python提供了一个函数,替你处理这个过程。
os.walk()
os.walk()函数传入一个字符串,即一个文件夹的路径,你可以在一个for循环中使用os.walk()函数,遍历目录树,就像使用range()函数遍历一个范围内的数字一样。与range()不同,os.walk()函数在循环的每次迭代中返回三个值:
1)当前遍历的文件夹的名称字符串【当前处理目录】
2)当前文件夹中子文件夹的字符串列表
3)当前文件夹中文件的字符串列表
当前文件夹是指for循环当前迭代的文件夹,程序的当前工作目录,不会因为os.walk()而改变。
因为os.walk()返回字符串列表,保存在subfolder()和filename()变量中,所以你可以在他们自己的ofr循环中使用这些列表。用你自己定制的代码,取代print()函数调用,或者如果不需要,就删除for循环。
def walkdirs():
crrpath = os.getcwd()
for folderName, subfolders, fileNames in os.walk(crrpath):
print('THe current folder is' + folderName)
for subfd in subfolders:
print('SUBFOLDER OF' +folderName +':' + subfd)
for file in fileNames:
print('FILE INSIDE '+ folderName + file)
THe current folder isC:\Learning\PYTHON\python-auto\100-days\zipfiles
SUBFOLDER OFC:\Learning\PYTHON\python-auto\100-days\zipfiles:copy_all
SUBFOLDER OFC:\Learning\PYTHON\python-auto\100-days\zipfiles:copy_one
SUBFOLDER OFC:\Learning\PYTHON\python-auto\100-days\zipfiles:forlder
SUBFOLDER OFC:\Learning\PYTHON\python-auto\100-days\zipfiles:movefile
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfilestar.txt
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfilesTAR_DIR.txt
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfileszipfile.py
THe current folder isC:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all
SUBFOLDER OFC:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all:copy_one
SUBFOLDER OFC:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all:forlder
SUBFOLDER OFC:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all:movefile
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_alltar.txt
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_allzipfile.py
THe current folder isC:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all\copy_one
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all\copy_onetar.txt
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all\copy_oneTAR_DIR.txt
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all\copy_onezipfile.py
THe current folder isC:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all\forlder
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all\forlderCHONG_DIR.txt
THe current folder isC:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all\movefile
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_all\movefileTAR_DIR.txt
THe current folder isC:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_one
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_onetar.txt
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_oneTAR_DIR.txt
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\copy_onezipfile.py
THe current folder isC:\Learning\PYTHON\python-auto\100-days\zipfiles\forlder
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\forlderCHONG_DIR.txt
THe current folder isC:\Learning\PYTHON\python-auto\100-days\zipfiles\movefile
FILE INSIDE C:\Learning\PYTHON\python-auto\100-days\zipfiles\movefileTAR_DIR.txt
3. 用zipfile模块压缩文件
利用zipfile模块中的函数,Python程序可以创建和打开ZIP文件。
3.1 读取ZIP文件
要读取zip文件,首先必须创建一个ZipFIle对象(注意,大写首字母Z和F)。ZipFile 对此昂在概念上与File对象相似,在打开文件时open()函数返回File对象,他们是一些值,程序通过他们来操作文件。
要创建一个ZipFile对象,就要调用 zipfile.ZipFile()函数。
zipfile.ZipFile(filename)
该函数创建一个ZipFile对象。传入一个字符串,表示.zip文件的文件名。
def zipfiles_read():
crrpath = os.getcwd()
example = 'example.zip'
exampleZip = zipfile.ZipFile(os.path.join(crrpath, example))
print('read zip file %s.' %(os.path.join(crrpath, example)))
for filename in exampleZip.namelist():
fileinfo = exampleZip.getinfo(filename)
print('%s file_size = %d , compress_size =%d.' %(filename, fileinfo.file_size, fileinfo.compress_size))
#使用zip专用的打印目录
print('USE ZIPPRINT----------------')
exampleZip.printdir()
exampleZip.close()
3.2 从zip文件中解压缩
ZipFIle对象的extractall()方法从ZIP 文件中解压缩所有文件和文件夹,放到当前工作目录中。可以向extractall()传递一个文件夹名称,它将解压缩到该文件夹,而不是当前目录,如果该文件夹不存在,它会被创建。
def zipfile_extract():
crrpath = os.getcwd()
example = 'example.zip'
exampleZip = zipfile.ZipFile(os.path.join(crrpath, example))
exampleZip.extractall(os.path.join(crrpath, 'example'))
if os.path.exists(os.path.join(crrpath, 'example')) and os.path.isdir(os.path.join(crrpath, 'example')):
print('after zipfile proc example listdir is:')
print(os.listdir(os.path.join(crrpath, 'example')))
exampleZip.close()
3.3 创建和添加到ZIP文件
要创建你自己的压缩ZIP文件,必须以“写模式”打开ZipFile对象,即传图 'w'作为第二个参数。(这类似于向open()函数传入'w',以写模式打开一个文本文件)。如果向ZipFile对象的write()方法传入一个路径,Python就会压缩该路径所指的文件,将他们加到ZIP文件中,write()方法的第一个参数是一个字符串,代表要添加的文件名。第二个参数是“压缩类型”参数,它告诉计算机使用怎样的算法来压缩文件。可以总是将这个值设置为 zipfile.ZIP_DEFLATED(这指定了deflated压缩算法,它对各种类型的数据都很有效)。
def zipfile_zipdeflated():
newzip = zipfile.ZipFile('currdir.zip', 'w')
newzip.write(os.getcwd(), compress_type = zipfile.ZIP_DEFLATED)
newzip.close()
注意,write压缩不能压缩整个文件夹的内容,一般只限于压缩单个文件。如果需要压缩文件夹,可以考虑配合使用os.walk()。将会在下文项目中介绍文件夹的压缩。
如果只是希望将文件添加到原有的ZIP文件中,就要向zipfile.ZipFile()传入'a'作为第二个参数,以添加模式打开zip文件。
4. 项目: 将带有美国日期风格的文件改名为欧洲风格日期
假定你需要将上千个包含美国日期风格(MM-DD-YYYY)的文件修改为欧洲风格的日期(DD-MM-YYYY)。
下面是程序要做的事:
*从工作目录查找美国日期风格的文件名
*如果找到,将改文件改名,交换月份和日期位置,使之成为欧洲风格
这意味着代码要做下面的事情:
1> 为美国日期风格的日期创建一个正则表达式
datePattern = re.compile(r'''^(.*?) #任意字符作为开头,^(1)
( (0|1)?\d )- #一个或两个数字作为月份,加短横线 (2(3))-
( (0|1|2|3)?\d )- #一个或两个数字作为天,分组4和5 (4(5))-
( (19|20)\d\d ) #四个数字表示年 表示二十世纪或二十一世纪分组6和7 (6(7))
(.*?)$ #日期后是任意字符结束(8)$
''',re.VERBOSE|re.DOTALL|re.I)
正则表达式以^(.*?)开始,匹配文件名开始处,日期出现之前的任何文本。(0|1?\d)分组匹配月份。第一个数字可以是0或1,所以正则表达式匹配12,作为十二月份,也会陪陪02,作为二月份。这个数字也是可选的,所以四月份可以使04或4.日期的分组是((0|1|2|3)?\d),它遵循类似的逻辑,3,03he31是有效的日期数字(是的,这个正则表达式会接受一些无效的日期,如4-31-2019、2-29-2018和0-15-1998,日期有很多特例,很容易被遗漏,为了简单,这个程序中的正则表达式已经足够好了。)正则表达式的(.*?)$部分,将匹配日期之后的任何文本。
2>识别文件名中的日期部分
接下来,程序要循环遍历os.listdir(path)返回的文件名字符串列表,用这个正则表达式匹配这些字符串。文件名不包含日期的文件将被忽略。如果文件名包含日期,匹配的文本将保存在几个变量中。其实此处只是交换日期中的月份和天数,可以使用正则表达式中的re.sub(r'',\分组...)替换
3> 组成新文件名
def Amaricandate():
datePattern = re.compile(r'''^(.*?) #任意字符作为开头,^(1)
( (0|1)?\d )- #一个或两个数字作为月份,加短横线 (2(3))-
( (0|1|2|3)?\d )- #一个或两个数字作为天,分组4和5 (4(5))-
( (19|20)\d\d ) #四个数字表示年 表示二十世纪或二十一世纪分组6和7 (6(7))
(.*?)$ #日期后是任意字符结束(8)$
''',re.VERBOSE|re.DOTALL|re.I)
#先生存测试文件
os.chdir(os.path.join(os.getcwd(), 'rename'))
for i in range(1,21):
print('create file: %s' %('abc_'+str(i)+'-07-2019'+'xyz'+str(i)) )
if os.path.exists('abc_'+str(i)+'-07-2019'+'xyz'+str(i)):
os.unlink('abc_'+str(i)+'-07-2019'+'xyz'+str(i))
fid = open('abc_'+str(i)+'-07-2019'+'xyz'+str(i), 'w')
fid.write('abc_'+str(i)+'-07-2019'+'xyz'+str(i))
fid.close()
for orgfilename in os.listdir(os.getcwd()):
#re.sub(r'\1\4-\2-\6\8', orgfilename) 可以用sub分组替换代替局部变量,此法在匹配时测试当循环大于20时日期获取不对
mt = datePattern.search(orgfilename)
if mt == None:
continue
beforepath = mt.group(1)
monthpart = mt.group(2)
daypart = mt.group(4)
yearpart = mt.group(6)
afterpart = mt.group(8)
reorgfilename= beforepath + daypart+'-'+monthpart+'-'+yearpart +afterpart
absdir = os.path.abspath('.')
orgfilename = os.path.join(absdir, orgfilename)
reorgfilename = os.path.join(absdir, reorgfilename)
print('renaming %s to %s...' %(orgfilename, reorgfilename))
shutil.move(orgfilename, reorgfilename)
5. 项目:将一个文件夹备份到一个ZIP文件
以下代码将一个文件夹压缩到一个ZIP文件做备份。分以下几个步骤:
1> 确定zip文件的名称,每次程序运行前zip文件的名称不能重复,即不能覆盖
2>创建新的zip文件
3> 遍历目录并添加到zip文件
以下函数可以分两种模式对目录进行压缩,传入一个存在的目录,并指定压缩方式为绝对路径压缩还是相对路径压缩,如果按照绝对路径压缩,那么解压开路径会变得很繁琐,相对路径压缩解压后比较好看。
def bakdirByzip(folder, isAbs):
folder = os.path.abspath(folder) #make sure folder is abslote
number = 1
while True:
zipFilename = os.path.basename(folder) + '_' + str(number) +'.zip'
#basename是最后一个斜杠后的字符串,dirname是最后一个斜杠钱的字符串
if not os.path.exists(zipFilename):
break
number += 1
#create zipfile
print('Creating %s...' %(zipFilename))
backupzip = zipfile.ZipFile(zipFilename, 'w')
#list all dir and zip to zipfile
#walk the entire folder tree and compress the files in each folder
for foldeName, subfolders, fileNames in os.walk(folder):
print('Adding files in %s...' %(foldeName))
#Add the current folder to the ZIP file
if True == isAbs:
backupzip.write(foldeName)
else:
#相对路径压缩,而不是绝对路径
fpath = foldeName.replace(folder, '')
#下面这句很必要,需要理解,如果当前目录替换了根目录就是空值‘’,那么说明当前目录为一级或最外围目录,
#如果不是'',说明当前目录是下一层子目录,则对子目录添加系统的分隔符
#如果不加这一句,将会把所有文件压缩到根目录下
fpath = fpath and fpath + os.sep or ''
#python里面,0、’’、[]、()、{}、None为假,其它任何东西都为真
# (对于or,如果没有真值,返回的是最后一个假值,如果有真值,则返回的是第一个真值。)
# (对于and,如果没有假值,返回的是最后一个真值,如果有假值,则返回的是第一个假值。)
#Add all the files in this folder to the ZIP file
for filename in fileNames:
newBase = os.path.basename(folder) + '_'
if filename.startswith(newBase) and filename.endswith('.zip'):
continue #不备份之前的备份压缩文件
if True == isAbs:
#以绝对路径压缩
backupzip.write(os.path.join(foldeName, filename))
#backupzip.write(os.sep.join([foldeName, filename]))
else:
#相对路径压缩而不是绝对路径
backupzip.write(os.path.join(foldeName, filename),fpath+filename)
#backupzip.write(os.sep.join([foldeName, filename]),fpath+filename) #os.sep 操作系统文件夹分割符 /或\\
backupzip.close()
print('Done.')
调用方法:
bakdirByzip(abspath, False)