os.path模块接口

2019年6月19日 / 95次阅读 / Last Modified 2019年8月2日
os模块

os.path模块是Python的一个标准模块,用来统一地跨平台地处理与pathname(路径文件名,包括了层次化的文件夹和具体文件)有关的操作。(os模块包含了与操作系统的各种接口,而os.path就是汇集了与路径文件名有关的接口)

os.path模块的接口函数,多以path变量为参数,path可以是str,也可以是bytes,官方推荐使用str对象(unicode character string)。之所以有这样的两种选择,是因为在Linux系统中,有一些文件名不能使用str,只能用bytes来表示;而在Win系统中,又不能使用bytes来表示所有的文件和路径,但却可以全部用str来表示。如果传入的参数是str,返回值也是str,如果传入的参数是bytes,返回值也是bytes。

os.path模块是跨平台的,三大主流OS平台(Win,Linux和Mac),它都支持并对外提供一致的接口。相关实现代码分别位于:Lib/posixpath.py(for Linux),Lib/ntpath.py(for Win),Lib/macpath.py (for Mac)。

os.path模块的官方页面:https://docs.python.org/3/library/os.path.html

引用os.path模块

有两种方式引入os.path模块:

>>> import os
>>> import os.path as fsPath

第一种方式很直接,不过在编码时,使用模块的接口,要写成这样的形式: os.path.xxxx 。好在这个前缀不是很长,阅读代码时,也能够更加清晰的看出代码调用的标准库。

第二种方式将os.path模块改了名字,在后续编码中,可以直接使用这个自己喜欢的名字,就是代码要写成这样的形式: fsPath.xxxx

以上两种引用方式没有特别的区别。本文下面的代码,全部采用第一种方式来调用接口。下面开始介绍os.path模块对外提供的函数接口:

os.path.abspath(path)

此函数将path转换为格式符合对应底层OS的绝对路径。

下面的代码是Win系统的对此函数的测试:

>>> os.path.abspath('')
'C:\\Users\\Admin'
>>> os.path.abspath('c:/windows/system32/abcde')
'c:\\windows\\system32\\abcde'
>>> os.path.abspath('/prog/abcde')
'C:\\prog\\abcde'
>>> os.path.abspath('d:prog/abcde')
'D:\\prog\\abcde'
>>> os.path.abspath('abcde')
'C:\\Users\\Admin\\abcde'
>>> os.path.abspath('d:abcde')
'D:\\abcde'
>>> os.path.abspath('test.txt')
'C:\\Users\\Admin\\test.txt'
>>> print(os.path.abspath('test.txt'))
C:\Users\Admin\test.txt

第1行的path参数为空,返回当前路径,这与os.getcwd()函数一样。第3行path参数所表示的路径并不符合Win系统规则,函数将其转换成符合Win系统的路径规则后返回。注意,以上所有示例的返回,都是绝对路径,并请看最后两行代码,Win系统中,因为路径使用“\”,而在代码中“\”是转义符号,所以函数返回的字符串里面有两个“\”(\\)。此函数不关心path路径是否真实存在,判断路径是否存在,有另外的函数代劳。

下面的代码是Linux系统对此函数的测试:

>>> os.path.abspath('')
'/home/xinlin'
>>> os.path.abspath('/home/abcd/1234')
'/home/abcd/1234'
>>> os.path.abspath('abcd')
'/home/xinlin/abcd'
>>> os.path.abspath('c:abcd')
'/home/xinlin/c:abcd'
>>> os.path.abspath('d:abcd')
'/home/xinlin/d:abcd'
>>> os.path.abspath('test.txt')
'/home/xinlin/test.txt'
>>> print(os.path.abspath('test.txt'))
/home/xinlin/test.txt

Linux的文件系统没有盘符的概念。路径的层次分割使用“/”,这看起更顺眼和自然一些。

os.path.basename(path)

所谓basename,我理解就是路径最后面的那一部分。

>>> os.path.basename('')
''
>>> os.path.basename('/home/xinlin')
'xinlin'
>>> os.path.basename('/home/xinlin/')
''
>>> os.path.basename('/home/xinlin/test')
'test'
>>> os.path.basename('/home/xinlin/test.txt')
'test.txt'

Linux系统中也有一个basename的程序,其行为模式与os.path.basename函数有点不同,注意上面代码第5,6行,如果是basename程序,返回的是 xinlin ,而不是空串。

os.path.commonpath(paths)

