Python中文件处理

Python 有几个内置的模块和函数来处理文件。这些功能分布在几个模块上,例如os,os``os.pathshutilpathlib,仅举几例。本文在一个地方收集了许多您需要了解的函数,以便对 Python 中的文件执行最常见的操作。


open

使用Python读取和写入数据非常简单。为此,必须首先在适当的模式下打开文件。下面是如何使用 Python 的“with open(...)作为...”打开文本文件并读取其内容的模式:

with open('data.txt', 'r') as f:
    data = f.read()

在上面的示例中,open() 打开文件进行读取或写入,并返回一个文件句柄(在本例中为 f),该句柄提供可用于读取或写入文件数据的方法。有关如何读取和写入文件的更多信息,请查看在 Python 中读取和写入文件和在 Python 中使用文件 I/O

获取目录列表

内置os 模块具有许多有用的功能,可用于列出目录内容和过滤结果。要获取文件系统中特定目录中所有文件和文件夹的列表,请在 Python 的旧版本中使用 os.listdir() 或 Python 3.x 中的 os.listdir()``os.scandir() os.scandir()

旧版Python版本中的目录列表

在 Python 3 之前的 Python 版本中,os.listdir() 是用于获取目录列表的方法:

>>> import os
>>> entries = os.listdir('my_directory/')

os.listdir() 返回一个 Python 列表,其中包含 path 参数给出的目录中的文件和子目录的名称:

>>> os.listdir('my_directory/')
['sub_dir_c', 'file1.py', 'sub_dir_b', 'file3.txt', 'file2.csv', 'sub_dir']

现代Python版本中的目录列表

在现代版本的 Python 中,os.listdir() 的替代方案是使用 os.listdir()os.scandir() pathlib.Path().

os.scandir() 是在 Python 3.5 中引入的,并记录在 PEP 471 中。os.scandir() 在调用时返回一个迭代器,而不是一个列表:

>>> import os
>>> entries = os.scandir('my_directory/')
>>> entries
<posix.ScandirIterator object at 0x7f5b047f3690>

ScandirIterator 指向当前目录中的所有条目。您可以遍历迭代器的内容并打印出文件名:

import os

with os.scandir('my_directory/') as entries:
    for entry in entries:
        print(entry.name)

在这里,os.scandir()with 语句结合使用,因为它支持上下文管理器协议。使用上下文管理器关闭迭代器,并在迭代器用尽后自动释放获取的资源。结果是 my_directory/ 中文件名的打印出来,就像您在 os.listdir() 示例中看到的那样:

获取目录列表的另一种方法是使用 pathlib 模块:

from pathlib import Path

entries = Path('my_directory/')
for entry in entries.iterdir():
    print(entry.name)

Path 返回的对象是 PosixPathPath 对象,具体取决于WindowsPath操作系统。

pathlib.Path() 对象有一个 .iterdir() 方法,用于创建目录中所有文件和文件夹的迭代器.iterdir() 生成的每个条目都包含有关文件或目录的信息,例如其名称和文件属性。pathlib 最初是在 Python 3.4 中引入的,它是 Python 的一个很好的补充,它为文件系统提供了一个面向对象的接口。

在上面的示例中,您调用 pathlib.Path() 并向其传递一个路径参数。接下来是调用 .iterdir() 以获取my_directory中所有文件和目录的列表.

pathlib 提供了一组类,以简单、面向对象的方式对路径进行了大多数常见操作。使用 pathlib 的效率更高,如果不是同样有效的,如果不是同样有效的,也是在 os。在os上使用 pathlib 的另一个好处是,它减少了操作文件系统路径所需的导入次数。有关更多信息,请阅读 Python 3 的 pathlib 模块:驯服文件系统.

注意:要开始使用 pathlib,请查看 Python 基础知识:文件系统操作和相关练习.

使用pathlib.Path() 或 os.scandir() 而不是 os.listdir() 是获取目录列表的首选方法,尤其是在处理需要文件类型和文件属性信息的代码时。os.listdir()``pathlib.Path() 提供了 osshutil 中的许多文件和路径处理功能,它的方法比这些模块中的一些方法更有效。我们将很快讨论如何获取文件属性。

列出目录中的所有文件

本节将向您展示如何使用 os.listdir()``os.scandir()pathlib.Path()要过滤掉目录并仅列出由 os.listdir() 生成的目录列表中的文件,请使用 os.path:

import os

# List all files in a directory using os.listdir
basepath = 'my_directory/'
for entry in os.listdir(basepath):
    if os.path.isfile(os.path.join(basepath, entry)):
        print(entry)

在这里,对 os.listdir()) 的调用返回指定路径中所有内容的列表,然后该列表被 os.path.isfile() 过滤以仅打印出文件而不是目录。

列出目录中文件的一种更简单的方法是使用 os.scandir()pathlib.Path():

import os

# List all files in a directory using scandir()
basepath = 'my_directory/'
with os.scandir(basepath) as entries:
    for entry in entries:
        if entry.is_file():
            print(entry.name)
            

使用 os.scandir() 的优点是看起来比使用os.listdir() 更干净、更容易理解,即使它的代码长一行。如果对象是文件,则对 ScandirIterator 中的每个项目调用 entry.is_file() 将返回 True

