7.2. os.path — 跨平台的文件名操作

未匹配的标注

目的:解析,构建,测试以及处理文件名和路径。

使用 os.path 模块中的函数开发跨平台文件处理程序是非常容易的。既是程序不考虑移植多个平台的情况,也应该使用 os.path 进行可靠的文件名称解析。

路径解析

os.path 中的第一组函数用于将表示文件名称的字符串解析成它的组成部分。认识到这些函数不依赖于实际存在的路径是非常重要的;它们只能用来操作字符串。

路径解析依赖于定义在 os 中的一些变量:

  • os.sep - 路径各部分之间的分隔符 (例如, 「/」或者 「\」)。
  • os.extsep - 文件名和 「扩展名」之间的分隔符 (例如, 「.」)。
  • os.pardir - 表示上级目录的路径组件(例如, 「..」)。
  • os.curdir - 表示当前目录的路径组件(例如, 「.」)。

split 函数用于将路径分割成两部分,它会返回一个表示结果的 tupletuple 的第二个元素是路径的最后一部分,第一个元素是之前的所有信息。

ospath_split.py

import os.path

PATHS = [
    '/one/two/three',
    '/one/two/three/',
    '/',
    '.',
    '',
]

for path in PATHS:
    print('{!r:>17} : {}'.format(path, os.path.split(path)))

当输入的参数是以 os.sep 结尾时,路径的最后一个元素就是空的字符串。

$ python3 ospath_split.py

 '/one/two/three' : ('/one/two', 'three')
'/one/two/three/' : ('/one/two/three', '')
              '/' : ('/', '')
              '.' : ('', '.')
               '' : ('', '')

basename() 函数会返回一个等同于 split() 函数第二部分的值。

ospath_basename.py

import os.path

PATHS = [
    '/one/two/three',
    '/one/two/three/',
    '/',
    '.',
    '',
]

for path in PATHS:
    print('{!r:>17} : {!r}'.format(path, os.path.basename(path)))

完整的路径会被层层解析直到最后一部分,不管这个路径代表一个文件还是目录。如果这个完整的路径是以目录分隔符( os.sep )结尾,那么这个路径的基础部分将被认为是空的。

$ python3 ospath_basename.py

 '/one/two/three' : 'three'
'/one/two/three/' : ''
              '/' : ''
              '.' : '.'
               '' : ''

dirname() 函数会返回一个等同于 split() 函数第一部分的值。

ospath_dirname.py

import os.path

PATHS = [
    '/one/two/three',
    '/one/two/three/',
    '/',
    '.',
    '',
]

for path in PATHS:
    print('{!r:>17} : {!r}'.format(path, os.path.dirname(path)))

basename()dirname() 的结果组合起来,就会得到原始的路径。

$ python3 ospath_dirname.py

 '/one/two/three' : '/one/two'
'/one/two/three/' : '/one/two/three'
              '/' : '/'
              '.' : ''
               '' : ''

splitext() 类似 split(),它分隔路径依据扩展分隔符而不是目录分隔符。

ospath_splitext.py

import os.path

PATHS = [
    'filename.txt',
    'filename',
    '/path/to/filename.txt',
    '/',
    '',
    'my-archive.tar.gz',
    'no-extension.',
]

for path in PATHS:
    print('{!r:>21} : {!r}'.format(path, os.path.splitext(path)))

当查找文件扩展名的时候,只使用最后一次出现的 os.extsep ,所以如果一个文件有多个扩展名,那么分隔结果会在前缀留下部分扩展名。

$ python3 ospath_splitext.py

       'filename.txt' : ('filename', '.txt')
           'filename' : ('filename', '')
'/path/to/filename.txt' : ('/path/to/filename', '.txt')
                  '/' : ('/', '')
                   '' : ('', '')
  'my-archive.tar.gz' : ('my-archive.tar', '.gz')
      'no-extension.' : ('no-extension', '.')

commonprefix() 函数会接受一个路径列表作为参数而返回一个字符串表示所有路径中的公共前缀。这个字符串可能表示一个实际不存在的路径,因为路径分隔符并没有被考虑在内,所以这个公共前缀可能并没有落在分隔符边界上。

ospath_commonprefix.py

import os.path

paths = ['/one/two/three/four',
         '/one/two/threefold',
         '/one/two/three/',
         ]
for path in paths:
    print('PATH:', path)

print()
print('PREFIX:', os.path.commonprefix(paths))

在这个例子中,公共的前缀字符串是 /one/two/three ,即使在第二个路径中并没有包含名为 three 的目录。

$ python3 ospath_commonprefix.py

PATH: /one/two/three/four
PATH: /one/two/threefold
PATH: /one/two/three/

PREFIX: /one/two/three

commonpath() 充分考虑路径分隔符,会返回路径序列中各个部分的最长公共有效子路径。

ospath_commonpath.py

import os.path

paths = ['/one/two/three/four',
         '/one/two/threefold',
         '/one/two/three/',
         ]
for path in paths:
    print('PATH:', path)

print()
print('PREFIX:', os.path.commonpath(paths))

因为 "threefold""three" 之后确实没有路径分隔符,所以公共路径是 /one/two

