6.5. 处理目录

os.path 模块有几个用于操作文件和目录的函数。在这里,我们将介绍如何处理路径名和列出目录的内容。

示例 6.16. 构造路径名

>>> import os
>>> os.path.join("c:\\music\\ap\\", "mahadeva.mp3") 1 2
'c:\\music\\ap\\mahadeva.mp3'
>>> os.path.join("c:\\music\\ap", "mahadeva.mp3")   3
'c:\\music\\ap\\mahadeva.mp3'
>>> os.path.expanduser("~")                         4
'c:\\Documents and Settings\\mpilgrim\\My Documents'
>>> os.path.join(os.path.expanduser("~"), "Python") 5
'c:\\Documents and Settings\\mpilgrim\\My Documents\\Python'
1 os.path 是对模块的引用,具体是哪个模块取决于您的平台。就像 getpass 通过将 getpass 设置为特定于平台的函数来封装平台之间的差异一样,os 通过将 path 设置为特定于平台的模块来封装平台之间的差异。
2 os.pathjoin 函数从一个或多个部分路径名构造一个路径名。在这种情况下,它只是连接字符串。(请注意,在 Windows 上处理路径名很烦人,因为反斜杠字符必须转义。)
3 在这个稍微不那么琐碎的例子中,join 会在将路径名连接到文件名之前,在路径名中添加一个额外的反斜杠。当我发现这一点时,我欣喜若狂,因为 addSlashIfNecessary 是我在用一种新语言构建工具箱时总是需要编写的一个愚蠢的小函数。在 Python不要 编写这个愚蠢的小函数;聪明的人已经为您处理好了。
4 expanduser 将扩展使用 ~ 表示当前用户主目录的路径名。这适用于任何用户具有主目录的平台,如 Windows、UNIXMac OS X;它对 Mac OS 没有影响。
5 结合这些技术,您可以轻松地为用户主目录下的目录和文件构造路径名。

示例 6.17. 拆分路径名

>>> os.path.split("c:\\music\\ap\\mahadeva.mp3")                        1
('c:\\music\\ap', 'mahadeva.mp3')
>>> (filepath, filename) = os.path.split("c:\\music\\ap\\mahadeva.mp3") 2
>>> filepath                                                            3
'c:\\music\\ap'
>>> filename                                                            4
'mahadeva.mp3'
>>> (shortname, extension) = os.path.splitext(filename)                 5
>>> shortname
'mahadeva'
>>> extension
'.mp3'
1 split 函数拆分一个完整的路径名,并返回一个包含路径和文件名的元组。还记得我说过您可以使用 多变量赋值 从函数返回多个值吗?嗯,split 就是这样一个函数。
2 您将 split 函数的返回值赋给一个由两个变量组成的元组。每个变量接收返回元组中相应元素的值。
3 第一个变量 filepath 接收从 split 返回的元组的第一个元素的值,即文件路径。
4 第二个变量 filename 接收从 split 返回的元组的第二个元素的值,即文件名。
5 os.path 还包含一个函数 splitext,它拆分文件名并返回一个包含文件名和文件扩展名的元组。您可以使用相同的技术将它们分别赋给不同的变量。

示例 6.18. 列出目录

>>> os.listdir("c:\\music\\_singles\\")              1
['a_time_long_forgotten_con.mp3', 'hellraiser.mp3',
'kairo.mp3', 'long_way_home1.mp3', 'sidewinder.mp3', 
'spinning.mp3']
>>> dirname = "c:\\"
>>> os.listdir(dirname)                              2
['AUTOEXEC.BAT', 'boot.ini', 'CONFIG.SYS', 'cygwin',
'docbook', 'Documents and Settings', 'Incoming', 'Inetpub', 'IO.SYS',
'MSDOS.SYS', 'Music', 'NTDETECT.COM', 'ntldr', 'pagefile.sys',
'Program Files', 'Python20', 'RECYCLER',
'System Volume Information', 'TEMP', 'WINNT']
>>> [f for f in os.listdir(dirname)
...     if os.path.isfile(os.path.join(dirname, f))] 3
['AUTOEXEC.BAT', 'boot.ini', 'CONFIG.SYS', 'IO.SYS', 'MSDOS.SYS',
'NTDETECT.COM', 'ntldr', 'pagefile.sys']
>>> [f for f in os.listdir(dirname)
...     if os.path.isdir(os.path.join(dirname, f))]  4
['cygwin', 'docbook', 'Documents and Settings', 'Incoming',
'Inetpub', 'Music', 'Program Files', 'Python20', 'RECYCLER',
'System Volume Information', 'TEMP', 'WINNT']
1 listdir 函数接受一个路径名,并返回该目录内容的列表。
2 listdir 返回文件和文件夹,但不指明哪个是哪个。
3 您可以使用 列表过滤os.path 模块的 isfile 函数来区分文件和文件夹。isfile 接受一个路径名,如果该路径表示一个文件,则返回 1,否则返回 0。这里您使用 os.path.join 来确保完整的路径名,但 isfile 也适用于相对于当前工作目录的部分路径。您可以使用 os.getcwd() 获取当前工作目录。
4 os.path 还有一个 isdir 函数,如果路径表示一个目录,则返回 1,否则返回 0。您可以使用它来获取目录中子目录的列表。