from pathlib import Path

basepath = Path('my_directory/')
files_in_basepath = basepath.iterdir()
for item in files_in_basepath:
    if item.is_file():
        print(item.name)

Dan Bader 有一篇关于生成器表达式和列表推导的优秀文章

from pathlib import Path

# List all files in directory using pathlib
basepath = Path('my_directory/')
files_in_basepath = (entry for entry in basepath.iterdir() if entry.is_file())
for item in files_in_basepath:
    print(item.name)

列出子目录

#方法1
import os

# List all subdirectories using os.listdir
basepath = 'my_directory/'
for entry in os.listdir(basepath):
    if os.path.isdir(os.path.join(basepath, entry)):
        print(entry)

        
#方法2       
import os

# List all subdirectories using scandir()
basepath = 'my_directory/'
with os.scandir(basepath) as entries:
    for entry in entries:
        if entry.is_dir():
            print(entry.name)
      
#方法3    
from pathlib import Path

# List all subdirectory using pathlib
basepath = Path('my_directory/')
for entry in basepath.iterdir():
    if entry.is_dir():
        print(entry.name)

获取文件属性

Python 使检索文件大小和修改时间等文件属性变得容易。这是通过 os.stat()、os.scandir()os.stat()``pathlib.Path().

os.scandir()pathlib.Path() 检索组合了文件属性的目录列表。这可能比使用 os.listdir() 列出文件然后获取每个文件的文件属性信息更有效。

下面的示例显示了如何获取 my_directory/ 中的文件的上次修改时间。输出以秒为单位:

>>> import os
>>> with os.scandir('my_directory/') as dir_contents:
...     for entry in dir_contents:
...         info = entry.stat()
...         print(info.st_mtime)
...
1539032199.0052035
1539032469.6324475
1538998552.2402923
1540233322.4009316
1537192240.0497339
1540266380.3434134

os.scandir() 返回一个 ScandirIterator 对象。ScandirIterator 对象中的每个条目都有一个 .stat() 方法,用于检索有关它指向的文件或目录的信息。.stat() 提供文件大小和上次修改时间等信息。在上面的示例中,代码打印出 st_mtime 属性,即上次修改文件内容的时间。

pathlib 模块具有相应的方法来检索给出相同结果的文件信息:

>>> from pathlib import Path
>>> current_dir = Path('my_directory')
>>> for path in current_dir.iterdir():
...     info = path.stat()
...     print(info.st_mtime)
...
1539032199.0052035
1539032469.6324475
1538998552.2402923
1540233322.4009316
1537192240.0497339
1540266380.3434134

在上面的示例中,代码循环遍历 .iterdir()) 返回的对象,并通过目录列表中每个文件的 .stat() 调用检索文件属性。st_mtime 属性返回一个浮点值,该值表示自纪元以来的秒数。若要转换 st_mtime 返回的值以用于显示目的,可以编写一个帮助程序函数将秒转换为datetime对象:

from datetime import datetime
from os import scandir

def convert_date(timestamp):
    d = datetime.utcfromtimestamp(timestamp)
    formated_date = d.strftime('%d %b %Y')
    return formated_date

def get_files():
    dir_entries = scandir('my_directory/')
    for entry in dir_entries:
        if entry.is_file():
            info = entry.stat()
            print(f'{entry.name}\t Last Modified: {convert_date(info.st_mtime)}')

这将首先获取my_directory中的文件列表及其属性,然后调用 convert_date() 将每个文件的上次修改时间转换为人类可读的形式。convert_date() 利用 .strftime() 将以秒为单位的时间转换为字符串。

传递给 .strftime() 的参数如下:

  • %d月份中的某天
  • %b月份,缩写形式
  • %Y年份

这些指令一起生成如下所示的输出:

>>> get_files()
file1.py        Last modified:  04 Oct 2018
file3.txt       Last modified:  17 Sep 2018
file2.txt       Last modified:  17 Sep 2018

创建目录

功能 描述
os.mkdir() 创建单个子目录
pathlib.Path.mkdir() 创建单个或多个目录
os.makedirs() 创建多个目录,包括中间目录

os.makedirs() 类似于 os.mkdir()两者之间的区别在于,os.makedirs() 不仅可以创建单独的目录,还可以用于创建目录树。换句话说,它可以创建任何必要的中间文件夹,以确保存在完整路径。

.makedirs() 创建具有默认权限的目录。如果需要创建具有不同权限的目录,请调用 .makedirs() 并传入您希望在其中创建目录的模式:

import os

os.makedirs('2018/10/05', mode=0o770)

这将创建 2018/10/05 目录结构,并为所有者和组用户提供读取、写入和执行权限。默认模式为 0o777,现有父目录的文件权限位不会更改。有关文件权限以及如何应用模式的更多详细信息,请参阅文档.

运行tree以确认应用了正确的权限:

$ tree -p -i .
.
[drwxrwx---]  2018
[drwxrwx---]  10
[drwxrwx---]  05

文件名模式匹配

使用上述方法之一获取目录中的文件列表后,您很可能希望搜索与特定模式匹配的文件。

以下是您可以使用的方法和功能:

  • endswith()) 和 startswith() 字符串方法
  • fnmatch.fnmatch()
  • glob.glob()
  • pathlib.Path.glob()

