Automate the Boring Stuff with Python: Practical Programming for Total Beginners (2nd Edition)
Copyright © 2020 by Al Sweigart.
9.1 文件和文件路径
文件有两个关键属性:文件名(filename)和 路径(path)。
例,Win7 计算机中有一个文件,其文件名为 project.docx,保存在路径 C:\Users\asweigart\Documents 中。
文件名中最后一个句点之后的部分称为文件扩展名(extension),指定文件的类型。
Users,asweigart 和 Documents 都指的是文件夹(或称为目录)。
路径中的 C:\ 部分是根文件夹(root folder),也称为 C: 盘(C: drive)。
在 OS X 和 Linux 中,根文件夹是 /。
其他卷(volumes),比如 DVD 驱动器或 USB 闪存驱动器,在不同的操作系统上显示也不同。
在 Windows 上,它们表示为新的、带字符的根驱动器。诸如 D:\ 或 E:\。
在 OS X 上,它们表示为新的文件夹,在 /Volumes 文件夹下。
在 Linux 上,它们表示为新的文件夹,在 /mnt(“mount”)文件夹下。
注意:文件夹名称和文件名在 Windows 和 OS X 上是不区分大小写的,但在 Linux 上是区分大小写的。
Windows 上的反斜杠,OS X 和 Linux 上的正斜杠
将表示单个文件和路径上的文件夹名称的字符串值,传递给 pathlib
模块的 Path()
函数,返回一个表示文件路径的字符串,它使用正确的路径分隔符。
>>> from pathlib import Path
>>> Path('spam', 'bacon', 'eggs')
WindowsPath('spam/bacon/eggs')
>>> str(Path('spam', 'bacon', 'eggs'))
'spam\\bacon\\eggs'
在 Windows 上运行代码,Path()
函数返回一个 WindowsPath 对象。尽管 Windows 使用反斜杠,但是交互式环境中的 WindowsPath 表示使用正斜杠来显示它们,因为开源软件开发人员历来喜欢 Linux 操作系统。
如果在 Linux 上调用这个函数,Path() 将返回一个 PosixPath
对象,当它被传递给 str()
时,返回“spam/bacon/eggs”。(POSIX 是 Linux 等类 Unix 操作系统的一套标准。)
下面的代码将文件名列表中的名称连接到文件夹名称的末尾:
>>> from pathlib import Path
>>> myFiles = ['accounts.txt', 'details.csv', 'invite.docx']
>>> for filename in myFiles:
print(Path(r'C:\Users\Al', filename))
C:\Users\Al\accounts.txt
C:\Users\Al\details.csv
C:\Users\Al\invite.docx
在 Windows 中,反斜杠分隔目录,所以不能在文件名中使用它。但是,可以在 macOS 和 Linux 上的文件名中使用反斜杠。因此,虽然 Path(r'spam\eggs')
指的是 Windows 上两个不同的文件夹(或 spam 文件夹中的一个文件 eggs),但相同的命令也指的是 macOS 和 Linux 上一个名为 spam\eggs 的文件夹(或文件)。
由于这个原因,在 Python 代码中总是使用正斜杠通常是一个好主意。
pathlib
模块将确保它能够在所有操作系统上工作。
注意,在 Python 3.4 中引入了 pathlib
来替换旧的 os.path
函数。Python 标准库模块从 Python 3.6 开始支持它,但如果使用的是 Python 2 版本,建议使用 pathlib2
,它在 Python 2.7 中提供了 pathlib
的特性。
可以在 https://docs.python.org/3/library/os.path.html 中查看以前的函数。
使用 / 操作符连接路径
通常用于除法的 / 操作符可以组合 Path 对象和字符串。在使用 Path()
函数创建 Path 对象之后,使用操作符 / 可以方便地修改 Path 对象。
>>> from pathlib import Path
>>> Path('spam') / 'bacon' / 'eggs'
WindowsPath('spam/bacon/eggs')
>>> Path('spam') / Path('bacon/eggs')
WindowsPath('spam/bacon/eggs')
>>> Path('spam') / Path('bacon', 'eggs')
WindowsPath('spam/bacon/eggs')
将 / 操作符与 Path 对象一起使用,使得连接路径与字符串连接一样简单。它也比使用字符串连接或 join()
方法更安全:
>>> homeFolder = r'C:\Users\Al'
>>> subFolder = 'spam'
>>> homeFolder + '\\' + subFolder
'C:\\Users\\Al\\spam'
>>> '\\'.join([homeFolder, subFolder])
'C:\\Users\\Al\\spam'
可以添加一个 if 语句,检查 sys.platform
(包含描述计算机操作系统的字符串)。
pathlib
模块通过反复使用 / 操作符来正确地连接路径,不管代码运行在什么操作系统上。
>>> homeFolder = Path('C:/Users/Al')
>>> subFolder = Path('spam')
>>> homeFolder / subFolder
WindowsPath('C:/Users/Al/spam')
>>> str(homeFolder / subFolder)
'C:\\Users\\Al\\spam'
在使用 / 操作符连接路径时,需要记住的唯一一件事是,前两个值中的一个必须是 Path 对象。
操作符 / 替换了旧的 os.path.join()
函数,了解 os.path.join()
函数更多信息可以参阅 https://docs.python.org/3/library/os.path.html#os.path.join 。
使用 os.path.join() 函数连接路径
将单个文件和路径上的文件夹名称的字符串值传递给 os.path.join()
函数,函数返回一个表示文件路径的字符串,它使用正确的路径分隔符。
>>> import os
>>> os.path.join('usr', 'bin', 'spam') # on Windows
'usr\\bin\\spam'
如果在 OS X 或 Linux 上调用该函数,返回的字符串则是
'usr/bin/spam'
使用 os.path.join()
函数创建表示文件名称的字符串。
>>> myFiles = ['accounts.txt', 'details.csv', 'invite.docx']
>>> for filename in myFiles:
print(os.path.join('C:\\Users\\asweigart', filename))
C:\Users\asweigart\accounts.txt
C:\Users\asweigart\details.csv
C:\Users\asweigart\invite.docx
当前工作目录
可以使用 Path.cwd()
函数以字符串值的形式获取当前工作目录,可以利用 os.chdir()
函数改变它。
>>> from pathlib import Path
>>> import os
>>> Path.cwd()
WindowsPath('C:/Users/Al/AppData/Local/Programs/Python/Python37')'
>>> os.chdir('C:\\Windows\\System32')
>>> Path.cwd()
WindowsPath('C:/Windows/System32')
getcwd()
函数是将当前工作目录作为字符串获取的较老方法。
每个运行在计算机的程序都有一个当前工作目录(current working directory,或者 cwd)。
可以使用 os.getcwd()
函数获取表示当前当前工作目录的字符串值,可以利用 os.chdir()
函数改变它。
>>> import os
>>> os.getcwd()
'C:\\Python34'
>>> os.chdir('C:\\Windows\\System32')
>>> os.getcwd()
'C:\\Windows\\System32'
如果试图改变的目录不存在,Python 会显示错误。
>>> os.chdir('C:\\ThisFolderDoesNotExist')
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
os.chdir('C:\\ThisFolderDoesNotExist')
FileNotFoundError: [WinError 2] The system cannot find the file specified:
'C:\\ThisFolderDoesNotExist'
虽然文件夹是目录的更现代的名称,但请注意,当前工作目录(或工作目录)是标准术语,没有当前工作文件夹这种说法。
主目录
所有用户在计算机上都有自己的文件夹,称为主目录(home directory)或主文件夹(home folder)。可以通过调用 Path.home()
来获得主文件夹的 Path 对象。
>>> Path.home()
WindowsPath('C:/Users/Al')
在 Windows 中,主目录位于 C:\Users 之下。
在 Mac 上,主目录在 /Users 之下。
在 Linux 上,主目录通常位于 /home 之下。
绝对路径与相对路径
有两种方式指定文件路径。
① 绝对路径(absolute path),总是从根文件夹开始。
② 相对路径(relative path),相对于程序当前工作目录。
.
文件夹:一个句点(dot)可以表示文件夹名称,是“这个目录”的缩写。
..
文件夹:两个句点(dot-dot)表示“父文件夹”。
相对路径开头的 .\
是可选的。例如,.\spam.txt
和 spam.txt
指的是同一个文件。
使用 os.makedirs() 创建新文件夹
>>> import os
>>> os.makedirs('C:\\delicious\\walnut\\waffles')
为了保证整个路径存在,os.makedirs()
会创建任何必要的中间的文件夹。
要从 Path 对象创建目录,请调用 mkdir()
方法。
>>> from pathlib import Path
>>> Path(r'C:\Users\Al\spam').mkdir()
注意,mkdir()
一次只能创建一个目录,它不会像 os.makedirs()
那样一次创建多个子目录。
os.path 模块
os.path
模块包含许多有用的与文件名和文件路径有关的函数。
因为 os.path
是 os
模块中的模块,所以执行 import os
就可以导入它。
os.path
模块的完整文档可以查看 Python 网站:http://docs.python.org/3/library/os.path.html
处理绝对路径和相对路径
pathlib
模块提供了检查给定路径是否是绝对路径并返回相对路径的绝对路径的方法。
调用 Path 对象的 is_absolute()
方法判断给定的路径是否是绝对路径。
>>> Path.cwd()
WindowsPath('C:/Users/Al/AppData/Local/Programs/Python/Python37')
>>> Path.cwd().is_absolute()
True
>>> Path('spam/bacon/eggs').is_absolute()
False
要从相对路径获取绝对路径,可以将 path.cwd() /
放在相对路径对象前面。毕竟,当说起“相对路径”时,几乎总是指相对于当前工作目录的路径。
>>> Path('my/relative/path')
WindowsPath('my/relative/path')
>>> Path.cwd() / Path('my/relative/path')
WindowsPath('C:/Users/Al/AppData/Local/Programs/Python/Python37/my/relative/path')
如果相对路径相对于当前工作目录之外的另一个路径,则只需将 path.cwd()
替换为另一个路径。
>>> Path('my/relative/path')
WindowsPath('my/relative/path')
>>> Path.home() / Path('my/relative/path')
WindowsPath('C:/Users/Al/my/relative/path')
os.path
模块也有一些与绝对路径和相对路径相关的有用函数。
- 调用
os.path.abspath(path)
返回一个字符串,表示参数的绝对路径。这是一个将相对路径转换为绝对路径的简便方式。 - 调用
os.path.isabs(path)
判断参数是否是绝对路径,如果是一个绝对路径,返回 True,如果是相对路径,返回 False。 - 调用
os.path.relpath(path, start)
返回一个字符串,表示从 start 路径到 path 的相对路径。如果没有提供 start,使用当前工作目录作为开始路径。
>>> os.getcwd()
'C:\\Python34'
>>> os.path.abspath('.')
'C:\\Python34'
>>> os.path.abspath('.\\Scripts')
'C:\\Python34\\Scripts'
>>> os.path.isabs('.')
False
>>> os.path.isabs(os.path.abspath('.'))
True
>>> os.path.relpath('C:\\Windows', 'C:\\')
'Windows'
>>> os.path.relpath('C:\\Windows', 'C:\\spam\\eggs')
'..\\..\\Windows'
- 调用
os.path.dirname(path)
返回一个字符串,包含 path 参数中最后一个斜杠之前的所有内容。 - 调用
os.path.basename(path)
将返回一个字符串,包含 path 参数中最后一个斜杠之后的所有内容。
>>> path = 'C:\\Windows\\System32\\calc.exe'
>>> os.path.basename(path)
'calc.exe'
>>> os.path.dirname(path)
'C:\\Windows\\System32'
- 如果同时需要路径的目录名称和基本名称,调用
os.path.split()
返回一个元组,包含这两个字符串。
>>> calcFilePath = 'C:\\Windows\\System32\\calc.exe'
>>> os.path.split(calcFilePath)
('C:\\Windows\\System32', 'calc.exe')
想要获取与上面相同的元组,可以调用 os.path.dirname()
和 os.path.basename()
。
>>> (os.path.dirname(calcFilePath), os.path.basename(calcFilePath))
('C:\\Windows\\System32', 'calc.exe')
- 使用
split()
字符串方法,根据os.sep
中的字符进行分割,可以获取每个文件夹的字符串列表。
os.sep
变量设置为正确的文件夹分割斜杠。
>>> os.sep
'\\'
>>> os.path.sep
'\\'
>>> calcFilePath.split(os.path.sep)
['C:', 'Windows', 'System32', 'calc.exe']
在 OS X 和 Linux 系统中,返回列表开头有一个空字符串。
>>> '/usr/bin'.split(os.path.sep)
['', 'usr', 'bin']
获取文件路径的各个部分
>>> p = Path('C:/Users/Al/spam.txt')
>>> p.anchor
'C:\\'
>>> p.parent # This is a Path object, not a string.
WindowsPath('C:/Users/Al')
>>> p.name
'spam.txt'
>>> p.stem
'spam'
>>> p.suffix
'.txt'
>>> p.drive
'C:'
parents
属性(与 parent
属性不同)计算为 Path 对象的祖先文件夹,其索引为整数:
>>> Path.cwd()
WindowsPath('C:/Users/Al/AppData/Local/Programs/Python/Python37')
>>> Path.cwd().parents[0]
WindowsPath('C:/Users/Al/AppData/Local/Programs/Python')
>>> Path.cwd().parents[1]
WindowsPath('C:/Users/Al/AppData/Local/Programs')
>>> Path.cwd().parents[2]
WindowsPath('C:/Users/Al/AppData/Local')
>>> Path.cwd().parents[3]
WindowsPath('C:/Users/Al/AppData')
>>> Path.cwd().parents[4]
WindowsPath('C:/Users/Al')
>>> Path.cwd().parents[5]
WindowsPath('C:/Users')
>>> Path.cwd().parents[6]
WindowsPath('C:/')
查看文件大小和文件夹内容
- 调用
os.path.getsize(path)
返回 path 参数中的文件的大小,以字节表示。 - 调用
os.listdir(path)
返回文件名字符串列表,包含 path 参数中的每个文件。
>>> os.path.getsize('C:\\Windows\\System32\\calc.exe')
776192
>>> os.listdir('C:\\Windows\\System32')
['0409', '12520437.cpx', '12520850.cpx', '5U877.ax', 'aaclient.dll',
--snip--
'xwtpdui.dll', 'xwtpw32.dll', 'zh-CN', 'zh-HK', 'zh-TW', 'zipfldr.dll']
查看这个目录内所有文件大小的总和,可以同时使用 os.path.getsize()
和 os.listdir()
。
>>> totalSize = 0
>>> for filename in os.listdir('C:\\Windows\\System32'):
totalSize = totalSize + os.path.getsize(os.path.join('C:\\Windows\\System32', filename))
>>> print(totalSize)
1117846456
使用 Glob 模式修改文件列表
如果希望处理特定的文件,那么使用 glob()
方法要比使用 listdir()
简单。Path 对象有一个 glob()
方法,根据 glob 模式列出文件夹的内容。Glob 模式类似于命令行命令中经常使用的正则表达式的简化形式。glob()
方法返回一个生成器对象,将它传递给 list()
,以便查看。
>>> p = Path('C:/Users/Al/Desktop')
>>> p.glob('*')
<generator object Path.glob at 0x000002A6E389DED0>
>>> list(p.glob('*')) # Make a list from the generator.
[WindowsPath('C:/Users/Al/Desktop/1.png'), WindowsPath('C:/Users/Al/Desktop/22-ap.pdf'), WindowsPath('C:/Users/Al/Desktop/cat.jpg'),
--snip--
WindowsPath('C:/Users/Al/Desktop/zzz.txt')]
星号(*)表示“任意字符的倍数”,因此 p.glob('*')
返回存储在 p
中的路径中的所有文件的生成器。
与正则表达式类似,可以创建复杂的表达式:
>>> list(p.glob('*.txt')) # Lists all text files.
[WindowsPath('C:/Users/Al/Desktop/foo.txt'),
--snip--
WindowsPath('C:/Users/Al/Desktop/zzz.txt')]
问号(?)表示任意单个字符:
>>> list(p.glob('project?.docx'))
[WindowsPath('C:/Users/Al/Desktop/project1.docx'), WindowsPath('C:/Users/Al/Desktop/project2.docx'),
--snip--
WindowsPath('C:/Users/Al/Desktop/project9.docx')]
还可以组合星号和问号来创建更复杂的 glob 表达式:
>>> list(p.glob('*.?x?')
[WindowsPath('C:/Users/Al/Desktop/calc.exe'), WindowsPath('C:/Users/Al/Desktop/foo.txt'),
--snip--
WindowsPath('C:/Users/Al/Desktop/zzz.txt')]
通过选择具有特定属性的文件,glob()
方法允许轻松地指定要执行某些操作的目录中的文件。可以使用 for 循环遍历 glob()
返回的生成器:
>>> p = Path('C:/Users/Al/Desktop')
>>> for textFilePathObj in p.glob('*.txt'):
... print(textFilePathObj) # Prints the Path object as a string.
... # Do something with the text file.
...
C:\Users\Al\Desktop\foo.txt
C:\Users\Al\Desktop\spam.txt
C:\Users\Al\Desktop\zzz.txt
如果希望对目录中的每个文件执行某些操作,可以使用 os.listdir(p)
或 p.glob('*')
。
检查路径有效性
- 调用
os.path.exists(path)
判断参数所指的文件或文件夹是否存在,如果存在,返回 True,如果不存在,返回 False。 - 调用
os.path.isfile(path)
返回布尔值,如果 path 参数存在,且是一个文件,返回 True,否则返回 False。 - 调用
os.path.isdir(path)
返回布尔值,如果 path 参数存在,且是一个文件夹,返回 True,否则返回 False。
>>> os.path.exists('C:\\Windows')
True
>>> os.path.exists('C:\\some_made_up_folder')
False
>>> os.path.isdir('C:\\Windows\\System32')
True
>>> os.path.isfile('C:\\Windows\\System32')
False
>>> os.path.isdir('C:\\Windows\\System32\\calc.exe')
False
>>> os.path.isfile('C:\\Windows\\System32\\calc.exe')
True
利用 os.path.exists()
函数,可以确定 DVD 或闪存盘当前是否连在计算机上。
>>> os.path.exists('D:\\') # 没有插入闪存盘
False
【python 让繁琐工作自动化】目录
【python 让繁琐工作自动化】第9章 读写文件 (2)
学习网站:
https://automatetheboringstuff.com/chapter8/
https://automatetheboringstuff.com/2e/chapter9/