此函数可以获取多个路径的共同部分,输入为一个list或tuple,每个路径参数必须同为绝对路径,或者同为相对路径,否则会有ValueError的异常抛出。

>>> os.path.commonpath(['/usr/local/apache','/usr/lock/php'])
'/usr'

os.path.commonprefix(paths)

此函数与os.path.commonpath不同的地方在于,前者寻找的是路径的共同部分,后者似乎是在寻找相同的字符串prefix。

>>> os.path.commonprefix(['/usr/local/apache','/usr/lock/php'])
'/usr/loc'

os.path.dirname(path)

此函数用于获取路径除去basename的部分。

>>> os.path.dirname('')
''
>>> os.path.dirname('/home/xinlin')
'/home'
>>> os.path.dirname('/home/xinlin/')
'/home/xinlin'
>>> os.path.dirname('/home/xinlin/test.txt')
'/home/xinlin'

注意高亮的两行的路径写法,如果没有末尾的/符号,我们是不知道xinlin是一个文件夹,还是一个文件,需要通过别的函数来判断。此函数与os.path.basename对应。

os.path.exists(path)

此函数用来判断路径是否存在,既可以判断文件夹是否存在,也可以判断文件是否存在。

>>> os.listdir()
['.local', '.ICEauthority', '.bash_logout', 'Music', 'Downloads', 'Documents', '.gnupg', 'Desktop', 'Public', 'Videos', '.config', '.profile', 'Pictures', '.bashrc', '.ssh', '.cache', '.bash_history', 'examples.desktop', 'Templates']
>>> os.getcwd()
'/home/xinlin'
>>> 
>>> os.path.exists('/home/xinlin/Music/')
True
>>> os.path.exists('/home/xinlin/Music')
True
>>> os.path.exists('/home/xinlin/.bashrc')
True
>>> os.path.exists('/home/xinlin/.bashrc/')
False
>>> os.path.exists('.bashrc')
True
>>> os.path.exists('Music/')
True
>>> os.path.exists('Music')
True

Music是文件还是文件夹,如果没有“/”符号,还需要别的函数来判断。

对于损坏的符号链接文件,函数返回False,因为要follow symbolic link。 在某些平台下,如果文件不被允许执行os.stat()函数,就算文件存在,此函数也可能返回False。

os.path.lexists(path)

此函数与os.path.exists不一样的地方在于,对于损坏的符号链接文件,返回True。 在某些平台下,如果文件不被允许执行os.lstat()函数,就算文件存在,此函数也可能返回False。

>>> os.path.exists('notexist')
False
>>> os.path.lexists('notexist')
True

以上代码,notexist是一个损坏的symbolic link file。函数名lexists,开始的字符是l,就是link的第一个字母。

os.path.expanduser(path)

此函数会使用环境变量HOME来替换path参数中的~。 On Unix, an initial ~ is replaced by the environment variable HOME if it is set; otherwise the current user’s home directory is looked up in the password directory through the built-in module pwd.

>>> os.path.expanduser('~')
'/home/xinlin'
>>> os.path.expanduser('/usr/loca/apache')
'/usr/loca/apache'
>>> os.path.expanduser('~/usr/loca/apache')
'/home/xinlin/usr/loca/apache'

如果path中没有~符号,就原样返回。

os.path.expandvars(path)

此函数其实就是将环境变量在path参数里面做替换,如果环境变量不存在,就原样返回。

>>> os.path.expandvars('$MONKEY')
'$MONKEY'
>>> os.path.expandvars('$PATH')
'/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin'
>>> os.path.expandvars('${MONKEY}')
'${MONKEY}'
>>> os.path.expandvars('${PATH}')
'/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin'

MONKEY这个变量是不存在的,函数就原样返回了。是否使用{},根据情况。

os.path.getatime(path)

获取文件的last access time。

>>> import time
>>> satime = os.path.getatime('.bashrc')
>>> time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(satime))
'2019-06-17 11:04:30'

os.path.getmtime(path)

获取文件的最后修改时间,last modification time。

>>> smtime = os.path.getmtime('.bashrc')
>>> time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(smtime))
'2019-06-11 15:38:52'

os.path.getctime(path)

在Win系统,ctime就是文件的创建时间。在Linux系统,ctime是time of last metadata change。

>>> sctime = os.path.getmtime('.bashrc')
>>> time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(sctime))
'2019-06-11 15:38:52'

上面三个函数,涉及到系统三个时间属性,请参考:关于atime,mtime,ctime的介绍。

os.path.getsize(path)

获取文件的大小,in bytes。