使用字符串方法

Python 有几个内置的方法来修改和操作字符串。其中两个方法,.startswith()) 和 .endswith(),在文件名中搜索模式时很有用。为此,首先获取目录列表,然后迭代它:

>>> import os

>>> # Get .txt files
>>> for f_name in os.listdir('some_directory'):
...     if f_name.endswith('.txt'):
...         print(f_name)

使用fnmatch的简单文件名匹配模式

字符串方法的匹配能力受到限制。fnmatch具有更高级的模式匹配功能和方法。我们将考虑 fnmatch.fnmatchfnmatch.fnmatch()一个支持使用 *? 等通配符来匹配文件名的函数。例如,要使用 fnmatch 查找目录中的所有.txt文件,您需要执行以下操作:

>>> import os
>>> import fnmatch

>>> for file_name in os.listdir('some_directory/'):
...     if fnmatch.fnmatch(file_name, '*.txt'):
...         print(file_name)

更高级的模式匹配

假设您要查找满足特定条件.txt文件。例如,您可能只对查找包含data、一组下划线之间的数字以及文件名中的backup``.txt文件感兴趣。类似于data_01_backupdata_02_backupdata_03_backup.

使用 fnmatch.fnmatch()你可以这样做:

>>> for filename in os.listdir('.'):
...     if fnmatch.fnmatch(filename, 'data_*_backup.txt'):
...         print(filename)

使用 glob 的文件名模式匹配

另一个有用的模式匹配模块是 glob.

glob` 模块中的 .glob() 的工作方式与 fnmatch.fnmatch() 类似,但与 fnmatch.fnmatch() 不同的是,它将以句点`.``.glob()` `fnmatch.fnmatch()` 开头的文件视为特殊文件。`fnmatch.fnmatch()

UNIX 和相关系统将带有通配符(如 ?* )的名称模式转换为文件列表。这称为通配。

例如,在 UNIX 外壳中键入 mv mv *.py python_files/ 会将 (mv) 所有扩展名为 .py 的文件从当前目录移动到目录python_files。* * 字符是通配符,表示“任意数量的字符”,*.py 是 glob 模式。此外壳功能在 Windows 操作系统中不可用。glob 模块在 Python 中添加了此功能,使 Windows 程序能够使用此功能。

下面是如何使用 glob 搜索当前目录中所有 Python (.py) 源文件的示例:

>>> import glob
>>> glob.glob('*.py')
['admin.py', 'tests.py']

#查找文件名中包含数字的所有文本 (.txt) 文件:
>>> import glob
>>> for name in glob.glob('*[0-9]*.txt'):
...     print(name)


# glob 也可以轻松地在子目录中递归搜索文件:
>>> import glob
>>> for file in glob.iglob('**/*.py', recursive=True):
...     print(file)

glob.glob('*.py') 搜索当前目录中具有.py扩展名的所有文件,并将它们作为列表返回。glob 还支持 shell 样式的通配符来匹配模式:

此示例使用 glob.iglob() 在当前目录和子目录中搜索.py文件。将recursive=True 作为参数传递给 .iglob() 会使其搜索当前目录和任何子目录中.py文件。glob.iglob()) 和 glob.glob() 之间的区别在于 .iglob() 返回迭代器而不是列表。

遍历目录和处理文件

一个常见的编程任务是遍历目录树并处理树中的文件。让我们探讨如何使用内置的 Python 函数 os.walk() 来做到这一点。os.walk() 用于通过自上而下或自下而上遍历目录树来生成目录树中的文件名。出于本节的目的,我们将操作以下目录树:

.
|
├── folder_1/
|   ├── file1.py
|   ├── file2.py
|   └── file3.py
|
├── folder_2/
|   ├── file4.py
|   ├── file5.py
|   └── file6.py
|
├── test1.txt
└── test2.txt

以下示例展示了如何使用 os.walk() 列出目录树中的所有文件和目录.

os.walk() 默认以自上而下的方式遍历目录:

# Walking a directory tree and printing the names of the directories and files
for dirpath, dirnames, files in os.walk('.'):
    print(f'Found directory: {dirpath}')
    for file_name in files:
        print(file_name)

os.walk() 在循环的每次迭代中返回三个值:

  1. 当前文件夹的名称
  2. 当前文件夹中的文件夹列表
  3. 当前文件夹中的文件列表

在每次迭代中,它都会打印出找到的子目录和文件的名称:

Found directory: .
test1.txt
test2.txt
Found directory: ./folder_1
file1.py
file3.py
file2.py
Found directory: ./folder_2
file4.py
file5.py
file6.py

要以自下而上的方式遍历目录树,请将 topdown=False 关键字参数传递给 os.walk():

for dirpath, dirnames, files in os.walk('.', topdown=False):
    print(f'Found directory: {dirpath}')
    for file_name in files:
        print(file_name)
        
"""
Found directory: ./folder_1
file1.py
file3.py
file2.py
Found directory: ./folder_2
file4.py
file5.py
file6.py
Found directory: .
test1.txt

"""

创建临时文件和目录

Python 提供了一个方便的模块,用于创建临时文件和目录,称为tempfile.

