Python编程-让繁琐的工作自动化(九)组织文件

目录

1.shutil模块

1.1 shutil.copy(source,destination)

1.2 shutil.copytree(source, destination)

1.3 文件的移动和改名

1.4 永久删除文件和文件夹

1.5 使用 send2trash 模块安全地删除

2. 遍历目录树

3. 用zipfile模块压缩文件

3.1 读取ZIP文件

3.2 从zip文件中解压缩

3.3 创建和添加到ZIP文件

4. 项目: 将带有美国日期风格的文件改名为欧洲风格日期

5. 项目:将一个文件夹备份到一个ZIP文件


本章的内容包括文件复制,移动,改名,压缩等。

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)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值