# 关于文件

# import glob

:Python 的标准库,使用 Unix shell 风格的通配符,来搜索磁盘文件。

  • 官方文档 (opens new window)

  • 原理:

    • 先调用 os.scandir() ,获取所有文件的路径。
    • 然后调用 fnmatch.fnmatch() ,筛选文件。因此通配符的语法,与标准库 fnmatch 一致。
  • 例:

    >>> glob.glob('*')          # 搜索当前目录下的所有文件
    ['search_in_code.sh']
    >>> glob.glob('/tmp/*.log') # 搜索 /tmp 目录下,名称匹配 *.log 的所有文件
    ['/tmp/1.log']
    >>> glob.glob('/tmp/.*')    # 默认不会返回隐藏文件,除非刻意搜索以 . 开头的文件
    ['/tmp/.font-unix', '/tmp/.X11-unix']
    >>> glob.glob('/tmp/**/*.log', recursive=True)  # 递归搜索所有子目录
    ['/tmp/1.log', '/tmp/test/1.log']
    

# import pathlib

:Python 的标准库,用于按面向对象的方式处理文件路径。

# 原理

  • 关于文件路径的风格:

    • 类 Unix 系统采用 POSIX 标准,因此文件路径区分大小写,使用正斜杠 / 作为路径分隔符,以根目录 / 作为目录树的起点。
    • Windows 系统的文件路径不区分大小写,使用反斜杠 \ 作为路径分隔符,以盘符(比如 C: )作为目录树的起点。
    • Python 的大部分原生模块都能自动处理路径分隔符,可以用正斜杠也可以用反斜杠。如下:
      >>> os.path.exists('D:/1')
      True
      >>> os.path.exists('D://1')   # 多余的正斜杠会被忽略
      True
      >>> os.path.exists('D:\\1')   # 在字符串中使用反斜杠时要转义为 \\ 或者加上前缀 r
      True
      >>> os.path.exists(r'D:\1')
      True
      
  • pathlib 提供了以下几个类,用于创建路径对象:

    class PurePath(object)
    class PurePosixPath(PurePath)
    class PureWindowsPath(PurePath)
    
    class Path(PurePath)
    class PosixPath(Path, PurePosixPath)
    class WindowsPath(Path, PureWindowsPath)
    
    • PurePath 类表示纯路径。实例化该类时,会根据当前操作系统自动返回一个 PurePosixPathPureWindowsPath 对象。如下:
      >>> from pathlib import PurePath
      >>> PurePath()
      PureWindowsPath('.')
      
      也可以直接创建指定风格的路径对象:
      >>> from pathlib import PurePosixPath, PureWindowsPath
      >>> PurePosixPath()
      PurePosixPath('.')
      >>> PureWindowsPath()
      PureWindowsPath('.')
      
    • PurePath 类表示具体路径。实例化该类时,会根据当前操作系统自动返回一个 PosixPathWindowsPath 对象。如下:
      >>> from pathlib import Path
      >>> Path()
      WindowsPath('.')
      
      也可以直接创建指定风格的路径对象:
      >>> from pathlib import PosixPath, WindowsPath
      >>> WindowsPath()
      WindowsPath('.')
      >>> PosixPath()       # 不能创建与当前操作系统的路径风格不同的、具体路径的对象
      NotImplementedError: cannot instantiate 'PosixPath' on your system
      
  • 传入一个表示路径的字符串,即可创建路径对象:

    >>> Path('test/f1/')
    WindowsPath('test/f1')
    

    参数为空时,默认为当前目录:

    >>> Path()
    WindowsPath('.')
    

    可以输入多个字符串,它们会自动拼接成一个路径:

    >>> Path('D:/', 'test', 'f1')
    WindowsPath('D:/test/f1')
    >>> Path('D:/', '/test', 'C:f1')  # 拼接时,如果出现多个根路径,则只会保留从最后一个根路径开始的路径
    WindowsPath('C:f1')
    
  • 创建路径对象时,会自动简化文件路径:

    >>> Path('D:\\test\\f1')    # 会将路径分隔符统一转换成正斜杠 / ,不管路径风格
    WindowsPath('D:/test/f1')
    >>> Path('test//f1/')       # 会删除多余的路径分隔符
    WindowsPath('test/f1')
    >>> Path('./test/f1')       # 会删除开头的 ./
    WindowsPath('test/f1')
    
  • 用 str() 函数可以获取路径对象对应的实际文件路径:

    >>> str(Path('D:/test/f1'))
    'D:\\test\\f1'
    
  • Python 的大部分原生模块都支持传入路径对象作为参数,代替字符串形式的文件路径:

    >>> os.path.exists(Path('D:/test/f1'))
    True
    >>> f = open(Path('D:/test/f1'))
    >>> f.close()
    