tempfile 可用于在程序运行时在文件或目录中临时打开和存储数据。临时文件处理程序处理完临时文件后删除tempfile的过程。

以下是创建临时文件的方法

from tempfile import TemporaryFile

# Create a temporary file and write some data to it
fp = TemporaryFile('w+t')
fp.write('Hello universe!')

# Go back to the beginning and read data from file
fp.seek(0)
data = fp.read()

# Close the file, after which it will be removed
fp.close()

第一步是从临时文件模块导入TemporaryFiletempfile接下来,使用 TemporaryFile() 方法创建一个类似对象的文件,方法是调用它并传递要在其中打开文件的模式。这将创建并打开一个可用作临时存储区域的文件。

在上面的示例中,模式为'w+t',这使得临时文件在写入模式下创建tempfile。无需为临时文件指定文件名,因为它将在脚本运行完毕后销毁。

写入文件后,可以从中读取并在处理完文件后关闭它。关闭文件后,将从文件系统中删除该文件。如果需要命名使用临时文件生成的临时文件,请使用tempfile``tempfile.NamedTemporaryFile().

使用临时文件创建的临时文件和目录存储在用于存储tempfile的特殊系统目录中。Python 搜索标准的目录列表,以查找用户可以在其中创建文件的目录。

在 Windows 上,目录依次为 C:`C:\TEMP\TMP、\\TEMPC:\TMP\TMP在所有其他平台上,目录依次为 /tmp、/var/tmp 和 /usr/tmp``/tmp/var/tmp作为最后的手段,临时文件会将tempfile`和目录保存在当前目录中。

.TemporaryFile() 也是一个上下文管理器,因此它可以与 with 语句结合使用。使用上下文管理器负责在读取文件后自动关闭和删除文件:

with TemporaryFile('w+t') as fp:
    fp.write('Hello universe!')
    fp.seek(0)
    fp.read()
# File is now closed and removed

这将创建一个临时文件并从中读取数据。读取文件内容后,将关闭临时文件并从文件系统中删除。

临时文件也可用于创建tempfile目录。让我们看看如何使用tempfile.TemporaryDirectory():

>>> import tempfile
>>> with tempfile.TemporaryDirectory() as tmpdir:
...     print('Created temporary directory ', tmpdir)
...     os.path.exists(tmpdir)
...
Created temporary directory  /tmp/tmpoxbkrm6c
True

>>> # Directory contents have been removed
...
>>> tmpdir
'/tmp/tmpoxbkrm6c'
>>> os.path.exists(tmpdir)
False

调用tempfile.TemporaryDirectory() 在文件系统中创建一个临时目录,并返回一个表示此目录的对象。在上面的示例中,目录是使用上下文管理器创建的,目录的名称存储在 tmpdir 中。第三行打印出临时目录的名称,os.path.exists(tmpdir) 确认该目录是否实际上是在文件系统中创建的。

上下文管理器脱离上下文后,将删除临时目录,并且对 os.path.exists(tmpdir) 的调用返回 False,这意味着该目录已被成功删除。

删除文件和目录

您可以使用 osshutilpathlib 模块中的方法删除单个文件、目录和整个目录树。以下各节介绍如何删除不再需要的文件和目录。

删除 Python 中的文件

要删除单个文件,请使用 pathlib.Path.unlink()(), os.remove()os.unlink().

os.remove()) 和 os.unlink() 在语义上是相同的。要使用 os.remove() 删除文件,请执行以下操作:

import os

data_file = 'C:\\Users\\vuyisile\\Desktop\\Test\\data.txt'
os.remove(data_file)

使用 os.unlink() 删除文件类似于使用 os.remove() 删除文件的方式os.unlink():

import os

data_file = 'C:\\Users\\vuyisile\\Desktop\\Test\\data.txt'
os.unlink(data_file)

在文件上调用 .unlink()) 或 .remove() 会从文件系统中删除该文件。如果传递给它们的路径指向目录而不是文件,则这两个函数将抛出 OSError。为了避免这种情况,您可以检查您尝试删除的内容是否确实是一个文件,并且仅在文件是时才删除它,或者您可以使用异常处理来处理 OSError:

import os

data_file = 'home/data.txt'

# If the file exists, delete it
if os.path.isfile(data_file):
    os.remove(data_file)
else:
    print(f'Error: {data_file} not a valid filename')

最后,你也可以使用 pathlib.Path.unlink() 删除文件:

from pathlib import Path

data_file = Path('home/data.txt')

try:
    data_file.unlink()
except IsADirectoryError as e:
    print(f'Error: {data_file} : {e.strerror}')

这将创建一个名为 data_filePath 对象,该对象指向文件。在data_file上调用 .remove() 将删除 home/data.txt。如果data_file指向目录,则会引发 IsADirectoryError。值得注意的是,上面的 Python 程序与运行它的用户具有相同的权限。如果用户没有删除文件的权限,则会引发PermissionError

删除目录

标准库提供以下删除目录的功能:

  • os.rmdir()
  • pathlib.Path.rmdir()
  • shutil.rmtree()

要删除单个目录或文件夹,请使用 os.rmdir()pathlib.rmdir()仅当您尝试删除的目录为空时,这两个函数才有效。如果目录不为空,则会引发 OSError。以下是删除文件夹的方法:

import os

trash_dir = 'my_documents/bad_dir'

try:
    os.rmdir(trash_dir)
except OSError as e:
    print(f'Error: {trash_dir} : {e.strerror}')

在这里,trash_dir目录通过将路径传递给 os.rmdir() 来删除。如果目录不为空,则会在屏幕上打印一条错误消息:

Traceback (most recent call last):
  File '<stdin>', line 1, in <module>
OSError: [Errno 39] Directory not empty: 'my_documents/bad_dir'

或者,您可以使用 pathlib 删除目录:

from pathlib import Path

trash_dir = Path('my_documents/bad_dir')

try:
    trash_dir.rmdir()
except OSError as e:
    print(f'Error: {trash_dir} : {e.strerror}')

在这里,您将创建一个指向要删除的目录的 Path 对象。在 Path 对象上调用 .rmdir() 将删除它(如果它为空)。

删除整个目录树

要删除非空目录和整个目录树,Python 提供了 shutil.rmtree():

import shutil

trash_dir = 'my_documents/bad_dir'

try:
    shutil.rmtree(trash_dir)
except OSError as e:
    print(f'Error: {trash_dir} : {e.strerror}')

当调用 shutil.rmtree() 时,trash_dir 中的所有内容都将被删除。在某些情况下,您可能希望以递归方式删除空文件夹。您可以使用上面讨论的方法之一与 os.walk() 一起执行此操作:

import os

for dirpath, dirnames, files in os.walk('.', topdown=False):
    try:
        os.rmdir(dirpath)
    except OSError as ex:
        pass

这将沿着目录树向下移动,并尝试删除它找到的每个目录。如果目录不为空,则会引发 OSError 并跳过该目录。下表列出了本节中介绍的功能:

功能 描述
os.remove() 删除文件但不删除目录
os.unlink() os.remove() 相同,并删除单个文件
pathlib.Path.unlink() 删除文件且无法删除目录
os.rmdir() 删除空目录
pathlib.Path.rmdir() 删除空目录
shutil.rmtree() 删除整个目录树,可用于删除非空目录

复制、移动和重命名文件和目录

Python 随 shutil 模块一起提供。shutil 是 shell 实用程序的缩写。它提供了许多对文件的高级操作,以支持文件和目录的复制、存档和删除。在本节中,您将学习如何移动和复制文件和目录。

在 Python 中复制文件

shutil 提供了几个用于复制文件的功能。最常用的函数是 shutil.copy()shutil.copy2()要使用 shutil.copy() 将文件从一个位置复制到另一个位置,请执行以下操作:

import shutil

src = 'path/to/file.txt'
dst = 'path/to/dest_dir'
shutil.copy(src, dst)

shutil.copy() 与基于 UNIX 的系统中的 cp 命令相当。shutil.copy(src, dst) 会将文件 src 复制到 dst 中指定的位置。如果 dst 是一个文件,则该文件的内容将替换为 src 的内容。如果 dst 是一个目录,则 src 将被复制到该目录中。shutil.copy() 仅复制文件的内容和文件的权限。不会保留其他元数据,例如文件的创建和修改时间

要在复制时保留所有文件元数据,请使用 shutil.copy2():

import shutil

src = 'path/to/file.txt'
dst = 'path/to/dest_dir'
shutil.copy2(src, dst)

使用 .copy2() 可以保留有关文件的详细信息,例如上次访问时间、权限位、上次修改时间和标志。

复制目录

虽然 shutil.copy()) 只复制一个文件,shutil.copytree() 将复制整个目录和其中包含的所有内容。shutil.copytree(src, dest)有两个参数:源目录和文件和文件夹将复制到的目标目录。

下面是如何将一个文件夹的内容复制到其他位置的示例:

>>> import shutil
>>> shutil.copytree('data_1', 'data1_backup')
'data1_backup'

在此示例中,.copytree()data_1的内容复制到新位置data1_backup并返回目标目录。目标目录必须不存在。将创建它以及缺少父目录。shutil.copytree() 是备份文件的好方法。

移动文件和目录

要将文件或目录移动到其他位置,请使用 shutil.move(src, dst).

src 是要移动的文件或目录,dst 是目标:

>>> import shutil
>>> shutil.move('dir_1/', 'backup/')
'backup'

重命名文件和目录

Python 包括用于重命名文件和目录的 os.rename(src, dst)

>>> os.rename('first.zip', 'first_01.zip')

上面的行将first.zip重命名为 first_01.zip。如果目标路径指向目录,它将引发一个 OSError.

重命名文件或目录的另一种方法是使用 pathlib 模块中的 rename()

>>> from pathlib import Path
>>> data_file = Path('data_01.txt')
>>> data_file.rename('data.txt')

要使用 pathlib 重命名文件pathlib,请先创建一个 pathlib.Path()要替换的文件的路径的 Path() 对象。下一步是在路径对象上调用 rename()并为要重命名的文件或目录传递新的文件名。

归档

存档是将多个文件打包为一个文件的便捷方法。两种最常见的存档类型是 ZIP 和 TAR。您编写的 Python 程序可以从存档中创建、读取和提取数据。在本节中,您将学习如何读取和写入这两种存档格式。