$ python3 ospath_commonpath.py

PATH: /one/two/three/four
PATH: /one/two/threefold
PATH: /one/two/three/

PREFIX: /one/two

构建路径

除了将现有路径分开之外,通常还需要从其他字符串构建路径。为了将几个路径组件组合成一个单个的值,可以使用 join()

ospath_join.py

import os.path

PATHS = [
    ('one', 'two', 'three'),
    ('/', 'one', 'two', 'three'),
    ('/one', '/two', '/three'),
]

for parts in PATHS:
    print('{} : {!r}'.format(parts, os.path.join(*parts)))

如果某个参数是以 os.sep 开头,那么它之前的所有参数都将被丢弃,并且返回值将以它作为新的开始。

$ python3 ospath_join.py

('one', 'two', 'three') : 'one/two/three'
('/', 'one', 'two', 'three') : '/one/two/three'
('/one', '/two', '/three') : '/three'

也可以用来处理包含可自动扩展「变量」组件的路径。例如,expanduser() 将会转化波浪号(~)为用户主目录的名称。

ospath_expanduser.py

import os.path

for user in ['', 'dhellmann', 'nosuchuser']:
    lookup = '~' + user
    print('{!r:>15} : {!r}'.format(
        lookup, os.path.expanduser(lookup)))

如果没有找到用户的主目录,那么这个字符串将被原样返回,正如例子中的 ~nosuchuser

$ python3 ospath_expanduser.py

            '~' : '/Users/dhellmann'
   '~dhellmann' : '/Users/dhellmann'
  '~nosuchuser' : '~nosuchuser'

expandvars() 更一般化,它将会解析路径中所有 shell 环境变量。

ospath_expandvars.py

import os.path
import os

os.environ['MYVAR'] = 'VALUE'

print(os.path.expandvars('/path/to/$MYVAR'))

该函数不会执行任何验证以确保存在一个以其变量解析结果为名称的文件。

$ python3 ospath_expandvars.py

/path/to/VALUE

规范路径

使用 join() 或者嵌入变量的独立字符串组合的路径结尾可能有额外的路径分隔符或者相对路径组件。使用 normpath() 来清理他们:

ospath_normpath.py

import os.path

PATHS = [
    'one//two//three',
    'one/./two/./three',
    'one/../alt/two/three',
]

for path in PATHS:
    print('{!r:>22} : {!r}'.format(path, os.path.normpath(path)))

os.curdiros.pardir 组成的路径片段将被解析并展开。

$ python3 ospath_normpath.py

     'one//two//three' : 'one/two/three'
   'one/./two/./three' : 'one/two/three'
'one/../alt/two/three' : 'alt/two/three'

使用 abspath() 将相对路径转化为绝对路径。

ospath_abspath.py

import os
import os.path

os.chdir('/usr')

PATHS = [
    '.',
    '..',
    './one/two/three',
    '../one/two/three',
]

for path in PATHS:
    print('{!r:>21} : {!r}'.format(path, os.path.abspath(path)))

结果是一个完整的路径,从文件系统树的顶部开始。

$ python3 ospath_abspath.py

                  '.' : '/usr'
                 '..' : '/'
    './one/two/three' : '/usr/one/two/three'
   '../one/two/three' : '/one/two/three'

文件时间

除了处理文件路径,os.path 还包含了用于检索文件属性的函数,类似于 os.stat()

ospath_properties.py

import os.path
import time

print('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() 返回文件包含数据的大小,以字节表示。

$ python3 ospath_properties.py

File         : ospath_properties.py
Access time  : Sun Mar 18 16:21:22 2018
Modified time: Fri Nov 11 17:18:44 2016
Change time  : Fri Nov 11 17:18:44 2016
Size         : 481

检测文件

当一个程序中遇到一个路径名称时,经常需要知道这个路径是指一个文件,目录,系统链接或者它是否存在。 os.path 包含了许多函数来检测这些条件。

ospath_tests.py

import os.path

FILENAMES = [
    __file__,
    os.path.dirname(__file__),
    '/',
    './broken_link',
]

for file in FILENAMES:
    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()

所有这些检测函数都返回 bool 值。

$ ln -s /does/not/exist broken_link
$ python3 ospath_tests.py

File        : 'ospath_tests.py'
Absolute    : False
Is File?    : True
Is Dir?     : False
Is Link?    : False
Mountpoint? : False
Exists?     : True
Link Exists?: True

File        : ''
Absolute    : False
Is File?    : False
Is Dir?     : False
Is Link?    : False
Mountpoint? : False
Exists?     : False
Link Exists?: False

File        : '/'
Absolute    : True
Is File?    : False
Is Dir?     : True
Is Link?    : False
Mountpoint? : True
Exists?     : True
Link Exists?: True

File        : './broken_link'
Absolute    : False
Is File?    : False
Is Dir?     : False
Is Link?    : True
Mountpoint? : False
Exists?     : False
Link Exists?: True

也可以参考

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/pymotw/ospath-c...

译文地址:https://learnku.com/docs/pymotw/ospath-c...

上一篇 下一篇
贡献者:2
讨论数量: 0
发起讨论 只看当前版本


暂无话题~