# PurePath

  • 可以通过正斜杠运算符 / 拼接多个路径:

    >>> 'test' / PurePath('./f1')
    PureWindowsPath('test/f1')
    >>> 'test' / PurePath('./f1') / PurePosixPath('test')
    PureWindowsPath('test/f1/test')
    
  • 可以用等于运算符 == 判断两个路径对象是否相等:

    >>> PurePosixPath('test') == PurePosixPath('test')      # 两个路径对象的风格相同、路径相同,则相等
    True
    >>> PurePosixPath('test') == PureWindowsPath('test')
    False
    >>> PurePosixPath('test') is PurePosixPath('test')      # 两个路径对象的风格相同、路径相同,依然不是同一个实例
    False
    
    >>> PurePosixPath('test') == PurePosixPath('Test')      # POSIX 风格区分大小写
    False
    >>> PureWindowsPath('test') == PureWindowsPath('Test')  # Windows 风格不区分大小写
    True
    
  • 常用属性:

    >>> p = PurePath('D:/test/1.txt')
    >>> p.name      # 返回路径的最后一个字段
    '1.txt'
    >>> p.suffix    # 返回文件的扩展名
    '.txt'
    >>> PurePath('1.tar.gz').suffix   # 只会截取最后一个点 . 之后的部分作为扩展名
    '.gz'
    >>> p.stem      # 返回文件的扩展名之前的部分
    '1'
    
  • 分割路径:

    >>> p.parts                                   # 返回一个元组,包含路径中的各个字段
    ('D:\\', 'test', '1.txt')
    >>> PurePosixPath('/usr/bin/python3').parts   # 注意根路径也算一个字段
    ('/', 'usr', 'bin', 'python3')
    
  • 父目录:

    >>> p.parent                                  # 返回上一级父目录
    PureWindowsPath('D:/test')
    >>> PurePath('D:/').parent                    # 根路径的父目录是它本身
    PureWindowsPath('D:/')
    >>> PurePosixPath('/').parent
    PurePosixPath('/')
    >>> PurePath('.').parent                      # . 和 .. 的父目录都是 .
    PureWindowsPath('.')
    >>> PurePath('..').parent
    PureWindowsPath('.')
    
    >>> p.parents                                 # 返回一个序列,包含每一级父目录
    <PureWindowsPath.parents>
    >>> list(p.parents)
    [PureWindowsPath('D:/test'), PureWindowsPath('D:/')]
    
    • 对于 C:/ / . .. 等特殊路径, pathlib 的处理过程可能难以预料。
  • 常用方法:

    >>> p.as_posix()        # 将路径转换成 Posix 风格
    'D:/test/1.txt'
    >>> p.is_absolute()     # 判断该路径是否为绝对路径
    True
    
  • 模糊匹配:

    >>> p.match('D:/test/*.txt')  # 判断路径是否与指定的 pattern 匹配
    True
    >>> p.match('D:/*.txt')       # 该 pattern 是匹配 D:/ 目录下的 *.txt 文件
    False
    

# Path

  • 常用方法:

    >>> p = Path('D:/test/1.txt')
    >>> p.exists()
    True
    >>> p.is_dir()
    False
    >>> p.is_file()
    True
    >>> p.is_symlink()
    False
    
  • 绝对路径:

    >>> Path('./test/1.txt').resolve()    # 转换绝对路径时可能因为该路径不存在而失败,没有 os.path.abspath() 可靠
    WindowsPath('test/1.txt')
    >>> Path('../test/1.txt').resolve()
    WindowsPath('D:/test/1.txt')
    
  • 查看目录:

    >>> p.parent.iterdir()        # iterdir() 方法会返回一个生成器,包含指定目录下的所有文件
    <generator object Path.iterdir at 0x000001EE3346E900>
    >>> list(p.parent.iterdir())
    [WindowsPath('D:/test/1.txt')]
    
  • 模糊匹配:

    >>> p.parent.glob('*.txt')    # glob() 方法会返回一个生成器,包含与指定 pattern 匹配的所有文件
    <generator object Path.glob at 0x000001EE3346EB30>
    >>> list(p.parent.glob('*.txt'))
    [WindowsPath('D:/test/1.txt')]
    

# import shutil

:Python 的标准库,用于管理磁盘文件。用途像 Linux 的 cp、mv、rm 等命令。

  • 官方文档 (opens new window)

  • 管理文件:

    >>> import shutil
    >>> shutil.copy('f1', '/tmp/f1')    # 拷贝文件,保存为路径为 /tmp/f1 的文件。如果目标文件已存在,则会自动覆盖
    '/tmp/f1'                           # 拷贝成功之后,会返回目标路径
    >>> shutil.copy('f1', '/tmp')       # 拷贝文件,保存到 /tmp 目录下
    '/tmp/f1'
    >>> shutil.copy('f1', '/tmp/test/f1') # 如果目标目录不存在,则报错
    FileNotFoundError: [Errno 2] No such file or directory: '/tmp/test/f1'
    
    >>> shutil.move('f1', '/tmp/f1')  # 移动文件或目录
    '/tmp/f1'
    
  • 管理目录:

    >>> shutil.copytree('/tmp/dir1', '/tmp/dir2') # 拷贝目录。如果目标目录已存在,则报错
    '/tmp/dir2'
    >>> shutil.rmtree('/tmp/dir1')  # 删除目录