读取 ZIP 文件

zipfile 模块是一个低级模块,是 Python 标准库的一部分。zipfile具有易于打开和提取ZIP文件的功能。要读取 ZIP 文件的内容,首先要做的是创建一个 ZipFile 对象。ZipFile 对象类似于使用 open() 创建的文件对象。ZipFile 也是一个上下文管理器,因此支持 with 语句:

import zipfile

with zipfile.ZipFile('data.zip', 'r') as zipobj:

在这里,您将创建一个 ZipFile 对象,传入要在读取模式下打开的 ZIP 文件的名称。打开 ZIP 文件后,可以通过 zipfile 模块提供的功能访问有关存档的信息。上面示例中的 data.zip 存档是从名为 data 的目录创建的,该目录总共包含 5 个文件和 1 个子目录:

.
|
├── sub_dir/
|   ├── bar.py
|   └── foo.py
|
├── file1.py
├── file2.py
└── file3.py

要获取存档中的文件列表,请在 ZipFile 对象上调用 namelist()

import zipfile

with zipfile.ZipFile('data.zip', 'r') as zipobj:
    zipobj.namelist()
"""
['file1.py', 'file2.py', 'file3.py', 'sub_dir/', 'sub_dir/bar.py', 'sub_dir/foo.py']

"""

.namelist() 返回存档中文件和目录的名称列表。要检索有关存档中文件的信息,请使用 .getinfo():

import zipfile

with zipfile.ZipFile('data.zip', 'r') as zipobj:
    bar_info = zipobj.getinfo('sub_dir/bar.py')
    bar_info.file_size#15277

.getinfo() 返回一个 ZipInfo 对象,该对象存储有关存档中单个成员的信息。要获取有关存档中文件的信息,请将其路径作为参数传递给 .getinfo()使用 getinfo()您可以检索有关存档成员的信息,例如上次修改文件的日期、压缩大小和完整文件名。访问.file_size将检索文件的原始大小(以字节为单位)

以下示例演示如何在 Python REPL 中检索有关存档文件的更多详细信息。假设 zipfile 模块已导入,bar_info与您在前面的示例中创建的对象相同:

>>> bar_info.date_time
(2018, 10, 7, 23, 30, 10)
>>> bar_info.compress_size
2856
>>> bar_info.filename
'sub_dir/bar.py'

ZipFile 支持上下文管理器协议,这就是为什么您可以将其与 with 语句一起使用的原因。执行此操作会在完成 ZipFile 对象后自动关闭该对象。尝试从关闭的 ZipFile 对象中打开或提取文件将导致错误。

提取压缩文档

zipfile 模块允许您通过 .extract() 和 .extractall() 从 ZIP 存档.extract()中提取一个或多个文件.

默认情况下,这些方法将文件提取到当前目录。它们都采用可选的 path 参数,该参数允许您指定要将文件提取到的不同目录。如果该目录不存在,则会自动创建该目录。要从存档中提取文件,请执行以下操作:

>>> import zipfile
>>> import os

>>> os.listdir('.')
['data.zip']

>>> data_zip = zipfile.ZipFile('data.zip', 'r')

>>> # Extract a single file to current directory
>>> data_zip.extract('file1.py')
'/home/terra/test/dir1/zip_extract/file1.py'

>>> os.listdir('.')
['file1.py', 'data.zip']

>>> # Extract all files into a different directory
>>> data_zip.extractall(path='extract_dir/')

>>> os.listdir('.')
['file1.py', 'extract_dir', 'data.zip']

>>> os.listdir('extract_dir')
['file1.py', 'file3.py', 'file2.py', 'sub_dir']

>>> data_zip.close()

第三行代码是对 os.listdir() 的调用,它显示当前目录只有一个文件,data.zip.

接下来,在读取模式下data.zip并调用 .extract() 从中提取 file1.py.extract() 返回提取文件的完整文件路径。由于没有指定路径,.extract() 提取 file1.py 到当前目录。

下一行打印一个目录列表,显示当前目录现在除了包含原始存档之外还包括提取的文件。后面的行显示了如何将整个存档提取到zip_extract目录中。.extractall() 创建extract_dir并将data.zip提取到其中。最后一行关闭 ZIP 存档。

从受密码保护的档案中提取数据

zipfile 支持提取受密码保护的 ZIP。要提取受密码保护的 ZIP 文件,请将密码作为参数传递给 ..extract()() 或 .extractall() 方法:

>>> import zipfile

>>> with zipfile.ZipFile('secret.zip', 'r') as pwd_zip:
...     # Extract from a password protected archive
...     pwd_zip.extractall(path='extract_dir', pwd='Quish3@o')

这将在读取模式下打开secret.zip存档。向 .extractall() 提供密码,并将存档内容提取到extract_dir。由于 with 语句,提取完成后,存档会自动关闭。

创建新的ZIP文档

要创建新的 ZIP 存档,请在写入模式 (w) 下打开 ZipFile 对象并添加要存档的文件:

>>> import zipfile

>>> file_list = ['file1.py', 'sub_dir/', 'sub_dir/bar.py', 'sub_dir/foo.py']
>>> with zipfile.ZipFile('new.zip', 'w') as new_zip:
...     for name in file_list:
...         new_zip.write(name)

