11. 组织文件

1. shutil 模块

shutil(或称为shell 工具)模块中包含一些函数,让你在Python 程序中复制、移动、改名和删除文件。要使用shutil 
的函数,首先需要import shutil。

1.1 复制文件和文件夹

  调用shutil.copy(source, destination),将路径source 处的文件复制到路径destination处的文件夹(source 和
destination 都是字符串)。如果destination 是一个文件名,它将作为被复制文件的新名字。该函数返回一个字符串,
表示被复制文件的路径。
import shutil, os
os.chdir('D:\\')    #os.chdir()改变当前目录
shutil.copy('D://demo1.txt', 'D:\\Jupyter Notebook\\demo2.txt')
  'D:\\Jupyter Notebook\\demo2.txt'
shutil.copytree('D:\\demopack1', 'D:\\Jupyter Notebook\\demopack2')
  'D:\\Jupyter Notebook\\demopack2'
  第一个shutil.copy()调用将文件D://demo1.txt复制到文件夹D:\\Jupyter Notebook。返回值是刚刚被复制的文件的路径。请注意,
因为指定了一个文件夹作为目的地址,原来的文件名demo1.txt 就被用作新复制的文件名。第二个shutil.copy()调用也
将文件D://demo1.txt 复制到文件夹D:\\Jupyter Notebook,但为新文件提供了一个名字demo2.txt。
  shutil.copy()将复制一个文件,shutil.copytree()将复制整个文件夹,以及它包含的文件夹和文件。调用
shutil.copytree(source, destination),将路径source 处的文件夹,包括它的所有文件和子文件夹,复制到路径
destination 处的文件夹。source 和destination 参数都是字符串。该函数返回一个字符串,是新复制的文件夹的路径。
shutil.copytree('D:\\demopack1', 'D:\\Jupyter Notebook\\demopack2')
  'D:\\Jupyter Notebook\\demopack2'

1.2 文件和文件夹的移动和改名

  调用shutil.move(source, destination),将路径source 处的文件夹移动到路径destination,并返回新位置的绝对路径
的字符串。
  如果destination 指向一个文件夹,source 文件将移动到destination 中,并保持原来的文件名。
shutil.move('D://demo1.txt', 'D:\\Jupyter Notebook\\demopack2')
  'D:\\Jupyter Notebook\\demopack2\\demo1.txt'
如果目的文件夹中已存在了该文件, 则它会被覆盖。因为这种方式很容易不小心覆盖文件, 所以使用move()时应注意。
destination路径也可以指定一个文件名, source文件被移动并改名.
shutil.move('D://demo1.txt', 'D:\\Jupyter Notebook\\new_demo1.txt')
  'D:\\Jupyter Notebook\\new_demo1.txt'
前面的例子都是目的文件夹已存在的情况,如果目的文件夹不存在的话,move()就会将bacon.txt 改名,变成名为eggs 的文件。
>>> shutil.move('C:\\bacon.txt', 'C:\\eggs')
'C:\\eggs'
  这里,move()在C:\目录下找不到名为eggs 的文件夹,所以假定destination 指的是一个文件,而非文件夹。所以
bacon.txt 文本文件被改名为eggs(没有.txt 文件扩展名的文本文件),但这可能不是你所希望的!这可能是程序中
很难发现的缺陷,因为move()调用会很开心地做一些事情,但和你所期望的完全不同。这也是在使用move()时要小
心的另一个理由。
  最后,构成目的地的文件夹必须已经存在,否则Python 会抛出异常。

1.3 永久删除文件和文件夹

  利用os 模块中的函数,可以删除一个文件或一个空文件夹。但利用shutil 模块,可以删除一个文件夹及其所有的内容。
• 用os.unlink(path)将删除path 处的文件。
• 调用os.rmdir(path)将删除path 处的文件夹。该文件夹必须为空,其中没有任何文件和文件夹。
• 调用shutil.rmtree(path)将删除path 处的文件夹,它包含的所有文件和文件夹都会被删除。
  在程序中使用这些函数时要小心!可以第一次运行程序时,注释掉这些调用,并且加上print()调用,显示会被删除