示例 6.19. 在 fileinfo.py 中列出目录


def listDirectory(directory, fileExtList):                                        
    "get list of file info objects for files of particular extensions" 
    fileList = [os.path.normcase(f)
                for f in os.listdir(directory)]            1 2
    fileList = [os.path.join(directory, f) 
               for f in fileList
                if os.path.splitext(f)[1] in fileExtList]  3 4 5
1 os.listdir(directory) 返回 directory 中所有文件和文件夹的列表。
2 使用 f 迭代列表,您可以使用 os.path.normcase(f) 根据操作系统默认值规范大小写。normcase 是一个有用的小函数,它可以补偿不区分大小写的操作系统,这些操作系统认为 mahadeva.mp3mahadeva.MP3 是同一个文件。例如,在 Windows 和 Mac OS 上,normcase 会将整个文件名转换为小写;在 UNIX 兼容系统上,它将返回未更改的文件名。
3 再次使用 f 迭代规范化列表,您可以使用 os.path.splitext(f) 将每个文件名拆分为名称和扩展名。
4 对于每个文件,您会查看其扩展名是否在您关心的文件扩展名列表中(fileExtList,它已传递给 listDirectory 函数)。
5 对于您关心的每个文件,您可以使用 os.path.join(directory, f) 构造文件的完整路径名,并返回完整路径名列表。
Note
只要有可能,您就应该使用 osos.path 中的函数来进行文件、目录和路径操作。这些模块是特定于平台的模块的包装器,因此像 os.path.split 这样的函数可以在 UNIX、Windows、Mac OS 以及 Python 支持的任何其他平台上工作。

还有另一种方法可以获取目录的内容。它非常强大,并且使用您在命令行工作中可能已经熟悉的通配符。

示例 6.20. 使用 glob 列出目录

>>> os.listdir("c:\\music\\_singles\\")               1
['a_time_long_forgotten_con.mp3', 'hellraiser.mp3',
'kairo.mp3', 'long_way_home1.mp3', 'sidewinder.mp3',
'spinning.mp3']
>>> import glob
>>> glob.glob('c:\\music\\_singles\\*.mp3')           2
['c:\\music\\_singles\\a_time_long_forgotten_con.mp3',
'c:\\music\\_singles\\hellraiser.mp3',
'c:\\music\\_singles\\kairo.mp3',
'c:\\music\\_singles\\long_way_home1.mp3',
'c:\\music\\_singles\\sidewinder.mp3',
'c:\\music\\_singles\\spinning.mp3']
>>> glob.glob('c:\\music\\_singles\\s*.mp3')          3
['c:\\music\\_singles\\sidewinder.mp3',
'c:\\music\\_singles\\spinning.mp3']
>>> glob.glob('c:\\music\\*\\*.mp3')                  4
1 正如您之前看到的,os.listdir 只是接受一个目录路径并列出该目录中的所有文件和目录。
2 另一方面,glob 模块接受一个通配符,并返回与该通配符匹配的所有文件和目录的完整路径。这里的通配符是一个目录路径加上 "*.mp3",它将匹配所有 .mp3 文件。请注意,返回列表中的每个元素都已经包含了文件的完整路径。
3 如果您想查找特定目录中所有以 "s" 开头并以 ".mp3" 结尾的文件,您也可以这样做。
4 现在考虑这种情况:您有一个 music 目录,其中有几个子目录,每个子目录中都有 .mp3 文件。您可以通过一次调用 glob 并同时使用两个通配符来获取所有这些文件的列表。一个通配符是 "*.mp3"(用于匹配 .mp3 文件),另一个通配符在目录路径本身中,用于匹配 c:\music 中的任何子目录。这是一个看似简单但功能强大的函数!

关于 os 模块的进一步阅读