在此示例中,new_zip以写入模式打开,file_list中的每个文件都添加到存档中。with 语句套件完成后,new_zip将关闭。在写入模式下打开 ZIP 文件会擦除存档的内容并创建新的存档。

要将文件添加到现有存档,请在追加模式下打开 ZipFile 对象,然后添加文件:

>>> # Open a ZipFile object in append mode
>>> with zipfile.ZipFile('new.zip', 'a') as new_zip:
...     new_zip.write('data.txt')
...     new_zip.write('latin.txt')

在这里,您将在追加模式下打开在上一示例中创建的new.zip存档。在追加模式下打开 ZipFile 对象允许您向 ZIP 文件添加新文件,而无需删除其当前内容。将文件添加到 ZIP 文件后,with 语句将脱离上下文并关闭 ZIP 文件。

打开Tar压缩包

TAR文件是未压缩的文件存档,如ZIP。它们可以使用gzip,bzip2和lzma压缩方法进行压缩。TarFile 类允许读取和写入 TAR 存档。

执行以下操作以从存档中读取:

import tarfile

with tarfile.open('example.tar', 'r') as tar_file:
    print(tar_file.getnames())

tarfile 对象像大多数类似文件的对象一样打开。它们有一个 open() 函数,该函数采用确定如何打开文件的模式。

使用“'r'``'w'”或'a'模式打开未压缩的 TAR 文件,分别用于读取、写入和追加。要打开压缩的 TAR 文件,请将 mode 参数传递给 tarfile.open()该参数的形式为 filemode[:compression]下表列出了可以打开 TAR 文件的可能模式:

模式 行动
r 打开存档以透明压缩进行读取
r:gz 打开存档以使用 gzip 压缩进行读取
r:bz2 使用 bzip2 压缩打开存档以供读取
r:xz 打开存档以使用 lzma 压缩进行读取
w 打开存档以进行未压缩的写入
w:gz 打开存档以进行 gzip 压缩写入
w:xz 打开 lzma 压缩写入的存档
a 打开存档以进行追加,无需压缩

.open() 默认为 ''r' 模式。要读取未压缩的 TAR 文件并检索其中文件的名称,请使用 .getnames():

>>> import tarfile

>>> tar = tarfile.open('example.tar', mode='r')
>>> tar.getnames()
['CONTRIBUTING.rst', 'README.md', 'app.py']

这将返回一个包含存档内容名称的列表。

注意:为了向您展示如何使用不同的 tarfile 对象方法,示例中的 TAR 文件在交互式 REPL 会话中手动打开和关闭。

以这种方式与 TAR 文件交互允许您查看运行每个命令的输出。通常,您可能希望使用上下文管理器来打开类似文件的对象。

可以使用特殊属性访问存档中每个条目的元数据:

>>> for entry in tar.getmembers():
...     print(entry.name)
...     print(' Modified:', time.ctime(entry.mtime))
...     print(' Size    :', entry.size, 'bytes')
...     print()
CONTRIBUTING.rst
 Modified: Sat Nov  1 09:09:51 2018
 Size    : 402 bytes

README.md
 Modified: Sat Nov  3 07:29:40 2018
 Size    : 5426 bytes

app.py
 Modified: Sat Nov  3 07:29:13 2018
 Size    : 6218 bytes

在此示例中,您将遍历 .getmembers() 返回的文件列表并打印出每个文件的属性。.getmembers() 返回的对象具有可以通过编程方式访问的属性,例如存档中每个文件的名称、大小和上次修改时间。读取或写入存档后,必须关闭它以释放系统资源。

从 TAR 存档中提取文件

在本节中,您将学习如何使用以下方法从 TAR 存档中提取文件:

  • .extract()
  • .extractfile()
  • .extractall()

要从 TAR 存档中提取单个文件,请使用 extract()传入文件名:

>>> tar.extract('README.md')
>>> os.listdir('.')
['README.md', 'example.tar']

README.md 文件从存档中提取到文件系统。调用 os.listdir() 确认 README.md 文件已成功解压缩到当前目录中。要从存档中解压缩或提取所有内容,请使用 .extractall():

>>> tar.extractall(path="extracted/")

.extractall() 有一个可选的 path 参数来指定提取的文件应该去哪里。在这里,存档被解压缩到extracted的目录中。以下命令显示已成功提取存档:

要提取文件对象以进行读取或写入,请使用 .extractfile()它将文件名或 TarInfo 对象作为参数提取。.extractfile() 返回一个可以读取和使用的类似文件的对象:

>>> f = tar.extractfile('app.py')
>>> f.read()
>>> tar.close()

打开的存档在被读取或写入后应始终关闭。要关闭存档,请在存档文件句柄上调用 .close()或者在创建 tarfile 对象时使用 with 语句,以便在完成后自动关闭存档。这将释放系统资源,并将您对归档所做的任何更改写入文件系统。

创建新的TAR档案

>>> import tarfile

>>> file_list = ['app.py', 'config.py', 'CONTRIBUTORS.md', 'tests.py']
>>> with tarfile.open('packages.tar', mode='w') as tar:
...     for file in file_list:
...         tar.add(file)