的文件。这样做是一个好主意。下面有一个Python 程序,本来打算删除具有.txt 扩展名的文件,但有一处录入错误
,结果导致它删除了.rxt 文件。
import os
for filename in os.listdir():
    if filename.endswith('.rxt'):
    os.unlink(filename)
如果你有某些重要的文件以.rxt 结尾,它们就会被不小心永久地删除。作为替代,你应该先运行像这样的程序:
import os
for filename in os.listdir():
    if filename.endswith('.rxt'):
    #os.unlink(filename)
    print(filename)
  现在os.unlink()调用被注释掉,所以Python 会忽略它。作为替代,你会打印出将被删除的文件名。先运行这个版
本的程序,你就会知道,你不小心告诉程序要删除.rxt 文件,而不是.txt 文件。
  在确定程序按照你的意图工作后, 删除print(filename) 代码行, 取消os.unlink(filename)代码行的注释。然后再次
运行该程序,实际删除这些文件。

1.4 用send2trash 模块安全地删除

  因为Python 内建的shutil.rmtree()函数不可恢复地删除文件和文件夹,所以用起来可能有危险。删除文件和文件夹
的更好方法,是使用第三方的send2trash 模块。
  利用send2trash,比Python 常规的删除函数要安全得多,因为它会将文件夹和文件发送到计算机的垃圾箱或回收
站,而不是永久删除它们。如果因程序缺陷而用send2trash 删除了某些你不想删除的东西,稍后可以从垃圾箱恢复。

2. 遍历目录树

  假定你希望对某个文件夹中的所有文件改名,包括该文件夹中所有子文件夹中的所有文件。也就是说,你希望遍
历目录树,处理遇到的每个文件。写程序完成这件事,可能需要一些技巧。好在,Python 提供了一个函数,替你
处理这个过程。
for folderName, subfolders, filenames in os.walk('D:\pywork\TXyiqing'):
    print('The current folder is ' + folderName)
    for subfolder in subfolders:
        print('SUBFOLDER OF ' + folderName + ': ' + subfolder)
    for filename in filenames:
        print('FILE INSIDE ' + folderName + ': ' + filename)
print(' ')
The current folder is D:\pywork\TXyiqing
SUBFOLDER OF D:\pywork\TXyiqing: .idea
SUBFOLDER OF D:\pywork\TXyiqing: Include
SUBFOLDER OF D:\pywork\TXyiqing: Lib
SUBFOLDER OF D:\pywork\TXyiqing: Scripts
FILE INSIDE D:\pywork\TXyiqing: pyvenv.cfg
FILE INSIDE D:\pywork\TXyiqing: spider.py
FILE INSIDE D:\pywork\TXyiqing: SQLdb.py
The current folder is D:\pywork\TXyiqing\.idea
SUBFOLDER OF D:\pywork\TXyiqing\.idea: dictionaries
SUBFOLDER OF D:\pywork\TXyiqing\.idea: inspectionProfiles
FILE INSIDE D:\pywork\TXyiqing\.idea: misc.xml
FILE INSIDE D:\pywork\TXyiqing\.idea: modules.xml
FILE INSIDE D:\pywork\TXyiqing\.idea: TXyiqing.iml
FILE INSIDE D:\pywork\TXyiqing\.idea: workspace.xml
The current folder is D:\pywork\TXyiqing\.idea\dictionaries
FILE INSIDE D:\pywork\TXyiqing\.idea\dictionaries: ChaosA.xml
The current folder is D:\pywork\TXyiqing\.idea\inspectionProfiles
FILE INSIDE D:\pywork\TXyiqing\.idea\inspectionProfiles: Project_Default.xml
os.walk()函数被传入一个字符串值,即一个文件夹的路径。你可以在一个for
循环语句中使用os.walk()函数,遍历目录树,就像使用range()函数遍历一个范围的
数字一样。不像range(),os.walk()在循环的每次迭代中,返回3 个值:
1.当前文件夹名称的字符串。
2.当前文件夹中子文件夹的字符串的列表。
3.当前文件夹中文件的字符串的列表。
所谓当前文件夹,是指for 循环当前迭代的文件夹。程序的当前工作目录,不会因为os.walk()而改变。就像你可以
在代码for i in range(10):中选择变量名称i 一样,你也可以选择前面列出来的3 个字的变量名称。我通常使用
foldername、subfolders 和filenames。

