Python中有几个内置模块和方法来处理文件。这些方法被分割到例如os,os。path,shutil和pathlib等等几个模块中。文章将列举Python中对文件最常用的操作和方法。 在这篇文章中,你将学习如何:获取文件属性创建目录文件名模式匹配遍历目录树创建临时文件和目录删除文件和目录复制、移动和重命名文件和目录创建和解压ZIP和TAR档案使用fileinput模块打开多个文件Python中文件数据的读和写 使用Python对文件进行读和写是十分简单的。为此,你首先必须使用合适的模式打开文件。这里有一个如何打开文本文件并读取其内容的例子。withopen(data。txt,r)asf:dataf。read()print(context:{}。format(data))复制代码 open()接收一个文件名和一个模式作为它的参数,r表示以只读模式打开文件。想要往文件中写数据的话,则用w作为参数。withopen(data。txt,w)asf:datasomedatatobewrittentothefilef。write(data)复制代码 在上述例子中,open()打开用于读取或写入的文件并返回文件句柄(本例子中的f),该句柄提供了可用于读取或写入文件数据的方法。阅读WorkingWithFileIOinPython获取更多关于如何读写文件的信息。获取目录列表 假设你当前的工作目录有一个叫mydirectory的子目录,该目录包含如下内容:。file1。pyfile2。csvfile3。txtsubdirbar。pyfoo。pysubdirbfile4。txtsubdircconfig。pyfile5。txt复制代码 Python内置的os模块有很多有用的方法能被用来列出目录内容和过滤结果。为了获取文件系统中特定目录的所有文件和文件夹列表,可以在遗留版本的Python中使用os。listdir()或在Python3。x中使用os。scandir()。如果你还想获取文件和目录属性(如文件大小和修改日期),那么os。scandir()则是首选的方法。使用遗留版本的Python获取目录列表importosentriesos。listdir(mydirectory)复制代码 os。listdir()返回一个Python列表,其中包含path参数所指目录的文件和子目录的名称。〔file1。py,file2。csv,file3。txt,subdir,subdirb,subdirc〕复制代码 目录列表现在看上去不容易阅读,对os。listdir()的调用结果使用循环打印有助于查看。forentryinentries:print(entry)file1。pyfile2。csvfile3。txtsubdirsubdirbsubdirc复制代码使用现代版本的Python获取目录列表 在现代Python版本中,可以使用os。scandir()和pathlib。Path来替代os。listdir()。 os。scandir()在Python3。5中被引用,其文档为PEP471。 os。scandir()调用时返回一个迭代器而不是一个列表。importosentriesos。scandir(mydirectory)print(entries)posix。ScandirIteratorat0x105b4d4b0复制代码 ScandirIterator指向了当前目录中的所有条目。你可以遍历迭代器的内容,并打印文件名。importoswithos。scandir(mydirectory)asentries:forentryinentries:print(entry。name)复制代码 这里os。scandir()和with语句一起使用,因为它支持上下文管理协议。使用上下文管理器关闭迭代器并在迭代器耗尽后自动释放获取的资源。在mydirectory打印文件名的结果就和在os。listdir()例子中看到的一样:file1。pyfile2。csvfile3。txtsubdirsubdirbsubdirc复制代码 另一个获取目录列表的方法是使用pathlib模块:frompathlibimportPathentriesPath(mydirectory)forentryinentries。iterdir():print(entry。name)复制代码 pathlib。Path()返回的是PosixPath或WindowsPath对象,这取决于操作系统。 pathlib。Path()对象有一个。iterdir()的方法用于创建一个迭代器包含该目录下所有文件和目录。由。iterdir()生成的每个条目都包含文件或目录的信息,例如其名称和文件属性。pathlib在Python3。4时被第一次引入,并且是对Python一个很好的加强,它为文件系统提供了面向对象的接口。 在上面的例子中,你调用pathlib。Path()并传入了一个路径参数。然后调用。iterdir()来获取mydirectory下的所有文件和目录列表。 pathlib提供了一组类,以简单并且面向对象的方式提供了路径上的大多数常见的操作。使用pathlib比起使用os中的函数更加有效。和os相比,使用pathlib的另一个好处是减少了操作文件系统路径所导入包或模块的数量。想要了解更多信息,可以阅读Python3’spathlibModule:TamingtheFileSystem。 运行上述代码会得到如下结果:file1。pyfile2。csvfile3。txtsubdirsubdirbsubdirc复制代码 使用pathlib。Path()或os。scandir()来替代os。listdir()是获取目录列表的首选方法,尤其是当你需要获取文件类型和文件属性信息的时候。pathlib。Path()提供了在os和shutil中大部分处理文件和路径的功能,并且它的方法比这些模块更加有效。我们将讨论如何快速的获取文件属性。 函数 描述 os。listdir() 以列表的方式返回目录中所有的文件和文件夹 os。scandir() 返回一个迭代器包含目录中所有的对象,对象包含文件属性信息 pathlib。Path()。iterdir() 返回一个迭代器包含目录中所有的对象,对象包含文件属性信息 这些函数返回目录中所有内容的列表,包括子目录。这可能并总是你一直想要的结果,下一节将向你展示如何从目录列表中过滤结果。列出目录中的所有文件 这节将向你展示如何使用os。listdir(),os。scandir()和pathlib。Path()打印出目录中文件的名称。为了过滤目录并仅列出os。listdir()生成的目录列表的文件,要使用os。path:importosbasepathmydirectoryforentryinos。listdir(basepath):使用os。path。isfile判断该路径是否是文件类型ifos。path。isfile(os。path。join(basepath,entry)):print(entry)复制代码 在这里调用os。listdir()返回指定路径中所有内容的列表,接着使用os。path。isfile()过滤列表让其只显示文件类型而非目录类型。代码执行结果如下:file1。pyfile2。csvfile3。txt复制代码 一个更简单的方式来列出一个目录中所有的文件是使用os。scandir()或pathlib。Path():importosbasepathmydirectorywithos。scandir(basepath)asentries:forentryinentries:ifentry。isfile():print(entry。name)复制代码 使用os。scandir()比起os。listdir()看上去更清楚和更容易理解。对ScandirIterator的每一项调用entry。isfile(),如果返回True则表示这一项是一个文件。上述代码的输出如下:file1。pyfile3。txtfile2。csv复制代码 接着,展示如何使用pathlib。Path()列出一个目录中的文件:frompathlibimportPathbasepathPath(mydirectory)forentryinbasepath。iterdir():ifentry。isfile():print(entry。name)复制代码 在。iterdir()产生的每一项调用。isfile()。产生的输出结果和上面相同:file1。pyfile3。txtfile2。csv复制代码 如果将for循环和if语句组合成单个生成器表达式,则上述的代码可以更加简洁。关于生成器表达式,推荐一篇DanBader的文章。 修改后的版本如下:frompathlibimportPathbasepathPath(mydirectory)filesinbasepath(entryforentryinbasepath。iterdir()ifentry。isfile())foriteminfilesinbasepath:print(item。name)复制代码 上述代码的执行结果和之前相同。本节展示使用os。scandir()和pathlib。Path()过滤文件或目录比使用os。listdir()和os。path更直观,代码看起来更简洁。列出子目录 如果要列出子目录而不是文件,请使用下面的方法。现在展示如何使用os。listdir()和os。path():importosbasepathmydirectoryforentryinos。listdir(basepath):ifos。path。isdir(os。path。join(basepath,entry)):print(entry)复制代码 当你多次调用os。path,join()时,以这种方式操作文件系统就会变得很笨重。在我电脑上运行此代码会产生以下输出:subdirsubdirbsubdirc复制代码 下面是如何使用os。scandir():importosbasepathmydirectorywithos。scandir(basepath)asentries:forentryinentries:ifentry。isdir():print(entry。name)复制代码 与文件列表中的示例一样,此处在os。scandir()返回的每一项上调用。isdir()。如果这项是目录,则isdir()返回True,并打印出目录的名称。输出结果和上面相同:subdircsubdirbsubdir复制代码 下面是如何使用pathlib。Path():frompathlibimportPathbasepathPath(mydirectory)forentryinbasepath。iterdir():ifentry。isdir():print(entry。name)复制代码 在。iterdir()迭代器返回的每一项上调用isdir()检查是文件还是目录。如果该项是目录,则打印其名称,并且生成的输出与上一示例中的输出相同:subdircsubdirbsubdir复制代码获取文件属性 Python可以很轻松的获取文件大小和修改时间等文件属性。可以通过使用os。stat(),os。scandir()或pathlib。Path来获取。 os。scandir()和pathlib。Path()能直接获取到包含文件属性的目录列表。这可能比使用os。listdir()列出文件然后获取每个文件的文件属性信息更加有效。 下面的例子显示了如何获取mydirectory中文件的最后修改时间。以时间戳的方式输出:importoswithos。scandir(mydirectory)asentries:forentryinentries:infoentry。stat()print(info。stmtime)1548163662。39526651548163689。19820621548163697。91759041548163721。18410281548163740。7651621548163769。4702623复制代码 os。scandir()返回一个ScandirIterator对象。ScandirIterator对象中的每一项有。stat()方法能获取关于它指向文件或目录的信息。。stat()提供了例如文件大小和最后修改时间的信息。在上面的示例中,代码打印了sttime属性,该属性是上次修改文件内容的时间。 pathlib模块具有相应的方法,用于获取相同结果的文件信息:frompathlibimportPathbasepathPath(mydirectory)forentryinbasepath。iterdir():infoentry。stat()print(info。stmtime)1548163662。39526651548163689。19820621548163697。91759041548163721。18410281548163740。7651621548163769。4702623复制代码 在上面的例子中,循环。iterdir()返回的迭代器并通过对其中每一项调用。stat()来获取文件属性。stmtime属性是一个浮点类型的值,表示的是时间戳。为了让sttime返回的值更容易阅读,你可以编写一个辅助函数将其转换为一个datetime对象:importdatetimefrompathlibimportPathdeftimestamp2datetime(timestamp,converttolocalTrue,utc8,isremovemsTrue)转换UNIX时间戳为datetime对象:paramtimestamp:时间戳:paramconverttolocal:是否转为本地时间:paramutc:时区信息,中国为utc8:paramisremovems:是否去除毫秒:return:datetime对象ifisremovems:timestampint(timestamp)dtdatetime。datetime。utcfromtimestamp(timestamp)ifconverttolocal:dtdtdatetime。timedelta(hoursutc)returndtdefconvertdate(timestamp,formatYmdH:M:S):dttimestamp2datetime(timestamp)returndt。strftime(format)basepathPath(mydirectory)forentryinbasepath。iterdir():ifentry。isfile()infoentry。stat()print({}上次修改时间为{}。format(entry。name,timestamp2datetime(info。stmtime)))复制代码 首先得到mydirectory中文件的列表以及它们的属性,然后调用convertdate()来转换文件最后修改时间让其以一种人类可读的方式显示。convertdate()使用。strftime()将datetime类型转换为字符串。 上述代码的输出结果:file3。txt上次修改时间为2019012409:04:39file2。csv上次修改时间为2019012409:04:39file1。py上次修改时间为2019012409:04:39复制代码 将日期和时间转换为字符串的语法可能会让你感到混乱。如果要了解更多的信息,请查询相关的官方文档。另一个方式则是阅读strftime。org。创建目录 你编写的程序迟早需要创建目录以便在其中存储数据。os和pathlib包含了创建目录的函数。我们将会考虑如下方法: 方法 描述 os。mkdir() 创建单个子目录 os。makedirs() 创建多个目录,包括中间目录 Pathlib。Path。mkdir() 创建单个或多个目录创建单个目录 要创建单个目录,把目录路径作为参数传给os。mkdir():importosos。mkdir(exampledirectory)复制代码 如果该目录已经存在,os。mkdir()将抛出FileExistsError异常。或者,你也可以使用pathlib来创建目录:frompathlibimportPathpPath(exampledirectory)p。mkdir()复制代码 如果路径已经存在,mkdir()会抛出FileExistsError异常:FileExistsError:〔Errno17〕Fileexists:exampledirectory复制代码 为了避免像这样的错误抛出,当发生错误时捕获错误并让你的用户知道:frompathlibimportPathpPath(exampledirectory)try:p。mkdir()exceptFileExistsErrorase:print(e)复制代码 或者,你可以给。mkdir()传入existokTrue参数来忽略FileExistsError异常:frompathlibimportPathpPath(exampledirectory)p。mkdir(existokTrue)复制代码 如果目录已存在,则不会引起错误。创建多个目录 os。makedirs()和os。mkdir()类似。两者之间的区别在于,os。makedirs()不仅可以创建单独的目录,还可以递归的创建目录树。换句话说,它可以创建任何必要的中间文件夹,来确保存在完整的路径。 os。makedirs()和在bash中运行mkdirp类似。例如,要创建一组目录像20181005,你可以像下面那样操作:importosos。makedirs(20181005,mode0o770)复制代码 上述代码创建了20181005的目录结构并为所有者和组用户提供读、写和执行权限。默认的模式为0o777,增加了其他用户组的权限。有关文件权限以及模式的应用方式的更多详细信息,请参考文档。 运行tree命令确认我们应用的权限:treepi。。〔drwxrwx〕2018〔drwxrwx〕10〔drwxrwx〕05复制代码 上述代码打印出当前目录的目录树。tree通常被用来以树形结构列出目录的内容。传入p和i参数则会以垂直列表打印出目录名称以及其文件权限信息。p用于输出文件权限,i则用于让tree命令产生一个没有缩进线的垂直列表。 正如你所看到的,所有的目录都拥有770权限。另一个方式创建多个目录是使用pathlib。Path的。mkdir():frompathlibimportPathpPath(20181005)p。mkdir(parentsTrue,existokTrue)复制代码 通过给Path。mkdir()传递parentsTrue关键字参数使它创建05目录和使其路径有效的所有父级目录。 在默认情况下,os。makedirs()和pathlib。Path。mkdir()会在目标目录存在的时候抛出OSError。通过每次调用函数时传递existokTrue作为关键字参数则可以覆盖此行为(从Python3。2开始)。 运行上述代码会得到像下面的结构:20181005复制代码 我更喜欢在创建目录时使用pathlib,因为我可以使用相同的函数方法来创建一个或多个目录。文件名模式匹配 使用上述方法之一获取目录中的文件列表后,你可能希望搜索和特定的模式匹配的文件。 下面这些是你可以使用的方法和函数:endswith()和startswith()字符串方法fnmatch。fnmatch()glob。glob()pathlib。Path。glob() 这些方法和函数是下面要讨论的。本小节的示例将在名为somedirectory的目录下执行,该目录具有以下的结构:。admin。pydata01backup。txtdata01。txtdata02backup。txtdata02。txtdata03backup。txtdata03。txtsubdirfile1。pyfile2。pytests。py复制代码 如果你正在使用Bashshell,你可以使用以下的命令创建上述目录结构:mkdirsomedirectorycdsomedirectorymkdirsubdirtouchsubdirfile1。pysubdirfile2。pytouchdata{01。。03}。txtdata{01。。03}backup。txtadmin。pytests。py复制代码 这将会创建somedirectory目录并进入它,接着创建subdir。下一行在subdir创建file1。py和file2。py,最后一行使用扩展创建其它所有文件。想要学习更多关于shell扩展,请阅读这里。使用字符串方法 Python有几个内置修改和操作字符串的方法。当在匹配文件名时,其中的两个方法。startswith()和。endswith()非常有用。要做到这点,首先要获取一个目录列表,然后遍历。importosforfnameinos。listdir(somedirectory):iffname。endswith(。txt):print(fname)复制代码 上述代码找到somedirectory中的所有文件,遍历并使用。endswith()来打印所有扩展名为。txt的文件名。运行代码在我的电脑上输出如下:data01。txtdata01backup。txtdata02。txtdata02backup。txtdata03。txtdata03backup。txt复制代码使用fnmatch进行简单文件名模式匹配 字符串方法匹配的能力是有限的。fnmatch有对于模式匹配有更先进的函数和方法。我们将考虑使用fnmatch。fnmatch(),这是一个支持使用和?等通配符的函数。例如,使用fnmatch查找目录中所有。txt文件,你可以这样做:importosimportfnmatchforfnameinos。listdir(somedirectory):iffnmatch。fnmatch(fname,。txt):print(fname)复制代码 迭代somedirectory中的文件列表,并使用。fnmatch()对扩展名为。txt的文件执行通配符搜索。更先进的模式匹配 假设你想要查找符合特定掉件的。txt文件。例如,你可能指向找到包含单次data的。txt文件,一组下划线之间的数字,以及文件名中包含单词backup。就类似于data01backup,data02backup,或data03backup。 你可以这样使用fnmatch。fnmatch():importosimportfnmatchforfnameinos。listdir(somedirectory):iffnmatch。fnmatch(fname,databackup。txt):print(fname)复制代码 这里就仅仅打印出匹配databackup。txt模式的文件名称。模式中的将匹配任何字符,因此运行这段代码则将查找文件名以data开头并以backup。txt的所有文本文件,就行下面的输出所示:data01backup。txtdata02backup。txtdata03backup。txt复制代码使用glob进行文件名模式匹配 另一个有用的模式匹配模块是glob。 。glob()在glob模块中的左右就像fnmatch。fnmatch(),但是与fnmach。fnmatch()不同的是,它将以。开头的文件视为特殊文件。 UNIX和相关系统在文件列表中使用通配符像?和表示全匹配。 例如,在UNIXshell中使用mv。pypythonfiles移动所有。py扩展名的文件从当前目录到pythonfiles。这是一个通配符表示任意数量的字符,。py是一个全模式。Windows操作系统中不提供此shell功能。但glob模块在Python中添加了此功能,使得Windows程序可以使用这个特性。 这里有一个使用glob模块在当前目录下查询所有Python代码文件:importglobprint(glob。glob(。py))复制代码 glob。glob(。py)搜索当前目录中具有。py扩展名的文件,并且将它们以列表的形式返回。glob还支持shell样式的通配符来进行匹配:importglobfornameinglob。glob(〔09〕。txt):print(name)复制代码 这将找到所有文件名中包含数字的文本文件(。txt):data01。txtdata01backup。txtdata02。txtdata02backup。txtdata03。txtdata03backup。txt复制代码 glob也很容易在子目录中递归的搜索文件:importglobfornameinglob。iglob(。py,recursiveTrue):print(name)复制代码 这里例子使用了glob。iglob()在当前目录和子目录中搜索所有的。py文件。传递recursiveTrue作为。iglob()的参数使其搜索当前目录和子目录中的。py文件。glob。glob()和glob。iglob()不同之处在于,iglob()返回一个迭代器而不是一个列表。 运行上述代码会得到以下结果:admin。pytests。pysubdirfile1。pysubdirfile2。py复制代码 pathlib也包含类似的方法来灵活的获取文件列表。下面的例子展示了你可以使用。Path。glob()列出以字母p开始的文件类型的文件列表。frompathlibimportPathpPath(。)fornameinp。glob(。p):print(name)复制代码 调用p。glob(。p)会返回一个指向当前目录中所有扩展名以字母p开头的文件的生成器对象。 Path。glob()和上面讨论过的os。glob()类似。正如你看到的,pathlib混合了许多os,os。path和glob模块的最佳特性到一个模块中,这使得使用起来很方便。 回顾一下,这是我们在本节中介绍的功能表: 函数 描述 startswith() 测试一个字符串是否以一个特定的模式开始,返回True或False endswith() 测试一个字符串是否以一个特定的模式结束,返回True或False fnmatch。fnmatch(filename,pattern) 测试文件名是否匹配这个模式,返回True或False glob。glob() 返回一个匹配该模式的文件名列表 pathlib。Path。glob() 返回一个匹配该模式的生成器对象遍历目录和处理文件 一个常见的编程任务是遍历目录树并处理目录树中的文件。让我们来探讨一下如何使用内置的Python函数os。walk()来实现这一功能。os。walk()用于通过从上到下或从下到上遍历树来生成目录树中的文件名。处于本节的目的,我们想操作以下的目录树:folder1file1。pyfile2。pyfile3。pyfolder2file4。pyfile5。pyfile6。pytest1。txttest2。txt复制代码 以下是一个示例,演示如何使用os。walk()列出目录树中的所有文件和目录。 os。walk()默认是从上到下遍历目录:importosfordirpath,dirname,filesinos。walk(。):print(fFounddirectory:{dirpath})forfilenameinfiles:print(filename)复制代码 os。walk()在每个循环中返回三个值:当前文件夹的名称当前文件夹中子文件夹的列表当前文件夹中文件的列表 在每次迭代中,会打印出它找到的子目录和文件的名称:Founddirectory:。test1。txttest2。txtFounddirectory:。folder1file1。pyfile3。pyfile2。pyFounddirectory:。folder2file4。pyfile5。pyfile6。py复制代码 要以自下而上的方式遍历目录树,则将topdownFalse关键字参数传递给os。walk():fordirpath,dirnames,filesinos。walk(。,topdownFalse):print(fFounddirectory:{dirpath})forfilenameinfiles:print(filename)复制代码 传递topdownFalse参数将使os。walk()首先打印出它在子目录中找到的文件:Founddirectory:。folder1file1。pyfile3。pyfile2。pyFounddirectory:。folder2file4。pyfile5。pyfile6。pyFounddirectory:。test1。txttest2。txt复制代码 如你看见的,程序在列出根目录的内容之前列出子目录的内容。这在在你想要递归删除文件和目录的情况下非常有用。你将在以下部分中学习如何执行此操作。默认情况下,os。walk不会访问通过软连接创建的目录。可以通过使用followlinksTrue参数来覆盖默认行为。创建临时文件和目录 Python提供了tempfile模块来便捷的创建临时文件和目录。 tempfile可以在你程序运行时打开并存储临时的数据在文件或目录中。tempfile会在你程序停止运行后删除这些临时文件。 现在,让我们看看如何创建一个临时文件:fromtempfileimportTemporaryFile创建一个临时文件并为其写入一些数据fpTemporaryFile(wt)fp。write(HelloWorld!)回到开始,从文件中读取数据fp。seek(0)datafp。read()print(data)关闭文件,之后他将会被删除fp。close()复制代码 第一步是从tempfile模块导入TemporaryFile。接下来,使用TemporaryFile()方法并传入一个你想打开这个文件的模式来创建一个类似于对象的文件。这将创建并打开一个可用作临时存储区域的文件。 在上面的示例中,模式为wt,这使得tempfile在写入模式下创建临时文本文件。没有必要为临时文件提供文件名,因为在脚本运行完毕后它将被销毁。 写入文件后,您可以从中读取并在完成处理后将其关闭。一旦文件关闭后,将从文件系统中删除。如果需要命名使用tempfile生成的临时文件,请使用tempfile。NamedTemporaryFile()。 使用tempfile创建的临时文件和目录存储在用于存储临时文件的特殊系统目录中。Python将在目录列表搜索用户可以在其中创建文件的目录。 在Windows上,目录按顺序为C:TEMP,C:TMP,TEMP和TMP。在所有其他平台上,目录按顺序为tmp,vartmp和usrtmp。如果上述目录中都没有,tempfile将在当前目录中存储临时文件和目录。 。TemporaryFile()也是一个上下文管理器,因此它可以与with语句一起使用。使用上下文管理器会在读取文件后自动关闭和删除文件:withTemporaryFile(wt)asfp:fp。write(Hellouniverse!)fp。seek(0)fp。read()临时文件现在已经被关闭和删除复制代码 这将创建一个临时文件并从中读取数据。一旦读取文件的内容,就会关闭临时文件并从文件系统中删除。 tempfile也可用于创建临时目录。让我们看一下如何使用tempfile。TemporaryDirectory()来做到这一点:importtempfileimportostmpwithtempfile。TemporaryDirectory()astmpdir:print(Createdtemporarydirectory,tmpdir)tmptmpdirprint(os。path。exists(tmpdir))print(tmp)print(os。path。exists(tmp))复制代码 调用tempfile。TemporaryDirectory()会在文件系统中创建一个临时目录,并返回一个表示该目录的对象。在上面的示例中,使用上下文管理器创建目录,目录的名称存储在tmpdir变量中。第三行打印出临时目录的名称,os。path。exists(tmpdir)来确认目录是否实际在文件系统中创建。 在上下文管理器退出上下文后,临时目录将被删除,并且对os。path。exists(tmpdir)的调用将返回False,这意味着该目录已成功删除。删除文件和目录 您可以使用os,shutil和pathlib模块中的方法删除单个文件,目录和整个目录树。以下将介绍如何删除你不再需要的文件和目录。Python中删除文件 要删除单个文件,请使用pathlib。Path。unlink(),os。remove()或os。unlink()。 os。remove()和os。unlink()在语义上是相同的。要使用os。remove()删除文件,请执行以下操作:importosdatafileC:UsersvuyisileDesktopTestdata。txtos。remove(datafile)复制代码 使用os。unlink()删除文件与使用os。remove()的方式类似:importosdatafileC:UsersvuyisileDesktopTestdata。txtos。unlink(datafile)复制代码 在文件上调用。unlink()或。remove()会从文件系统中删除该文件。如果传递给它们的路径指向目录而不是文件,这两个函数将抛出OSError。为避免这种情况,可以检查你要删除的内容是否是文件,并在确认是文件时执行删除操作,或者可以使用异常处理来处理OSError:importosdatafilehomedata。txt如果类型是文件则进行删除ifos。path。isfile(datafile):os。remove(datafile)else:print(fError:{datafile}notavalidfilename)复制代码 os。path。isfile()检查datafile是否实际上是一个文件。如果是,则通过调用os。remove()删除它。如果datafile指向文件夹,则会向控制台输出错误消息。 以下示例说明如何在删除文件时使用异常处理来处理错误:importosdatafilehomedata。txt使用异常处理try:os。remove(datafile)exceptOSErrorase:print(fError:{datafile}:{e。strerror})复制代码 上面的代码尝试在检查其类型之前先删除该文件。如果datafile实际上不是文件,则抛出的OSError将在except子句中处理,并向控制台输出错误消息。打印出的错误消息使用Pythonfstrings格式化。 最后,你还可以使用pathlib。Path。unlink()删除文件:frompathlibimportPathdatafilePath(homedata。txt)try:datafile。unlink()exceptIsADirectoryErrorase:print(fError:{datafile}:{e。strerror})复制代码 这将创建一个名为datafile的Path对象,该对象指向一个文件。在datafile上调用。unlink()将删除homedata。txt。如果datafile指向目录,则引发IsADirectoryError。值得注意的是,上面的Python程序和运行它的用户具有相同的权限。如果用户没有删除文件的权限,则会引发PermissionError。删除目录 标准库提供了一下函数来删除目录:os。rmdir()pathlib。Path。rmdir()shutil。rmtree() 要删除单个目录或文件夹可以使用os。rmdir()或pathlib。Path。rmdir()。这两个函数只在你删除空目录的时候有效。如果目录不为空,则会抛出OSError。下面演示如何删除一个文件夹:importostrashdirmydocumentsbaddirtry:os。rmdir(trashdir)exceptOSErrorase:print(fError:{trashdir}:{e。strerror})复制代码 现在,trashdir已经通过os。rmdir()被删除了。如果目录不为空,则会在屏幕上打印错误信息:Traceback(mostrecentcalllast):Filestdin,line1,inmoduleOSError:〔Errno39〕Directorynotempty:mydocumentsbaddir复制代码 同样,你也可使用pathlib来删除目录:frompathlibimportPathtrashdirPath(mydocumentsbaddir)try:trashdir。rmdir()exceptOSErrorase:print(fError:{trashdir}:{e。strerror})复制代码 这里创建了一个Path对象指向要被删除的目录。如果目录为空,调用Path对象的。rmdir()方法删除它。删除完整的目录树 要删除非空目录和完整的目录树,Python提供了shutil。rmtree():importshutiltrashdirmydocumentsbaddirtry:shutil。rmtree(trashdir)exceptOSErrorase:print(fError:{trashdir}:{e。strerror})复制代码 当调用shutil。rmtree()时,trashdir中的所有内容都将被删除。在某些情况下,你可能希望以递归方式删除空文件夹。你可以使用上面讨论的方法之一结合os。walk()来完成此操作:importosfordirpath,dirnames,filesinos。walk(。,topdownFalse):try:os。rmdir(dirpath)exceptOSErrorasex:pass复制代码 这将遍历目录树并尝试删除它找到的每个目录。如果目录不为空,则引发OSError并跳过该目录。下表列出了本节中涉及的功能: 函数 描述 os。remove() 删除单个文件,不能删除目录 os。unlink() 和os。remove()一样,职能删除单个文件 pathlib。Path。unlink() 删除单个文件,不能删除目录 os。rmdir() 删除一个空目录 pathlib。Path。rmdir() 删除一个空目录 shutil。rmtree() 删除完整的目录树,可用于删除非空目录复制、移动和重命名文件和目录 Python附带了shutil模块。shutil是shell实用程序的缩写。它为文件提供了许多高级操作,来支持文件和目录的复制,归档和删除。在本节中,你将学习如何移动和复制文件和目录。复制文件 shutil提供了一些复制文件的函数。最常用的函数是shutil。copy()和shutil。copy2()。使用shutil。copy()将文件从一个位置复制到另一个位置,请执行以下操作:importshutilsrcpathtofile。txtdstpathtodestdirshutil。copy(src,dst)复制代码 shutil。copy()与基于UNIX的系统中的cp命令相当。shutil。copy(src,dst)会将文件src复制到dst中指定的位置。如果dst是文件,则该文件的内容将替换为src的内容。如果dst是目录,则src将被复制到该目录中。shutil。copy()仅复制文件的内容和文件的权限。其他元数据(如文件的创建和修改时间)不会保留。 要在复制时保留所有文件元数据,请使用shutil。copy2():importshutilsrcpathtofile。txtdstpathtodestdirshutil。copy2(src,dst)复制代码 使用。copy2()保留有关文件的详细信息,例如上次访问时间,权限位,上次修改时间和标志。复制目录 虽然shutil。copy()只复制单个文件,但shutil。copytree()将复制整个目录及其中包含的所有内容。shutil。copytree(src,dest)接收两个参数:源目录和将文件和文件夹复制到的目标目录。 以下是如何将一个文件夹的内容复制到其他位置的示例:importshutildstshutil。copytree(data1,data1backup)print(dst)data1backup复制代码 在此示例中,。copytree()将data1的内容复制到新位置data1backup并返回目标目录。目标目录不能是已存在的。它将被创建而不带有其父目录。shutil。copytree()是备份文件的一个好方法。移动文件和目录 要将文件或目录移动到其他位置,请使用shutil。move(src,dst)。 src是要移动的文件或目录,dst是目标:importshutildstshutil。move(dir1,backup)print(dst)backup复制代码 如果backup存在,则shutil。move(dir1,backup)将dir1移动到backup。如果backup不存在,则dir1将重命名为backup。重命名文件和目录 Python包含用于重命名文件和目录的os。rename(src,dst):importosos。rename(first。zip,first01。zip)复制代码 上面的行将first。zip重命名为first01。zip。如果目标路径指向目录,则会抛出OSError。 重命名文件或目录的另一种方法是使用pathlib模块中的rename():frompathlibimportPathdatafilePath(data01。txt)datafile。rename(data。txt)复制代码 要使用pathlib重命名文件,首先要创建一个pathlib。Path()对象,该对象包含要替换的文件的路径。下一步是在路径对象上调用rename()并传入你要重命名的文件或目录的新名称。归档 归档是将多个文件打包成一个文件的便捷方式。两种最常见的存档类型是ZIP和TAR。你编写的Python程序可以创建存档文件,读取存档文件和从存档文件中提取数据。你将在本节中学习如何读取和写入两种压缩格式。读取ZIP文件 zipfile模块是一个底层模块,是Python标准库的一部分。zipfile具有可以轻松打开和提取ZIP文件的函数。要读取ZIP文件的内容,首先要做的是创建一个ZipFile对象。ZipFile对象类似于使用open()创建的文件对象。ZipFile也是一个上下文管理器,因此支持with语句:importzipfilewithzipfile。ZipFile(data。zip,r)aszipobj:pass复制代码 这里创建一个ZipFile对象,传入ZIP文件的名称并以读取模式下打开。打开ZIP文件后,可以通过zipfile模块提供的函数访问有关存档文件的信息。上面示例中的data。zip存档是从名为data的目录创建的,该目录包含总共5个文件和1个子目录:。subdirbar。pyfoo。pyfile1。pyfile2。pyfile3。py复制代码 要获取存档文件中的文件列表,请在ZipFile对象上调用namelist():importzipfilewithzipfile。ZipFile(data。zip,r)aszipobj:zipobj。namelist()复制代码 这会生成一个文件列表:〔file1。py,file2。py,file3。py,subdir,subdirbar。py,subdirfoo。py〕复制代码 。namelist()返回存档文件中文件和目录的名称列表。要检索有关存档文件中文件的信息,使用。getinfo():importzipfilewithzipfile。ZipFile(data。zip,r)aszipobj:barinfozipobj。getinfo(subdirbar。py)print(barinfo。filesize)复制代码 这将输出:15277复制代码 。getinfo()返回一个ZipInfo对象,该对象存储有关存档文件的单个成员的信息。要获取有关存档文件中文件的信息,请将其路径作为参数传递给。getinfo()。使用getinfo(),你可以检索有关存档文件成员的信息,例如上次修改文件的日期,压缩大小及其完整文件名。访问。filesize将以字节为单位检索文件的原始大小。 以下示例说明如何在PythonREPL中检索有关已归档文件的更多详细信息。假设已导入zipfile模块,barinfo与在前面的示例中创建的对象相同:barinfo。datetime(2018,10,7,23,30,10)barinfo。compresssize2856barinfo。filenamesubdirbar。py复制代码 barinfo包含有关bar。py的详细信息,例如压缩的大小及其完整路径。 第一行显示了如何检索文件的上次修改日期。下一行显示了如何在归档后获取文件的大小。最后一行显示了存档文件中bar。py的完整路径。 ZipFile支持上下文管理器协议,这就是你可以将它与with语句一起使用的原因。操作完成后会自动关闭ZipFile对象。尝试从已关闭的ZipFile对象中打开或提取文件将导致错误。提取ZIP文件 zipfile模块允许你通过。extract()和。extractall()从ZIP文件中提取一个或多个文件。 默认情况下,这些方法将文件提取到当前目录。它们都采用可选的路径参数,允许指定要将文件提取到的其他指定目录。如果该目录不存在,则会自动创建该目录。要从压缩文件中提取文件,请执行以下操作:importzipfileimportosos。listdir(。)〔data。zip〕datazipzipfile。ZipFile(data。zip,r)提取单个文件到当前目录datazip。extract(file1。py)hometestdir1zipextractfile1。pyos。listdir(。)〔file1。py,data。zip〕提所有文件到指定目录datazip。extractall(pathextractdir)os。listdir(。)〔file1。py,extractdir,data。zip〕os。listdir(extractdir)〔file1。py,file3。py,file2。py,subdir〕datazip。close()复制代码 第三行代码是对os。listdir()的调用,它显示当前目录只有一个文件data。zip。 接下来,以读取模式下打开data。zip并调用。extract()从中提取file1。py。。extract()返回提取文件的完整文件路径。由于没有指定路径,。extract()会将file1。py提取到当前目录。 下一行打印一个目录列表,显示当前目录现在包括除原始存档文件之外的存档文件。之后显示了如何将整个存档提取到指定目录中。。extractall()创建extractdir并将data。zip的内容提取到其中。最后一行关闭ZIP存档文件。从加密的文档提取数据 zipfile支持提取受密码保护的ZIP。要提取受密码保护的ZIP文件,请将密码作为参数传递给。extract()或。extractall()方法:importzipfilewithzipfile。ZipFile(secret。zip,r)aspwdzip:。。。从加密的文档提取数据。。。pwdzip。extractall(pathextractdir,pwdQuish3o)复制代码 将以读取模式打开secret。zip存档。密码提供给。extractall(),并且压缩文件内容被提取到extractdir。由于with语句,在完成提取后,存档文件会自动关闭。创建新的存档文件 要创建新的ZIP存档,请以写入模式(w)打开ZipFile对象并添加要归档的文件:importzipfilefilelist〔file1。py,subdir,subdirbar。py,subdirfoo。py〕withzipfile。ZipFile(new。zip,w)asnewzip:。。。fornameinfilelist:。。。newzip。write(name)复制代码 在该示例中,newzip以写入模式打开,filelist中的每个文件都添加到存档文件中。with语句结束后,将关闭newzip。以写入模式打开ZIP文件会删除压缩文件的内容并创建新存档文件。 要将文件添加到现有的存档文件,请以追加模式打开ZipFile对象,然后添加文件:withzipfile。ZipFile(new。zip,a)asnewzip:。。。newzip。write(data。txt)。。。newzip。write(latin。txt)复制代码 这里打开在上一个示例中以追加模式创建的new。zip存档。在追加模式下打开ZipFile对象允许将新文件添加到ZIP文件而不删除其当前内容。将文件添加到ZIP文件后,with语句将脱离上下文并关闭ZIP文件。打开TAR存档文件 TAR文件是像ZIP等未压缩的文件存档。它们可以使用gzip,bzip2和lzma压缩方法进行压缩。TarFile类允许读取和写入TAR存档。 下面是从存档中读取:importtarfilewithtarfile。open(example。tar,r)astarfile:print(tarfile。getnames())复制代码 tarfile对象像大多数类似文件的对象一样打开。它们有一个open()函数,它采用一种模式来确定文件的打开方式。 使用r,w或a模式分别打开未压缩的TAR文件以进行读取,写入和追加。要打开压缩的TAR文件,请将模式参数传递给tarfile。open(),其格式为filemode〔:compression〕。下表列出了可以打开TAR文件的可能模式: 模式 行为 r 以无压缩的读取模式打开存档 r:gz 以gzip压缩的读取模式打开存档 r:bz2 以bzip2压缩的读取模式打开存档 w 以无压缩的写入模式打开存档 w:gz 以gzip压缩的写入模式打开存档 w:xz 以lzma压缩的写入模式打开存档 a 以无压缩的追加模式打开存档 。open()默认为r模式。要读取未压缩的TAR文件并检索其中的文件名,请使用。getnames():importtarfiletartarfile。open(example。tar,moder)tar。getnames()〔CONTRIBUTING。rst,README。md,app。py〕复制代码 这以列表的方式返回存档中内容的名字。注意:为了向你展示如何使用不同的tarfile对象方法,示例中的TAR文件在交互式REPL会话中手动打开和关闭。 通过这种方式与TAR文件交互,你可以查看运行每个命令的输出。通常,你可能希望使用上下文管理器来打开类似文件的对象。 此外可以使用特殊属性访问存档中每个条目的元数据:forentryintar。getmembers():。。。print(entry。name)。。。print(Modified:,time。ctime(entry。mtime))。。。print(Size:,entry。size,bytes)。。。print()CONTRIBUTING。rstModified:SatNov109:09:512018Size:402bytesREADME。mdModified:SatNov307:29:402018Size:5426bytesapp。pyModified:SatNov307:29:132018Size:6218bytes复制代码 在此示例中,循环遍历。getmembers()返回的文件列表,并打印出每个文件的属性。。getmembers()返回的对象具有可以通过编程方式访问的属性,例如归档中每个文件的名称,大小和上次修改时间。在读取或写入存档后,必须关闭它以释放系统资源。从TAR存档中提取文件 在本节中,你将学习如何使用以下方法从TAR存档中提取文件:。extract()。extractfile()。extractall() 要从TAR存档中提取单个文件,请使用extract(),传入文件名:tar。extract(README。md)os。listdir(。)〔README。md,example。tar〕复制代码 README。md文件从存档中提取到文件系统。调用os。listdir()确认README。md文件已成功提取到当前目录中。要从存档中解压缩或提取所有内容,请使用。extractall():tar。extractall(pathextracted)复制代码 。extractall()有一个可选的path参数来指定解压缩文件的去向。这里,存档被提取到extracted目录中。以下命令显示已成功提取存档:lsexample。tarextractedREADME。mdtree。example。tarextractedapp。pyCONTRIBUTING。rstREADME。mdREADME。md1directory,5fileslsextractedapp。pyCONTRIBUTING。rstREADME。md复制代码 要提取文件对象以进行读取或写入,请使用。extractfile(),它接收文件名或TarInfo对象作为参数。。extractfile()返回一个可以读取和使用的类文件对象:ftar。extractfile(app。py)f。read()tar。close()复制代码 打开的存档应在读取或写入后始终关闭。要关闭存档,请在存档文件句柄上调用。close(),或在创建tarfile对象时使用with语句,以便在完成后自动关闭存档。这将释放系统资源,并将你对存档所做的任何更改写入文件系统。创建新的TAR存档 创建新的TAR存档,你可以这样操作:importtarfilefilelist〔app。py,config。py,CONTRIBUTORS。md,tests。py〕withtarfile。open(packages。tar,modew)astar:。。。forfileinfilelist:。。。tar。add(file)Readthecontentsofthenewlycreatedarchivewithtarfile。open(package。tar,moder)ast:。。。formemberint。getmembers():。。。print(member。name)app。pyconfig。pyCONTRIBUTORS。mdtests。py复制代码 首先,你要创建要添加到存档的文件列表,这样你就不必手动添加每个文件。 下一行使用with光线文管理器在写入模式下打开名为packages。tar的新存档。以写入模式(w)打开存档使你可以将新文件写入存档。将删除存档中的所有现有文件,并创建新存档。 创建并填充存档后,with上下文管理器会自动关闭它并将其保存到文件系统。最后三行打开刚刚创建的存档,并打印出其中包含的文件的名称。 要将新文件添加到现有存档,请以追加模式(a)打开存档:withtarfile。open(package。tar,modea)astar:。。。tar。add(foo。bar)withtarfile。open(package。tar,moder)astar:。。。formemberintar。getmembers():。。。print(member。name)app。pyconfig。pyCONTRIBUTORS。mdtests。pyfoo。bar复制代码 在追加模式下打开存档允许你向其添加新文件而不删除其中已存在的文件。使用压缩存档 tarfile可以读取和写入使用gzip,bzip2和lzma压缩的TAR存档文件。要读取或写入压缩存档,请使用tarfile。open(),为压缩类型传递适当的模式。 例如,要读取或写入使用gzip压缩的TAR存档的数据,请分别使用r:gz或w:gz模式:files〔app。py,config。py,tests。py〕withtarfile。open(packages。tar。gz,modew:gz)astar:。。。tar。add(app。py)。。。tar。add(config。py)。。。tar。add(tests。py)withtarfile。open(packages。tar。gz,moder:gz)ast:。。。formemberint。getmembers():。。。print(member。name)app。pyconfig。pytests。py复制代码 w:gz以写模式模式打开gzip压缩的存档,r:gz以读模式打开gzip压缩的存档。无法在追加模式下打开压缩存档。要将文件添加到压缩存档,你必须创建新存档。一个更简单的方式创建存档 Python标准库还支持使用shutil模块中的高级方法创建TAR和ZIP存档。shutil中的归档实用工具允许你创建,读取和提取ZIP和TAR归档。这些实用工具依赖于较底层的tarfile和zipfile模块。使用shutil。makearchive()创建存档 shutil。makearchive()至少接收两个参数:归档的名称和归档格式。 默认情况下,它将当前目录中的所有文件压缩为format参数中指定的归档格式。你可以传入可选的rootdir参数来压缩不同目录中的文件。。makearchive()支持zip,tar,bztar和gztar存档格式。 以下是使用shutil创建TAR存档的方法:importshutilshutil。makearchive(basename,format,rootdir)shutil。makearchive(databackup,tar,data)复制代码 这将复制data中的所有内容,并在文件系统中创建名为backup。tar的存档并返回其名称。要提取存档,请调用。unpackarchive():shutil。unpackarchive(backup。tar,extractdir)复制代码 调用。unpackarchive()并传入存档名称和目标目录,将backup。tar的内容提取到extractdir中。ZIP存档可以以相同的方式创建和提取。读取多个文件 Python支持通过fileinput模块从多个输入流或文件列表中读取数据。此模块允许你快速轻松地循环遍历一个或多个文本文件的内容。以下是使用fileinput的典型方法:importfileinputforlineinfileinput。input()process(line)复制代码 fileinput默认从传递给sys。argv的命令行参数获取其输入。使用fileinput循环遍历多个文件 让我们使用fileinput构建一个普通的UNIX工具cat的原始版本。cat工具按顺序读取文件,将它们写入标准输出。当在命令行参数中给出多个文件时,cat将连接文本文件并在终端中显示结果:File:fileinputexample。pyimportfileinputimportsysfilesfileinput。input()forlineinfiles:iffileinput。isfirstline():print(fReading{fileinput。filename()})print(line,end)print()复制代码 在当前目录中有两个文本文件,运行此命令会产生以下输出:python3fileinputexample。pybacon。txtcupcake。txtReadingbacon。txtSpicyjalapenobaconipsumdolorametininauteestquienimaliquip,irurecillumdrumstickelit。Donerjowlshankeaexercitationlandjaegerincididuntutporchetta。Tenderloinbaconaliquipcupidatatchickenchuckquisanimetswine。Tritipdonerkevincillumhamveniamcowhamburger。Turkeyporkloincupidatatfiletmignoncapicolabrisketcupimadin。Balltipdolordomagnalaborisnisipancettanostruddoner。Readingcupcake。txtCupcakeipsumdolorsitametcandyIlovecheesecakefruitcake。Toppingmuffincottoncandy。Gummiesmacaroonjujubesjellybeansmarzipan。复制代码 fileinput允许你检索有关每一行的更多信息,例如它是否是第一行(。isfirstline()),行号(。lineno())和文件名(。filename())。你可以在这里读更多关于它的内容。总结 你现在知道如何使用Python对文件和文件组执行最常见的操作。你已经了解使用不同的内置模块来读取,查找和操作文件。