>>> # Read the contents of the newly created archive
>>> with tarfile.open('package.tar', mode='r') as t:
...     for member in t.getmembers():
...         print(member.name)
app.py
config.py
CONTRIBUTORS.md
tests.py

首先,列出要添加到存档的文件,这样就不必手动添加每个文件。

下一行使用 with 上下文管理器在写入模式下打开一个名为 packages.tar 的新存档。在写入模式(''w')下打开存档使您能够将新文件写入存档。将删除存档中的任何现有文件,并创建新的存档。

创建并填充归档文件后,with 上下文管理器会自动将其关闭并将其保存到文件系统中。最后三行打开您刚刚创建的存档并打印出其中包含的文件的名称。

要将新文件添加到现有存档,请在追加模式下打开存档 (''a'):

>>> with tarfile.open('package.tar', mode='a') as tar:
...     tar.add('foo.bar')

>>> with tarfile.open('package.tar', mode='r') as tar:
...     for member in tar.getmembers():
...         print(member.name)
app.py
config.py
CONTRIBUTORS.md
tests.py
foo.bar

在追加模式下打开存档允许您向其添加新文件,而无需删除其中已有的文件。

使用压缩存档

tarfile 还可以读取和写入使用 gzip、bzip2 和 lzma 压缩压缩的 TAR 存档。要读取或写入压缩存档,请使用 tarfile.open()以压缩类型的适当模式传递。

例如,要读取或写入使用 gzip 压缩的 TAR 存档,请分别使用 'r:gz' 或 ''w:gz' 模式'r:gz'

>>> files = ['app.py', 'config.py', 'tests.py']
>>> with tarfile.open('packages.tar.gz', mode='w:gz') as tar:
...     tar.add('app.py')
...     tar.add('config.py')
...     tar.add('tests.py')

>>> with tarfile.open('packages.tar.gz', mode='r:gz') as t:
...     for member in t.getmembers():
...         print(member.name)
app.py
config.py
tests.py

'w:gz' 压缩写入的存档,'r:gz' 压缩读取的存档。无法在追加模式下打开压缩的存档。要将文件添加到压缩存档,必须创建新存档。

创建存档的更简单方法

Python 标准库还支持使用 shutil 模块中的高级方法创建 TAR 和 ZIP 存档。shutil 中的归档实用程序允许您创建、读取和提取 ZIP 和 TAR 归档。这些实用程序依赖于较低级别的 tarfilezipfile 模块。

使用 shutil.make_archive() 处理存档

shutil.make_archive() 至少接受两个参数:存档的名称和存档格式。

默认情况下,它将当前目录中的所有文件压缩为 format 参数中指定的存档格式。您可以传入可选的 root_dir 参数来压缩不同目录中的文件。.make_archive() 支持 ziptarbztar 存档格式。gztar

这是如何使用 shutil 创建 TAR 存档:

import shutil

# shutil.make_archive(base_name, format, root_dir)
shutil.make_archive('data/backup', 'tar', 'data/')

这将复制 data/ 中的所有内容,并在文件系统中创建一个名为 backup.tar 的归档并返回其名称。要提取存档,请调用 .unpack_archive():

shutil.unpack_archive('backup.tar', 'extract_dir/')

读取多个文件

Python 支持通过 fileinput 模块从多个输入流或文件列表中读取数据。此模块允许您快速轻松地循环访问一个或多个文本文件的内容。以下是fileinput的典型方式:

import fileinput
for line in fileinput.input()
    process(line)

fileinput 从传递给 sys.argv命令行参数获取其输入。

使用文件输入循环访问多个fileinput

让我们使用 fileinput 来构建一个普通 UNIX cat的粗略版本。cat 实用程序按顺序读取文件,将它们写入标准输出。当在其命令行参数中给定多个文件时,cat 将连接文本文件并在终端中显示结果:

# File: fileinput-example.py
import fileinput
import sys

files = fileinput.input()
for line in files:
    if fileinput.isfirstline():
        print(f'\n--- Reading {fileinput.filename()} ---')
    print(' -> ' + line, end='')
print()



$ python3 fileinput-example.py bacon.txt cupcake.txt
--- Reading bacon.txt ---
 -> Spicy jalapeno bacon ipsum dolor amet in in aute est qui enim aliquip,
 -> irure cillum drumstick elit.
 -> Doner jowl shank ea exercitation landjaeger incididunt ut porchetta.
 -> Tenderloin bacon aliquip cupidatat chicken chuck quis anim et swine.
 -> Tri-tip doner kevin cillum ham veniam cow hamburger.
 -> Turkey pork loin cupidatat filet mignon capicola brisket cupim ad in.
 -> Ball tip dolor do magna laboris nisi pancetta nostrud doner.

--- Reading cupcake.txt ---
 -> Cupcake ipsum dolor sit amet candy I love cheesecake fruitcake.
 -> Topping muffin cotton candy.
 -> Gummies macaroon jujubes jelly beans marzipan.

fileinput允许您检索有关每行的更多信息,例如它是否是第一行(..isfirstline()()),行号(..lineno()())和文件名(.filename())。您可以在此处阅读更多相关信息.


链接:https://realpython.com/working-with-files-in-python/

posted @ 2023-04-11 09:34  LgRun  阅读(37)  评论(0编辑  收藏  举报