3. 用zipfile 模块压缩文件

3.1 读取ZIP文件

  要读取ZIP 文件的内容,首先必须创建一个ZipFile 对象(请注意大写首字母Z和F)。ZipFile 对象在概念上与File 
对象相似,你在第8 章中曾经看到open()函数返回File 对象:它们是一些值,程序通过它们与文件打交道。要创建
一个ZipFile对象,就调用zipfile.ZipFile()函数,向它传入一个字符串,表示.zip 文件的文件名。
请注意,zipfile 是Python 模块的名称,ZipFile()是函数的名称。
import zipfile, os
os.chdir('D:\\')
exampleZip = zipfile.ZipFile('example.zip')
exampleZip.namelist()
  ['cats/catnames.txt', 'cats/zophie.jpg', 'spam.txt', 'cats/']
spamInfo = exampleZip.getinfo('spam.txt')
spamInfo.file_size
  0
spamInfo.compress_size
  0
  ZipFile 对象有一个namelist()方法,返回ZIP 文件中包含的所有文件和文件夹的字符串的列表。这些字符串可以传
递给ZipFile 对象的getinfo()方法,返回一个关于特定文件的ZipInfo 对象。ZipInfo 对象有自己的属性,诸如表示字
节数的file_size和compress_size,它们分别表示原来文件大小和压缩后文件大小。ZipFile 对象表示整个归档文
件,而ZipInfo 对象则保存该归档文件中每个文件的有用信息。

3.2 从ZIP中解压缩

ZipFile 对象的extractall()方法从ZIP 文件中解压缩所有文件和文件夹,放到当前工作目录中。
import zipfile, os
os.chdir('D:\\')
exampleZip = zipfile.ZipFile('example.zip')
exampleZip.extractall()
exampleZip.close()
  运行这段代码后, example.zip的内容被解压到D:\。或者,你可以想extractall()传递一个文件夹名称, 它将解压
缩到那个文件夹,而不是当前工作目录。如果传递给extractall()方法的文件夹不存在, 它会被创建。
ZipFile对象的extract()方法从ZIP文件中解压缩单个文件
import zipfile, os
os.chdir('D:\\')
exampleZip = zipfile.ZipFile('example.zip')
exampleZip.extract('spam.txt')
  'D:\\spam.txt'
exampleZip.extract('spam.txt', 'D:\Jupyter Notebook')
  'D:\\Jupyter Notebook\\spam.txt'
exampleZip.close()
传递给extract()的字符串,必须匹配namelist()返回的字符串列表中的一个。或者,你可以向extract()传递第二个参
数,将文件解压缩到指定的文件夹,而不是当前工作目录。如果第二个参数指定的文件夹不存在,Python 就会创
建它。extract()的返回值是被压缩后文件的绝对路径。

3.3 创建和添加ZIP文件

  要创建你自己的压缩ZIP 文件,必须以“写模式”打开ZipFile 对象,即传入'w'作为第二个参数(这类似于向open()
函数传入'w',以写模式打开一个文本文件)。如果向ZipFile 对象的write()方法传入一个路径,Python 就会压缩该
路径所指的文件,将它加到ZIP 文件中。write()方法的第一个参数是一个字符串,代表要添加的文件名。第二个参
数是“压缩类型”参数,它告诉计算机使用怎样的算法来压缩文件。可以总是将这个值设置为    
zipfile.ZIP_DEFLATED(这指定了deflate 压缩算法,它对各种类型的数据都很有效)
newZip = zipfile.ZipFile('new.zip', 'w')
newZip.write('spam.txt', compress_type = zipfile.ZIP_DEFLATED)
newZip.close()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354