>>> os.path.getsize('.bashrc')
3771

os.path.isabs(path)

判断path是否为绝对路径。

>>> os.path.isabs('')
False
>>> os.path.isabs('/')
True
>>> os.path.isabs('abcd')
False
>>> os.path.isabs('/abcd')
True

Linux系统的绝对路径以/开始,Win系统的绝对路径以盘符开始。

os.path.isfile(path)

判断path是否为一个文件。如果path是symbolic link file,函数会follow;如果是损坏的symbolic link file,返回False。

>>> os.path.isfile('.bashrc')
True
>>> os.path.isfile('Music')
False

对于不存在的path,返回False。需要注意,isfile函数判断的是regular file。

os.path.isdir(path)

判断path是否为一个文件夹(directory)。如果是symbolic link file,函数会follow ;如果是损坏的symbolic link file,返回False

>>> os.path.isdir('.bashrc')
False
>>> os.path.isdir('Music')
True

os.path.islink(path)

判断path是否为一个symbolic link file(软链接)。就算是损坏的symbolic link file,也会返回True,除非Python的运行环境不支持symbolic link file。

对于硬链接文件,此函数会返回False,不支持。也许可以这样来理解,硬链接,hard link,就是一个与普通文件一模一样的文件。(参考:硬链接,软链接

下面的代码中,testslink是一个损坏的symbolic link file。

[xinlin@promote ~]$ readlink testslink
/home/xinlin/abcde
[xinlin@promote ~]$ cat testslink
cat: testslink: No such file or directory
>>> os.path.isfile('testslink')
False
>>> os.path.islink('testslink')
True

以上几个os.path.is*函数,可以考虑用stat模块提供的函数来替换,stat模块提供的类似接口更丰富,而且在需要频繁判断文件类别的场景下,还可以减少底层的stat()系统调用带来的性能消耗。

os.path.ismount(path)

判断path路径是否为一个mount point。

>>> os.path.ismount('/')
True
>>> os.path.ismount('/home/xinlin')
False

os.path.join(path, *paths)

函数将path参数和*paths所代表的部分(*paths表示可以输入任意个数的参数)拼接起来,形成一个新的路径字符串。拼接使用的符号来自os.sep。

>>> os.path.join('/home','xinlin')
'/home/xinlin'
>>> os.path.join('/home','xinlin','')
'/home/xinlin/'
>>> os.sep
'/'

由于Win系统有盘符的概念,如果在处理*paths参数时遇到了盘符,前面的参数内容都会被丢弃。

>>> os.path.join('C:\\','home','xinlin')
'C:\\home\\xinlin'
>>> os.path.join('C:\\','home','xinlin','')
'C:\\home\\xinlin\\'
>>> os.path.join('C:\\','home','xinlin','D:')
'D:'
>>> os.path.join('C:\\','home','xinlin','D:\\')
'D:\\'
>>> os.sep
'\\'

os.path.normcase(path)

将路径名称的大小写进行规则化。此函数主要针对Win系统开发,作用是将路径名称全部小写(Win系统的路径不区分大小写),并且将forwoad slash转换成Win系统使用的backward slash。对于其它OS,函数原样返回。

>>> os.path.normcase('C:\ABCD')
'c:\\abcd'
>>> os.path.normcase('C:/ABCD')
'c:\\abcd'

os.path.normpath(path)

此函数对路径中冗余的部分进行清理。

>>> os.path.normpath('/home//XINLIN')
'/home/XINLIN'
>>> os.path.normpath('/home/./XINLIN')
'/home/XINLIN'
>>> os.path.normpath('/home/XINLIN/')
'/home/XINLIN'
>>> os.path.normpath('/home/foo/../XINLIN/')
'/home/XINLIN'

在Win平台,此函数与os.path.normcase是一样的。

os.path.realpath(path)

返回path参数的真实路径,这个真实路径,也是绝对路径。

>>> os.path.realpath('')
'/home/xinlin'
>>> os.path.realpath('.bashrc')
'/home/xinlin/.bashrc'

os.path.relpath(path, start=os.curdir)

返回path的相对路径,此函数做纯粹的路径计算,不关心path是否存在。

>>> os.path.relpath('abc')
'abc'
>>> os.path.relpath('abc','/')
'home/xinlin/abc'
>>> os.curdir
'.'
>>> os.getcwd()
'/home/xinlin'

os.path.samefile(path1, path2)

判断path1和path2是否一致。如果os.stat()对path1或path2调用失败,此函数会抛出异常,这表示path1和path2必须要真实存在。

>>> os.path.samefile('a.txt','b.txt')
False
>>> os.path.samefile('a.txt','c.txt')
True

此函数会follow symbolic link file,即上面的高亮第3行。

os.path.sameopenfile(fp1, fp2)

如果fp1和fp2指向的是相同的open file,返回True。两个参数需要时file number。

>>> fp1 = open('a.txt','r')
>>> fp2 = open('b.txt','r')
>>> fp3 = open('c.txt','r')
>>> os.path.sameopenfile(fp1.fileno(),fp2.fileno())
False
>>> os.path.sameopenfile(fp1.fileno(),fp3.fileno())
True

os.path.samestat(stat1, stat2)

stat是指调用os.stat()函数获取到的返回值。

>>> os.listdir()
['c.txt', 'a.txt', 'b.txt']
>>> sa = os.stat('a.txt')
>>> sb = os.stat('b.txt')
>>> sc = os.stat('c.txt')
>>> os.path.samestat(sa,sb)
False
>>> os.path.samestat(sa,sc)
True

c.txt是a.txt的链接文件,因此stat是一样的。

os.path.split(path)

将path分割成为basename和dirname两个部分,返回的是一个tuple。

>>> os.path.split('/home/xinlin/abc')
('/home/xinlin', 'abc')
>>> os.path.split('/home/xinlin/abc/')
('/home/xinlin/abc', '')
>>> os.path.split('abc')
('', 'abc')

tuple第1个部分是dirname,第2个部分是basename。请参考os.path.basename函数和os.path.dirname函数。

os.path.splitdrive(path)

顾名思义,此函数的作用就是分离出盘符和路径,也可以用于分离UNC路径,盘符和UNC路径都是Win系统开始的概念。此函数我们来看一下docstring,就知道怎么使用了:

>>> help(os.path.splitdrive)
Help on function splitdrive in module ntpath:

splitdrive(p)
    Split a pathname into drive/UNC sharepoint and relative path specifiers.
    Returns a 2-tuple (drive_or_unc, path); either part may be empty.

    If you assign
        result = splitdrive(p)
    It is always true that:
        result[0] + result[1] == p

    If the path contained a drive letter, drive_or_unc will contain everything
    up to and including the colon.  e.g. splitdrive("c:/dir") returns ("c:", "/dir")

    If the path contained a UNC path, the drive_or_unc will contain the host name
    and share up to but not including the fourth directory separator character.
    e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")

    Paths cannot contain both a drive letter and a UNC path.

照例,还是给出示例代码:

>>> os.path.splitdrive('c:/abcd/1234')
('c:', '/abcd/1234')
>>> os.path.splitdrive('//servername/sharedir')
('//servername/sharedir', '')
>>> os.path.splitdrive('//servername/sharedir/filename')
('//servername/sharedir', '/filename')

os.path.splitext(path)

ext是extension的缩写,表示的是扩展,什么是ext?通过查阅此函数的docstring,可以清晰的知道,ext就是pathname中,最后一个dot(点)后后买你的部分,就是我们常说的文件扩展名。Win系统大量的使用扩展名,Linux系统就不能通过扩展名来判断文件类型。此函数将path按最后一个dot来分割,返回一个tuple。

>>> os.path.splitext('text.txt')
('text', '.txt')
>>> os.path.splitext('game.exe')
('game', '.exe')
>>> os.path.splitext('config.ini')
('config', '.ini')
>>> os.path.splitext('py.maixj.net.sh')
('py.maixj.net', '.sh')
>>> os.path.splitext('abcde12345')
('abcde12345', '')

如果path中没有dot,返回就如上面的示例代码的最后两行。

os.path.supports_unicode_filenames

Win系统下,这个变量的值是True,表示文件名可以使用Unicode。

Linux系统下,这个变量的值是False。

其它

之前的Python版本还有一个os.path.walk()函数,用来遍历路径和文件,这个函数现在没有了。取而代之的应该是os.walk()函数。本站还自研了一个用来遍历路径和文件的函数,请参考:用Python递归遍历目录树

-- EOF --

本文链接:https://www.pynote.net/archives/131

留言区

《os.path模块接口》有1条留言

电子邮件地址不会被公开。 必填项已用*标注

  • 麦新杰

    os.path.abspath不支持~符号,需要使用os.path.expanduser函数来支持。 [回复]


前一篇:
后一篇:

More


©Copyright 麦新杰 Since 2019 Python笔记

go to top