1. os.path平台独立的文件名管理
利用os.path模块中包含的函数,很容易编写代码来处理多个平台上的文件。即使程序不打算在平台之间移植,也应当使用os.path来完成可靠的文件名解析。
1.1 解析路径
os.path中的第一组函数可以用来将表示文件名的字符串解析为文件名的各个组成部分。这些函数并不要求路径真正存在:它们只是处理字符串。
路径解析依赖于os中定义的一些变量:
os.sep:路径各部分之间的分隔符(例如,“/”或“\”)。
os.extsep:文件名与文件“扩展名”之间的分隔符(例如“.”)。
os.pardir:路径中表示目录树上一级的部分(例如:“..”)。
os.curdir:路径中指示当前目录的部分(例如:“.”)。
split()函数将路径分解为两个单独的部分,并返回包含这些结果的一个tuple。这个tuple的第二个元素是路径的最后一部分,第一个元素则是此前的所有内容。
importos.path
PATHS=['/one/two/three','/one/two/three/','/','.','',
]for path inPATHS:print('{!r:>17} : {}'.format(path, os.path.split(path)))
输入参数以os.sep结尾时,路径的最后一个元素是一个空串。
basename()函数返回的值等价于split()值的第二部分。
importos.path
PATHS=['/one/two/three','/one/two/three/','/','.','',
]for path inPATHS:print('{!r:>17} : {!r}'.format(path, os.path.basename(path))
整个路径会剥除到只剩下最后一个元素,不论这指示的是一个文件还是一个目录。如果路径以目录分隔符结尾(os.sep),则认为基本部分为空。
dirname()函数返回分解路径得到的第一部分。
importos.path
PATHS=['/one/two/three','/one/two/three/','/','.','',
]for path inPATHS:print('{!r:>17} : {!r}'.format(path, os.path.dirname(path)))
将basename()的结果与dirname()结合可以得到原来的路径。
splitext()的工作类似于split(),不过它会根据扩展名分隔符而不是目录分隔符来分解路径。
importos.path
PATHS=['filename.txt','filename','/path/to/filename.txt','/','','my-archive.tar.gz','no-extension.',
]for path inPATHS:print('{!r:>21} : {!r}'.format(path, os.path.splitext(path)))
查找扩展名时,只使用os.extsep的最后一次出现,所以如果一个文件名有多个扩展名,那么分解这个文件名时,部分扩展名会留在前缀上。
commonprefix()取一个路径列表作为参数,并且返回一个字符串,表示所有路径中都出现的公共前缀。这个值可能表示一个根本不存在的路径,而且并不考虑路径分隔符,所以这个前缀可能并不落在一个分隔符边界上。
importos.path
paths= ['/one/two/three/four','/one/two/threefold','/one/two/three/',
]for path inpaths:print('PATH:', path)print()print('PREFIX:', os.path.commonprefix(paths))
在这个例子中,公共前缀字符串是/one/two/three。尽管其中一个路径并不包括一个名为three的目录。
commonpath()则要考虑路径分隔符。它返回的前缀不包括部分路径值。
importos.path
paths= ['/one/two/three/four','/one/two/threefold','/one/two/three/',
]for path inpaths:print('PATH:', path)print()print('PREFIX:', os.path.commonpath(paths))
由于“threefold”在“three”后面没有一个路径分隔符,所以公共前缀为/one/two。
1.2 建立路径
除了分解现有的路径,还经常需要从其他字符串建立路径。要将多个路径组成部分结合为一个值,可以使用join()。
importos.path
PATHS=[
('one', 'two', 'three'),
('/', 'one', 'two', 'three'),
('/one', '/two', '/three'),
]for parts inPATHS:print('{} : {!r}'.format(parts, os.path.join(*parts)))
如果要连接的某个参数以os.sep开头,那么前面的所有参数都会被丢弃,并且这个新参数会成为返回值的开始部分。
还可以处理包含“可变”部分的路径,这些“可变”部分可以自动扩展。例如,expanduser()可以将波浪线(~)字符串转换为用户主目录名。
importos.pathfor user in ['', 'dhellmann', 'nosuchuser']:
lookup= '~' +userprint('{!r:>15} : {!r}'.format(
lookup, os.path.expanduser(lookup)))
如果用户的主目录无法找到,那么字符串将不做任何改动并直接返回,如下面这个例子中的~nosuchuser。
expandvars()更为通用,它会扩展路径中出现的所有shell环境变量。
importos.pathimportos
os.environ['MYVAR'] = 'VALUE'
print(os.path.expandvars('/path/to/$MYVAR'))
这里不会完成任何验证来确保变量值能够得到真正存在的文件名。
1.3 规范化路径
使用join()或利用嵌入变量由单独的字符串组合路径时,得到的路径最后可能会有多余的分隔符或相对路径部分。使用normpath()可以清除这些内容。
importos.path
PATHS=['one//two//three','one/./two/./three','one/../alt/two/three',
]for path inPATHS:print('{!r:>22} : {!r}'.format(path, os.path.normpath(path)))
这里会估算并折叠os.curdir和os.pardir构成的路径段。
要把一个相对路径转换为一个绝对文件名,可以使用abspath()。
importosimportos.path
os.chdir('/')
PATHS=['.','..','./one/two/three','../one/two/three',
]for path inPATHS:print('{!r:>21} : {!r}'.format(path, os.path.abspath(path)))
结果是一个从文件系统数最顶层开始的完整的路径。
1.4 文件时间
除了处理路径,os.path还包括一些用于获取文件属性的函数,类似于os.stat()返回的结果。
importos.pathimporttimeprint('File :', __file__)print('Access time :', time.ctime(os.path.getatime(__file__)))print('Modified time:', time.ctime(os.path.getmtime(__file__)))print('Change time :', time.ctime(os.path.getctime(__file__)))print('Size :', os.path.getsize(__file__))
os.path.getatime()返回访问时间,os.path.getmtime()返回修改时间,os.path.getctime()返回创建时间。os.path.getsize()返回文件中的数据量,以字节为单位表示。
1.5 测试文件
程序在遇到一个路径名时,通常需要知道这个路径指示的是一个文件、目录还是一个符号连接(symlink),另外还要知道它是否确实存在。os.path包含了一些用于测试所以这些条件的函数。
importos.path
FILENAMES=[__file__,
os.path.dirname(__file__),'/','./broken_link',
]for file inFILENAMES:print('File : {!r}'.format(file))print('Absolute :', os.path.isabs(file))print('Is File? :', os.path.isfile(file))print('Is Dir? :', os.path.isdir(file))print('Is Link? :', os.path.islink(file))print('Mountpoint? :', os.path.ismount(file))print('Exists? :', os.path.exists(file))print('Link Exists?:', os.path.lexists(file))print()
所有这些测试函数都返回布尔值。