9 第九章 文件与文件夹操作

Python 文件与文件夹操作学习笔记

Python中操作文件的相关知识,包括文本文件与二进制文件的区别、文件和文件夹操作相关的函数与标准库的用法,并通过Word文件和Excel文件操作介绍了Python扩展库python-docx和openpyxl的用法。

本章学习目标包括:

  • 了解文件的概念及分类
  • 掌握内置函数open()的用法
  • 熟练运用with关键字
  • 掌握osos.pathshutil标准库中常用函数的用法
  • 掌握递归遍历文件及其子文件夹的原理
  • 了解python-docxopenpyxl等扩展库的用法

一、文件的概念及分类

(一)存储形式

文件存储在各种存储设备上,如磁盘、U 盘、光盘、云盘等。

(二)分类依据及类别

  1. 文本文件
    • 存储常规字符串,由若干文本行组成,每行以换行符\n结尾。
    • 常规字符串能被文本编辑器正常显示、编辑,人类可直接阅读理解,如英文、汉字、数字、标点符号等组成的字符串。
    • 扩展名为.txt.log.ini.c.cpp.py.pyw等的文件属于文本文件,可用gedit、记事本、wordpad等字处理软件编辑。
  2. 二进制文件
    • 常见的如图形图像文件、音视频文件、可执行文件、资源文件、数据库文件、Office 文档等。
    • 无法用记事本或普通字处理软件直接编辑,通常不能被人类直接阅读理解,需相应软件才能正确读取、显示、修改或执行。

二、文件操作基本知识

(一)操作流程

无论是文本文件还是二进制文件,其操作流程基本都是一致的

  1. 打开文件并创建文件对象。
  2. 通过文件对象对文件内容进行读取、写入、删除和修改等操作。
  3. 关闭并保存文件内容。

(二)内置函数open()

  1. 语法及参数含义

    • open(file, mode='r', buffering=1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

    • file:指定要打开或创建的文件名称,可使用相对路径或绝对路径,可用原始字符串减少\输入。

    • mode:取值范围及说明(见表 9-1),指定打开文件后的处理方式,如 “只读”“只写”“读写”“追加”“二进制只读”“二进制读写” 等,默认为 “文本只读模式”。

      模式说明
      r读模式(默认模式,可省略),如果文件不存在则抛出异常
      w写模式,如果文件已存在,先清空原有内容
      x写模式,创建新文件,如果文件已存在则抛出异常
      a追加模式,不覆盖文件中原有内容
      b二进制模式(可与其他模式组合使用),使用二进制模式打开文件时不允许指定 encoding 参数
      t文本模式(默认模式,可省略)
      +读、写模式(可与其他模式组合使用)
    • encoding:只适用于文本模式,指定对文本进行编码和解码的方式,如GBKUTF - 8CP936等,需与文件实际编码格式一致。

  2. 返回值及异常处理

    • 执行正常返回可迭代的文件对象,通过该对象进行读写操作。
    • 若指定文件不存在、访问权限不够、磁盘空间不够等导致创建文件对象失败则抛出异常。
    • 在对文件内容操作完毕后,必须关闭文件对象,这样才能确保所做的修改被保存到文件中
    • 即使编写了关闭文件的代码,也不能完全保证文件能够正常关闭,例如在打开文件后、关闭文件前程序出现错误导致崩溃的情况。为避免这种情况,文中推荐在管理文件对象时使用with关键字(具体可参考 9.2.3 内容)。
    fp=open('file1.txt','r')
    fp=open('file2.txt','w')
    fp.close()
    

(三)文件对象常用方法

如果执行正常,open () 函数返回 1 个文件对象,通过该文件对象可以对文件进行读写操作,文件对象常用方法如表 9-2 所示,具体用法将在后面几节中陆续介绍。

  1. close():把缓冲区内容写入文件,关闭文件并释放文件对象。
  2. read([size]):从文本文件读取size个字符或从二进制文件读取指定字节数,省略size表示读取所有内容,大文件不建议省略。
  3. readline():从文本文件读取一行内容返回。
  4. readlines():把文本文件每行文本存入列表返回,大文件占用内存多不建议使用。
  5. seek(offset, whence):移动文件指针到指定位置(单位字节),whence为 0 从文件头、1 从当前位置、2 从文件尾计算偏移量,默认为 0。
  6. tell():返回文件指针当前位置。
  7. write(s):把字符串s写入文本文件或字节串s写入二进制文件。
  8. writelines(s):把列表s中的字符串依次写入文本文件,不添加换行符。

(四)上下文管理语句with

在实际程序开发中,读写文件应优先考虑使用上下文管理语句 with,关键字 with 可以自动管理资源,不论因为什么原因(哪怕是代码引发了异常)跳出 with 块,总能保证文件被正确关闭,可用于文件操作、数据库连接等场合。用于文件内容读写时,with 语句的用法如下。

  1. 作用

    • 自动管理资源,可以确保文件在使用后被正确地关闭,可用于文件操作、数据库连接等。
  2. 用法示例

    • with open(filename, mode, encoding) as fp:
      
      • 在此处写通过文件对象fp读写文件内容的语句。
  3. 举例(补)

    with open('example.txt', 'r') as file:
        content = file.read()
        print(content)
    
    • 这里,open('example.txt', 'r')返回一个文件对象,这个文件对象就是上下文管理器。
    • as file将文件对象绑定到变量file上。
    • with块内,可以通过file变量对文件进行操作(如read()读取文件内容)。当with块执行结束时,文件会自动关闭,不需要显式地调用file.close()

三、文本文件内容操作案例

(一)写入与读取

例 9-1 将字符串写入文本文件,然后再读取并输出。
基本思路:使用上下文管理语句 with 避免忘记关闭文件,使用文件对象的 write () 方法写入内容,使用 read () 方法从文件中读取内容。

# 定义一个字符串变量s,其中包含了一些文本内容
# 包括"Hello world"、"文本文件的读取方法"和"文本文件的写入方法"
# 每个字符串之间用换行符"\n"分隔
s = "Hello world\n文本文件的读取方法\n文本文件的写入方法\n"

# 使用with语句打开一个名为"sample.txt"的文件,以写入模式("w")打开
# as关键字用于给打开的文件对象赋予一个别名fp
# 在这种情况下,默认使用cp936编码(在Windows系统中常见的中文编码)
with open('sample.txt', 'w') as fp:
    # 使用文件对象fp的write方法将字符串s的内容写入到文件中
    fp.write(s)

# 再次使用with语句打开刚才写入的"sample.txt"文件
# 这里没有指定模式,默认为读取模式("r")
with open('sample.txt') as fp:
    # 使用文件对象fp的read方法读取文件中的内容,并将其打印出来
    # 这里同样默认使用cp936编码
    print(fp.read())

这段代码的功能是:

  1. 首先创建一个包含多行文本的字符串。
  2. 然后将这个字符串写入到一个名为sample.txt的文本文件中。
  3. 最后再从这个文本文件中读取内容并打印出来。

请注意:

  • 如果sample.txt文件不存在,在写入模式下会自动创建。
  • 如果sample.txt文件已存在,在写入模式下会清空原有内容再写入新内容。

(二)遍历文件行

例 9-2 遍历并输出文本文件的所有行内容。
基本思路:对于大的文本文件,一般不建议使用 read () 方法或readlines ()方法一次性读取全部内容,因为这会占用较多的内存。更建议使用readline ()方法逐行读取并处理,或者直接使用 for 循环遍历文件中的每一行并进行必要的处理。

with open('sample.txt') as fp:  # 假设文件采用CP936编码
    for line in fp:
        print(line)  # 文本文件对象可以直接迭代

(三)数据处理与写入

例 9-3 假设文件 data.txt 中有若干行整数,每行有 1 个整数。编写程序读取所有整数,将其按降序排序后再写入文本文件 data_desc.txt 中,每行 1 个整数。

基本思路:虽然不建议使用 readlines () 方法一次性读取全部内容,但也要具体问题具体分析。以本例问题为例,只有把所有数据都读取之后才能进行排序。另外,内置函数 int () 在把字符串转换为整数时,会自动忽略字符串两侧的换行符 \n、空格、制表符等空白字符。

with open('data.txt', 'r') as fp:
    data = fp.readlines()  # 读取所有行,存入列表
data = [int(item) for item in data]  # 列表推导式,转换为数字
data.sort(reverse=True)  # 降序排序
data = [str(item) + "\n" for item in data]  # 将结果转换为字符串
with open('data_desc.txt', 'w') as fp:
    fp.writelines(data)
# 以读取模式('r')打开名为'data.txt'的文件,并将文件对象赋值给fp
with open('data.txt', 'r') as fp:
    # 使用readlines()方法读取文件中的所有行,并将每行作为一个字符串元素存入列表data
    data = fp.readlines()  

# 使用列表推导式将列表data中的每个字符串元素转换为整数
data = [int(item) for item in data]  

# 对列表data中的整数进行降序排序
data.sort(reverse=True)  

# 使用列表推导式将列表data中的每个整数转换为字符串,并在每个字符串后添加换行符'\n'
data = [str(item) + "\n" for item in data]  

# 以写入模式('w')打开名为'data_desc.txt'的文件,并将文件对象赋值给fp
with open('data_desc.txt', 'w') as fp:
    # 使用writelines()方法将列表data中的每个字符串元素写入文件
    fp.writelines(data)

(四)统计最长行

例 9-4 统计文本文件中最长一行的长度和该行的内容。
基本思路:遍历文件所有行,使用选择法找出最长的行及其内容。

with open('sample.txt') as fp:
    result = [0, ""]
    for line in fp:
        t = len(line)  # 使用列表保存某行长度和内容
        if t > result[0]:  # 发现更长的行,保存最新结果
            result = [t, line]
print(result)
with open('sample.txt') as fp:
    # 初始化一个列表result,其中result[0]用于保存最长行的长度(初始化为0),
    # result[1]用于保存最长行的内容(初始化为空字符串)
    result = [0, ""]
    # 循环遍历文件中的每一行
    for line in fp:
        # 计算当前行的长度
        t = len(line)
        # 如果当前行的长度大于之前记录的最长行长度
        if t > result[0]:
            # 更新result列表,将最长行的长度和内容更新为当前行的长度和内容
            result = [t, line]
# 打印结果,结果是一个包含最长行长度和内容的列表
print(result)

四、文件夹操作

(一)os模块

Python 标准库 os 除了提供使用操作系统功能和访问文件系统的简便方法之外,还提供了大量文件与文件夹操作的函数,如表 9 - 3 所示。

函数功能说明
chdir(path)path设为当前工作目录
chmod(path, mode, *, dir_fd=None, follow_symlinks=True)改变文件的访问权限
listdir(path)返回path目录下的文件和目录列表
mkdir(path[, mode=511])创建目录,要求上级目录必须存在
makedirs(path1/path2/…, mode=511)创建多级目录,会根据需要自动创建中间缺失的目录
rmdir(path)删除目录,目录中不能有文件或子文件夹
remove(path)删除指定的文件,要求用户拥有删除文件的权限,并且文件没有只读或其他特殊属性
removedirs(path1/path2/…)删除多级目录,目录中不能有文件
rename(src, dst)重命名文件或目录,可以实现文件的移动,若目标文件已存在则抛出异常,并且不能跨越磁盘或分区
replace(old, new)重命名文件或目录,若目标文件已存在则直接覆盖,不能跨越磁盘或分区
startfile(filepath[, operation])使用关联的应用程序打开指定文件或启动指定应用程序
stat(path)返回文件的所有属性
system()启动外部程序
  1. 函数用法示例

    import os
    import os.path
    
    os.rename(r'C:\test1.txt', r'C:\test2.txt')
    
    for fname in os.listdir('.'):
        if fname.endswith(('.pyc', '.py', '.pyw')):
            pass
    
    os.getcwd()
    # 返回当前工作目录
    'C:\\Python38'
    
    os.mkdir(os.getcwd() + '\\temp')
    # 创建目录
    
    os.chdir(os.getcwd() + '\\temp')
    # 改变当前工作目录
    
    os.getcwd()
    'C:\\Python38\\temp'
    
    os.mkdir(os.getcwd() + '\\test')
    
    os.listdir('.')
    ['test']
    
    os.rmdir('test')
    # 删除目录
    
    os.listdir('.')
    []
    
    import time
    time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(os.stat('test.py').st_ctime))
    '2020-12-28 14:56:57'
    
    os.startfile('notepad.exe')
    # 启动记事本程序
    
    • os.rename('C:\test1.txt', 'C:\test2.txt'):重命名文件。
    • os.getcwd():返回当前工作目录。
    • os.mkdir(os.getcwd() + '\temp'):创建目录。
    • os.chdir(os.getcwd() + "\temp"):改变当前工作目录。
    • os.rmdir("test"):删除目录(目录需为空)。
    • os.listdir('.'):返回当前目录下的文件和目录列表。
    • os.startfile('notepad.exe'):启动记事本程序。
  2. 例题

    例 9-5 使用递归法遍历指定目录下所有子目录和文件。

    基本思路:遍历指定文件夹中所有文件和子文件夹,对于文件则直接输出,对于子文件夹则进入该文件夹继续遍历,重复上面的过程。Python 标准库 os.path 中的 isfile () 函数用来测试一个路径是否为文件,isdir () 函数用来测试一个路径是否为文件夹,见 9.4.2 节对 os.path 模块的介绍。

    from os import listdir
    from os.path import join, isfile, isdir
    
    
    def listDirDepthFirst(directory):
        """
        这个函数用于深度优先遍历指定的目录。
        它会遍历目录中的所有文件和子目录。
        如果是文件,就直接打印文件路径。
        如果是子目录,就先打印子目录路径,然后进入该子目录继续遍历。
        :param directory: 要遍历的目录路径
        """
        # 遍历给定目录中的所有内容(文件和子目录)
        for subPath in listdir(directory):
            # listdir()函数返回的是目录中的文件名和子目录名,这些是相对路径
            # 需要使用join()函数将给定目录和相对路径组合成完整路径
            path = join(directory, subPath)
    
            # 判断路径是否指向一个文件
            if isfile(path):
                # 如果是文件,打印文件的完整路径
                print(path)
            # 判断路径是否指向一个目录
            elif isdir(path):
                # 如果是目录,打印目录的完整路径
                print(path)
                # 递归调用listDirDepthFirst函数来遍历这个子目录
                listDirDepthFirst(path)
    

(二)os.path模块

  1. 提供的功能及函数列表(见表 9-4)

    • 提供大量用于路径判断、切分、连接以及文件夹遍历的方法。

      表 9 - 4 os.path 模块常用成员

      函数功能说明
      abspath(path)返回给定路径的绝对路径
      basename(path)返回指定路径的最后一个分隔符后面的部分
      dirname(p)返回给定路径的最后一个分隔符前面的部分
      exists(path)判断路径是否存在
      getatime(filename)返回文件的最后访问时间
      getctime(filename)返回文件的创建时间
      getmtime(filename)返回文件的最后修改时间
      getsize(filename)返回文件的大小,单位是字节
      isdir(path)判断 path 是否为文件夹
      isfile(path)判断 path 是否为文件
      join(path, *paths)连接两个或多个 path
      split(path)以路径中的最后一个斜线或反斜线为分隔符把路径分隔成两部分,以元组形式返回
      splitext(path)从路径中分隔文件的扩展名
      splitdrive(path)从路径中分隔驱动器的名称
  2. 函数用法示例

    • os.path.dirname(path):返回路径的文件夹名。
    • os.path.basename(path):返回路径的最后一个组成部分。
    • os.path.split(path):以最后一个斜线或反斜线为分隔符把路径分隔成两部分,以元组形式返回。
    • os.path.splitext(path):从路径中分隔文件的扩展名。
    • os.path.splitdrive(path):从路径中分隔驱动器的名称。
    path = 'D:\\mypython_exp\\new_test.txt'
    os.path.dirname(path)
    # 返回路径的文件夹名
    'D:\\mypython_exp'
    
    os.path.basename(path)
    # 返回路径的最后一个组成部分
    'new_test.txt'
    
    os.path.split(path)
    # 切分文件路径和文件名
    ('D:\\mypython_exp', 'new_test.txt')
    
    os.path.split('')
    # 切分结果为空字符串
    ('', '')
    
    os.path.split('C:\\windows')
    ('C:\\', 'windows')
    
    os.path.split('C:\\windows\\')
    ('C:\\windows', '')
    
    os.path.splitdrive(path)
    # 切分驱动器符号
    ('D:', '\\mypython_exp\\new_test.txt')
    
    os.path.splitext(path)
    # 切分文件扩展名
    ('D:\\mypython_exp\\new_test', '.txt')
    

(三)shutil模块

  1. 提供的功能及函数列表(见表 9-5)

    • 提供大量函数支持文件和文件夹操作。

      函数功能说明
      copy(src, dst)复制文件,新文件具有同样的文件属性,如果目标文件已存在则抛出异常
      copyfile(src, dst)复制文件,不复制文件属性,如果目标文件已存在则直接覆盖
      copytree(src, dst)递归复制文件夹
      disk_usage(path)查看磁盘使用情况
      move(src, dst)移动文件或递归移动文件夹,也可以用来给文件和文件夹重命名
      rmtree(path)递归删除文件夹
      make_archive(base_name, format, root_dir=None, base_dir=None)创建 tar 或 zip 格式的压缩文件
      unpack_archive(filename, extract_dir=None, format=None)解压缩文件
  2. 函数用法示例

    下面的代码演示了标准库shutil的一些基本用法,包括复制文件、压缩文件、解压缩文件、删除文件夹和复制文件夹。

    1. 复制文件
    import shutil
    shutil.copyfile('C:\\dir1.txt', 'D:\\dir2.txt')
    
    • 首先导入shutil模块。
    • 然后使用shutil.copyfile()函数将C:\\dir1.txt文件复制到D:\\dir2.txt。这个函数会直接覆盖目标文件(如果存在),并且不复制文件的元数据。
    1. 压缩文件
    shutil.make_archive('D:\\a', 'zip', 'C:\\Python38', 'Dlls')
    
    • 使用shutil.make_archive()函数将C:\\Python38\\Dlls文件夹及其所有内容压缩成一个zip格式的归档文件,并保存为D:\\a.zip
    1. 解压缩文件
    shutil.unpack_archive('D:\\a.zip', 'D:\\a_unpack')
    
    • 使用shutil.unpack_archive()函数将D:\\a.zip文件解压缩到D:\\a_unpack文件夹。
    1. 删除文件夹
    shutil.rmtree('D:\\a_unpack')
    
    • 使用shutil.rmtree()函数删除D:\\a_unpack文件夹及其所有内容。这个函数会递归地删除文件夹。
    1. 复制文件夹
    from shutil import copytree, ignore_patterns
    copytree('C:\\Python38\\test', 'D:\\des_test', ignore=ignore_patterns('*.pyc', '新*'))
    
    • 首先从shutil模块中导入copytreeignore_patterns
    • 然后使用copytree()函数将C:\\Python38\\test文件夹及其内容复制到D:\\des_test文件夹,但会忽略扩展名为.pyc的文件和以 “新” 开头的文件和文件夹。

    总结

    这段代码展示了shutil模块在文件和文件夹操作方面的强大功能,包括简单的文件复制、复杂的文件夹压缩和解压缩、文件夹删除以及有条件的文件夹复制。这些操作在日常的文件管理和数据处理中非常有用。

(四)综合案例解析

  1. 例 9-6 把指定文件夹中的所有文件名批量随机化,保持文件类型不变。

    基本思路:遍历指定文件夹中的所有文件,对文件名进行切分得到主文件名和扩展名,使用随机生成的字符串替换主文件名。

    from string import ascii_letters
    from os import listdir, rename
    from os.path import splitext, join
    from random import choice, randint
    
    
    def randomFilename(directory):
        # 遍历指定目录中的所有文件
        for fn in listdir(directory):
            # 分离文件名和扩展名
            name, ext = splitext(fn)
            # 生成一个5到20之间的随机整数作为新文件名的长度
            n = randint(5, 20)
            # 使用随机选择的字母生成新的文件名
            newName = ''.join(choice(ascii_letters) for i in range(n))
            # 重命名文件,将原文件名替换为随机生成的文件名,扩展名不变
            rename(join(directory, fn), join(directory, newName + ext))
    
    
    randomFilename('C:\\test')
    

    这段代码的功能是:

    1. string模块中导入ascii_letters,这是包含所有英文字母(大写和小写)的字符串。
    2. os模块中导入listdir(用于列出目录中的文件)和rename(用于重命名文件)。
    3. os.path模块中导入splitext(用于分离文件名和扩展名)和join(用于拼接路径)。
    4. random模块中导入choice(用于随机选择字符)和randint(用于生成随机整数)。

    randomFilename函数:

    • 接受一个目录路径作为参数。
    • 遍历指定目录中的所有文件。
    • 对于每个文件,分离文件名和扩展名。
    • 生成一个长度在 5 到 20 之间的随机字符串作为新的文件名。
    • 使用rename函数将原文件重命名为新的随机文件名,扩展名保持不变。

    最后,调用randomFilename函数并传入'C:\\test'作为参数,对C:\\test目录中的文件进行随机重命名。

    需要注意的是:

    • 如果C:\\test目录中的文件数量较多,生成的随机文件名可能会有重复,导致重命名失败。

    • 如果没有对C:\\test目录的写入权限,重命名操作会失败。

  2. 例 9-7 编写程序,统计指定文件夹大小以及文件和子文件夹数量。

    问题描述:本例属于系统运维范畴,可用于磁盘配额的计算,例如,email、博客、FTP、快盘等系统中每个账号所占空间大小的统计。

    基本思路:递归遍历指定目录的所有子目录和文件,如果遇到文件夹就把表示文件夹数量的变量加 1,如果遇到文件就把表示文件数量的变量加 1,同时获取该文件大小并累加到表示文件夹大小的变量上去。

    import os
    
    # 初始化变量,用于统计文件总大小、文件数量和目录数量
    totalsize = 0
    fileNum = 0
    dirNum = 0
    
    def visitDir(path):
        """
        递归函数,用于遍历指定路径下的所有文件和目录。
    
        参数:
        path (str):要遍历的路径。
    
        作用:
        1. 遍历指定路径下的所有文件和目录。
        2. 如果是文件,增加文件数量 fileNum,并将文件大小累加到 totalsize。
        3. 如果是目录,增加目录数量 dirNum,并递归调用自身处理子目录。
        """
        global totalsize, fileNum, dirNum
        for lists in os.listdir(path):
            sub_path = os.path.join(path, lists)
            if os.path.isfile(sub_path):
                fileNum += 1
                totalsize += os.path.getsize(sub_path)
            elif os.path.isdir(sub_path):
                dirNum += 1
                visitDir(sub_path)
    
    def main(path):
        """
        主函数,用于检查输入路径是否为有效的目录,并开始遍历。
    
        参数:
        path (str):要检查和遍历的路径。
    
        作用:
        1. 检查输入路径是否为有效的目录。
        2. 如果不是目录或不存在,打印错误信息并返回。
        3. 如果是有效的目录,调用 visitDir 函数开始遍历。
        """
        if not os.path.isdir(path):
            print(f'Error: "{path}" is not a directory or does not exist.')
            return
        visitDir(path)
    
    def sizeConvert(size):
        """
        函数,用于将文件大小从字节转换为更易读的单位。
    
        参数:
        size (int):文件大小,以字节为单位。
    
        作用:
        根据文件大小与不同单位的比较,选择合适的单位进行转换并返回格式化后的字符串。
        """
        K, M, G = 1024, 1024 ** 2, 1024 ** 3
        if size > G:
            return str(size / G) + 'G Bytes'
        elif size >= M:
            return str(size / M) + 'M Bytes'
        elif size > K:
            return str(size / K) + 'K Bytes'
        else:
            return str(size) + 'Bytes'
    
    def output(path):
        """
        函数,用于打印指定路径的文件总大小、文件总数和目录总数。
    
        参数:
        path (str):要打印统计信息的路径。
    
        作用:
        1. 调用 sizeConvert 函数将文件总大小转换为合适的单位后进行打印。
        2. 打印指定路径下的文件总数。
        3. 打印指定路径下的目录总数。
        """
        print(f'The total size of {path} is: {sizeConvert(totalsize)}({str(totalsize)} Bytes)')
        print(f'The total number of files in {path} is: {fileNum}')
        print(f'The total number of directories in {path} is: {dirNum}')
    
    if __name__ == '__main__':
        # 指定要遍历的路径
        path = r'd:\dapro6.5plus'
        main(path)
        output(path)
    
  3. 例 9-8 编写程序,递归删除指定文件夹中指定类型的文件和大小为 0 的文件。

    基本思路:递归遍历文件夹及其所有子文件夹,如果某个文件扩展名是指定的类型或者文件大小为 0,就删除它。

    from os.path import isdir, join, splitext
    from os import remove, listdir, chmod, getsize
    
    # 定义要删除的文件类型列表
    filetypes = ['.tmp', '.log', '.obj', '.txt']
    
    
    def delCertainFiles(directory):
        # 如果传入的路径不是目录,则直接返回
        if not isdir(directory):
            return
        # 遍历目录中的所有文件名
        for filename in listdir(directory):
            # 拼接完整路径
            temp = join(directory, filename)
            # 如果是目录,则递归调用函数
            if isdir(temp):
                delCertainFiles(temp)
            # 如果是文件,并且扩展名在要删除的类型列表中或者文件大小为0
            elif splitext(temp)[1] in filetypes or getsize(temp) == 0:
                # 修改文件权限为0o777(所有用户可读、写、执行)
                chmod(temp, 0o777)
                # 删除文件
                remove(temp)
                # 打印被删除文件的路径和提示信息
                print(temp, 'deleted...')
    
    
    # 调用函数,传入要处理的目录路径
    delCertainFiles(r'C:\test')
    

    代码功能解释:

    1. 导入模块
      • os.path模块导入isdir(用于判断路径是否为目录)、join(用于拼接路径)和splitext(用于分离文件名和扩展名)。
      • os模块导入remove(用于删除文件)、listdir(用于列出目录中的文件和目录)、chmod(用于修改文件权限)和getsize(用于获取文件大小)。
    2. 定义要删除的文件类型
      • filetypes列表包含了要删除的文件扩展名。
    3. 定义delCertainFiles函数
      • 首先判断传入的directory是否是目录,如果不是则直接返回,不进行后续操作。
      • 然后遍历directory中的所有文件和目录:
        • 对于每个filename,通过join函数拼接出完整路径temp
        • 如果temp是目录,则递归调用delCertainFiles函数来处理这个子目录。
        • 如果temp是文件,并且其扩展名在filetypes列表中或者文件大小为 0:
          • 先通过chmod函数将文件权限修改为0o777(八进制表示的权限,意味着所有用户都有读、写、执行权限)。
          • 然后通过remove函数删除文件。
          • 最后打印出被删除文件的路径和'deleted...'的提示信息。
    4. 调用函数
      • 调用delCertainFiles函数,并传入r'C:\test'作为参数,开始处理C:\test目录下的文件和子目录。

    注意事项:

    • 确保程序有足够的权限来操作C:\test目录及其文件,特别是修改权限和删除文件的操作。
    • 这种递归操作可能会删除大量文件,使用时要谨慎,避免误删重要数据。

五、Excel 与 Word 文件操作案例

(一)openpyxl库操作 Excel 文件

  1. 例 9-9 使用扩展库 openpyxl 读写 Excel 2007 以及更高版本的文件。
    说明:首先在命令提示符环境执行命令pip install openpyxl安装扩展库 openpyxl,然后再执行下面的代码对 Excel 文件进行读写操作。

    import openpyxl
    from openpyxl import Workbook
    
    # 定义Excel文件路径
    fn = r'f:\test.xlsx'
    
    # 创建一个新的Excel工作簿
    wb = Workbook()
    # 在工作簿中创建一个新的工作表,标题为'你好,世界'
    ws = wb.create_sheet(title='你好,世界')
    
    # 在工作表的A1单元格写入'这是第一个单元格'
    ws['A1'] = '这是第一个单元格'
    # 在工作表的B1单元格写入3.1415926
    ws['B1'] = 3.1415926
    # 保存工作簿到指定路径
    wb.save(fn)
    
    # 打开刚才保存的Excel文件
    wb = openpyxl.load_workbook(fn)
    # 获取工作簿中的第二个工作表(索引从0开始,所以[1]表示第二个)
    ws = wb.worksheets[1]
    # 打印出工作表中A1单元格的值
    print(ws['A1'].value)
    # 在工作表的下一行添加数据[1, 2, 3, 4, 5]
    ws.append([1, 2, 3, 4, 5])
    # 合并F2和F3单元格
    ws.merge_cells('F2:F3')
    # 在F2单元格写入公式=sum(A2:E2)
    ws['F2'] = '=sum(A2:E2)'
    
    # 循环行索引从10到14
    for r in range(10, 15):
        # 循环列索引从3到7
        for c in range(3, 8):
            # 在对应的单元格中写入r * c的值
            ws.cell(row=r, column=c, value=r * c)
    # 保存工作簿,确保所有修改都被保存
    wb.save(fn)
    
  2. 例 9-10 把记事本文件 test.txt 转换成 Excel 2007 + 文件。

    问题描述:假设 test.txt 文件中第一行为表头,从第二行开始是实际数据,并且表头和数据行中的不同字段信息都是用逗号分隔。

    说明:需要首先根据题目描述创建记事本文件 test.txt 并写入一些内容,然后再执行下面的代码。

    from openpyxl import Workbook
    
    
    def main(txtFileName):
        # 将输入的文本文件名的扩展名从.txt改为.xlsx,得到新的Excel文件名
        new_XlsxFileName = txtFileName[:-3] + 'xlsx'
        # 创建一个新的Excel工作簿
        wb = Workbook()
        # 获取工作簿中的第一个工作表
        ws = wb.worksheets[0]
        # 打开输入的文本文件,fp是文件对象
        with open(txtFileName) as fp:
            # 遍历文本文件中的每一行
            for line in fp:
                # 去除行两端的空白字符,并按逗号分割行内容,得到一个列表
                line = line.strip().split(',')
                # 将分割后的列表作为一行数据添加到Excel工作表中
                ws.append(line)
        # 保存Excel工作簿到新的文件名
        wb.save(new_XlsxFileName)
    
    
    main('test.txt')
    

    这段代码的功能是将一个逗号分隔的文本文件(如test.txt)转换为 Excel 文件(test.xlsx)。它读取文本文件的每一行,将每行按逗号分割后添加到 Excel 工作表中,最后保存 Excel 文件。

  3. 例 9-11 输出 Excel 文件中单元格中公式计算结果。

    基本思路:在使用扩展库 openpyxl 的 load_workbook () 函数打开 Excel 文件时,如果指定参数 data_only 为 True,则只读取其中单元格里的文字,而不会读取公式的内容。

    import openpyxl
    
    wb = openpyxl.load_workbook('data.xlsx', data_only=True)
    ws = wb.worksheets[1]
    for row in ws.rows:
        print(row[3].value)
    

    以下是对这段代码的解释:

    1. 导入模块
      • import openpyxl:导入openpyxl库,用于操作 Excel 文件。
    2. 打开 Excel 文件并设置参数
      • wb = openpyxl.load_workbook(‘data.xlsx’, data_only=True):
        • 使用openpyxlload_workbook函数打开名为data.xlsx的 Excel 文件。
        • data_only=True参数表示只读取单元格中的计算结果(如果有公式的话),而不读取公式本身。
    3. 选择工作表
      • ws = wb.worksheets[1]:
        • 从打开的工作簿wb中选择第二个工作表(索引从 0 开始,所以[1]表示第二个工作表)。
    4. 遍历并打印单元格值
      • for row in ws.rows::
        • 遍历所选工作表ws中的每一行。
      • print(row[3].value):
        • 对于每一行,打印该行中第四列(索引为 3,因为索引从 0 开始)单元格的值。

    这段代码的总体功能是打开一个 Excel 文件,选择其中的第二个工作表,然后遍历每一行,打印出每行第四列单元格中的值(只读取计算结果,不读取公式)。

(二)python - docx库操作 Word 文件

  1. 例 9-12 检查 Word 文档的连续重复字。

    问题描述:在 Word 文档中,经常会由于键盘操作不小心而使得文档中出现连续的重复字,例如 “用户的的资料” 或 “需要需要用户输入” 之类的情况。本例使用扩展库 python - docx 对 Word 文档进行检查并提示类似的重复汉字。

    说明:本例需要先在命令提示符环境中执行命令 pip install python - docx 安装扩展库 python - docx;示例代码中的文件 “《Python 程序设计开发宝典》.docx” 可以替换成任意其他 Word 文档,但要求该文件和本程序文件存储在同一个文件夹中,否则需要指定完整路径。

    基本思路:使用扩展库 python - docx 打开 Word 文档,然后把所有段的文字连接为一个长字符串,遍历该字符串,检查是否有连续两个字是一样的,或者是否有中间隔一个字的两个字是一样的,如果有就进行提示。

    from docx import Document
    
    # 创建一个Document对象,用于表示Word文档
    # 这里传入Word文档的文件名,尝试打开该文档
    doc = Document('《Python程序设计开发宝典》.docx')
    
    # 使用生成器表达式获取文档中每个段落的文本内容
    # 然后使用join方法将这些文本内容连接成一个字符串
    contents = ''.join((p.text for p in doc.paragraphs))
    
    # 创建一个空列表,用于存储找到的重复字串
    words = []
    
    # 使用enumerate函数同时获取字符的索引和字符本身
    # 由于要检查当前字符与后面的字符,所以遍历到倒数第三个字符即可
    for index, ch in enumerate(contents[:-2]):
        # 检查当前字符是否与下一个字符相同,或者与下下一个字符相同
        if ch == contents[index + 1] or ch == contents[index + 2]:
            # 如果满足条件,提取从当前字符开始的连续3个字符作为一个字串
            word = contents[index:index + 3]
            # 检查这个字串是否已经在words列表中
            if word not in words:
                # 如果不在列表中,将其添加到列表中
                words.append(word)
                # 并打印这个重复的字串
                print(word)
    

    这段代码的功能是读取一个 Word 文档,检查文档内容中是否存在连续重复的字串(如两个连续相同的字,或者中间隔一个字的两个相同字),并将找到的重复字串打印出来。

    具体步骤如下:

    1. 首先从docx模块导入Document类,用于处理 Word 文档。
    2. 使用Document类打开指定的 Word 文档。
    3. 通过生成器表达式获取文档中每个段落的文本,并将它们连接成一个字符串。
    4. 创建一个空列表words,用于存储找到的重复字串。
    5. 遍历字符串中的字符(除了最后两个字符),检查当前字符是否与下一个字符或下下一个字符相同。
    6. 如果找到重复字符,提取连续 3 个字符作为字串。
    7. 检查这个字串是否已经在words列表中,如果不在,将其添加到列表并打印出来。
  2. 例 9-13 提取 Word 文档中例题、插图和表格清单。

    基本思路:遍历 Word 文档中每一段文字,如果是以类似于 “例 3-6” 这样形式的内容开头则认为是例题,如果是以类似于 “图 13-12” 这样形式的内容则认为是插图,如果是以类似于 “表 9-1” 这样形式的内容开头则认为是表格。使用正则表达式分别匹配这些内容,并写入字典 result 的不同元素中。

    import re
    from docx import Document
    
    # 创建一个字典result,用于存储提取的内容
    # "li"键对应的值将存储例题标题,初始化为空列表
    # "fig"键对应的值将存储插图标题,初始化为空列表
    # "tab"键对应的值将存储表格标题,初始化为空列表
    result = {"li": [], 'fig': [], 'tab': []}
    
    # 使用Document类打开名为《Python可以这样学》的Word文档
    doc = Document('《Python可以这样学》.docx')
    
    # 遍历文档中的每一个段落
    for p in doc.paragraphs:
        # 获取段落中的文本内容
        t = p.text
        # 使用正则表达式检查文本是否以"例"开头,后面跟着数字-数字的格式(例如"例3-6")
        if re.match('例\d+-\d+', t):
            # 如果匹配成功,将该文本添加到result字典中"li"键对应的列表中
            result['li'].append(t)
        # 使用正则表达式检查文本是否以"图"开头,后面跟着数字-数字的格式(例如"图13-12")
        elif re.match('图\d+-\d+', t):
            # 如果匹配成功,将该文本添加到result字典中"fig"键对应的列表中
            result['fig'].append(t)
        # 使用正则表达式检查文本是否以"表"开头,后面跟着数字-数字的格式(例如"表9-1")
        elif re.match('表\d+-\d+', t):
            # 如果匹配成功,将该文本添加到result字典中"tab"键对应的列表中
            result['tab'].append(t)
    
    # 遍历result字典中的每一个键值对
    for key, value in result.items():
        # 打印30个等号作为分隔线,使输出结果更清晰
        print("=" * 30)
        # 遍历当前键对应的值列表
        for v in value:
            # 打印列表中的每一个元素(即例题、插图或表格的标题)
            print(v)
    

    以下是对上述代码功能实现步骤的详细解释:

    1. 导入必要的模块
      • import re:导入re模块,用于使用正则表达式进行文本匹配。
      • from docx import Document:从docx库中导入Document类,用于读取 Word 文档。
    2. 初始化结果字典
      • result = {“li”: [], ‘fig’: [], 'tab": []}:创建一个字典result,其中包含三个键值对。
        • "li"键对应的值是一个空列表,用于存储例题的标题。
        • "fig"键对应的值是一个空列表,用于存储插图的标题。
        • "tab"键对应的值是一个空列表,用于存储表格的标题。
    3. 读取 Word 文档
      • doc = Document('《Python可以这样学》.docx'):使用Document类打开名为《Python 可以这样学》的 Word 文档。
    4. 遍历文档段落并进行匹配
      • for p in doc.paragraphs::开始遍历文档中的每一个段落。
        • t = p.text:获取当前段落的文本内容。
        • 然后使用正则表达式进行匹配:
          • 匹配例题标题
            • if re.match('例\d+-\d+', t)::使用re.match函数检查文本t是否以"例"开头,后面跟着由数字组成的格式数字-数字(例如"例3-6")。
              • 如果匹配成功,执行result['li'].append(t),将该段落的文本添加到result字典中"li"键对应的列表中。
          • 匹配插图标题
            • elif re.match('图\d+-\d+', t)::检查文本t是否以"图"开头,后面跟着数字-数字的格式(例如"图13-12")。
              • 如果匹配成功,执行result['fig'].append(t),将该段落的文本添加到result字典中"fig"键对应的列表中。
          • 匹配表格标题
            • elif re.match('表\d+-\d+', t)::检查文本t是否以"表"开头,后面跟着数字-数字的格式(例如"表9-1")。
              • 如果匹配成功,执行result['tab'].append(t),将该段落的文本添加到result字典中"tab"键对应的列表中。
    5. 输出结果
      • for key, value in result.items()::遍历result字典中的每一个键值对。
        • print("=" * 30):先打印 30 个等号作为分隔线。
        • for v in value::再遍历当前键对应的值列表。
          • print(v):打印列表中的元素,即分别打印出例题、插图和表格的标题。

    通过以上步骤,代码能够从 Word 文档中提取出例题、插图和表格的标题,并分类打印出来。

  3. 例 9-14 查找 Word 文件中所有红色字体和加粗的文字。

    基本思路:Word 文件的文本结构可以简单地分为三层,①Document 对象表示整个文档;②Document 包含了 Paragraph 对象的列表,每个 Paragraph 对象用来表示文档中的一个段落;③一个 Paragraph 对象包含 Run 对象的列表,一个 Run 对象就是 style 相同的一段文本。遍历 Word 文档中所有段落的所有 run 对象,根据 run 对象的属性进行识别和输出。

    # 1. 从docx库中导入Document类,用于操作Word文档
    from docx import Document
    # 2. 从docx.shared模块中导入RGBColor类,用于处理颜色
    from docx.shared import RGBColor
    
    # 4. 创建一个空列表boldText,用于存储加粗的文本
    boldText = []
    # 5. 创建一个空列表redText,用于存储红色的文本
    redText = []
    
    # 6. 以下是处理Word文档的主逻辑
    # 打开名为test.docx的Word文件,创建一个Document对象doc
    doc = Document('test.docx')
    
    # 8. 开始遍历文档中的每个段落
    for p in doc.paragraphs:
        # 9. 对于每个段落,遍历其中的每个文本块(runs)
        for r in p.runs:
            # 10. 判断当前文本块是否为加粗字体
            if r.bold:
                # 11. 如果是加粗字体,将该文本块的文本内容添加到boldText列表中
                boldText.append(r.text)
            # 13. 判断当前文本块的字体颜色是否为红色(RGB值为(255, 0, 0))
            if r.font.color.rgb == RGBColor(255, 0, 0):
                # 14. 如果是红色字体,将该文本块的文本内容添加到redText列表中
                redText.append(r.text)
    
    # 17. 创建一个字典result,用于存储不同类型的文本
    # 'red text'键对应的值为redText列表,存储红色文本
    # 'bold text'键对应的值为boldText列表,存储加粗文本
    # 'both'键对应的值为redText和boldText列表的交集(即既是红色又是加粗的文本)
    result = {'red text': redText, 'bold text': boldText,
              'both': set(redText) & set(boldText)}
    
    # 20. 以下是输出结果的逻辑
    # 遍历result字典中的每个键值对
    for title, value in result.items():
        # 21. 打印居中对齐的标题,标题两侧用等号填充至30个字符宽度
        print(title.center(30, '='))
        # 23. 遍历当前类型的文本列表
        for text in value:
            # 24. 打印文本内容
            print(text)
    

    以下是对上述代码功能实现步骤的详细解释:

    1. 导入必要的模块和类
      • from docx import Document:从docx库中导入Document类,用于操作Word文档。
      • from docx.shared import RGBColor:从docx.shared模块中导入RGBColor类,用于表示颜色(这里用于判断字体颜色)。
    2. 初始化数据结构
      • boldText = []:创建一个空列表boldText,用于存储加粗的文本内容。
      • redText = []:创建一个空列表redText,用于存储红色字体的文本内容。
    3. 打开Word文档并遍历段落和文本块
      • doc = Document('test.docx'):使用Document类打开名为test.docx的Word文档。
      • 外层for循环:for p in doc.paragraphs:
        • 遍历文档中的每个段落。
        • 内层for循环:for r in p.runs:
          • 对于每个段落,遍历其中的每个文本块(runs)。在docx库中,一个Run对象代表具有相同样式的一段文本。
    4. 判断并提取加粗和红色字体的文本
      • 判断加粗文本
        • if r.bold::检查当前文本块是否为加粗字体。
          • 如果是加粗字体,执行boldText.append(r.text),将该文本块的文本内容添加到boldText列表中。
      • 判断红色字体文本
        • if r.font.color.rgb == RGBColor(255, 0, 0)::检查当前文本块的字体颜色是否为红色(RGB值为(255, 0, 0))。
          • 如果是红色字体,执行redText.append(r.text),将该文本块的文本内容添加到redText列表中。
    5. 整理结果数据
      • result = {'red text': redText, 'bold text': boldText, 'both': set(redText) & set(boldText)}
        • 创建一个字典result
        • 'red text'键对应的值为redText列表,存储红色文本。
        • 'bold text'键对应的值为boldText列表,存储加粗文本。
        • 'both'键对应的值为redTextboldText列表的交集(即既是红色又是加粗的文本),这里使用set函数将列表转换为集合来求交集。
    6. 输出结果
      • for title, value in result.items()::遍历result字典中的每个键值对。
        • print(title.center(30, '=')):打印居中对齐的标题,标题两侧用等号填充至30个字符宽度。
        • for text in value::遍历当前类型的文本列表。
          • print(text):打印文本内容。

    通过以上步骤,代码能够从test.docx文档中提取加粗和红色字体的文本,并分别进行展示,同时还展示了既是红色又是加粗的文本。

六、总结及课后练习

总结

详细讲解文件操作原理与内置函数 open () 的用法、文件夹操作常用的标准库对象,以及操作 Word 和 Excel 文件的常用扩展库。强调读写文本文件时要注意编码格式,指出 Word 和 Excel 文件操作在办公自动化领域很有用,由于篇幅限制只演示了几个例题,读者可以关注作者微信公众号 “Python 小屋” 学习更多相关内容。

课后练习

  1. 假设有两个文本文件 file1.txt 和 file2.txt,编写程序 merge.py,把两个文本文件中的内容合并到新文件 result.txt 中,要求文件 file1.txt 和 file2.txt 中的行在 result.txt 中交替出现。也就是说,result.txt 文件中的奇数行来自 file1.txt,而偶数行来自 file2.txt。如果两个文件行数不一样,那么处理完行数较少的文件之后,把另一个文件中剩余的所有行直接追加到 result.txt 的最后。

    以下是merge.py的代码实现:

    def merge_files(file1, file2, result):
        with open(file1, 'r') as f1, open(file2, 'r') as f2, open(result, 'w') as f_out:
            lines1 = f1.readlines()
            lines2 = f2.readlines()
            len1 = len(lines1)
            len2 = len(lines2)
            min_len = min(len1, len2)
    
            for i in range(min_len):
                f_out.write(lines1[i])
                f_out.write(lines2[i])
    
            if len1 > len2:
                for j in range(min_len, len1):
                    f_out.write(lines1[j])
            elif len2 > len1:
                for k in range(min_len, len2):
                    f_out.write(lines2[k])
    
    if __name__ == "__main__":
        file1 = "file1.txt"
        file2 = "file2.txt"
        result = "result.txt"
        merge_files(file1, file2, result)
    

    代码解释:

    1. 函数定义
      • merge_files函数接受三个参数:file1(第一个文本文件的路径)、file2(第二个文本文件的路径)和result(合并后结果文件的路径)。
      • 使用with语句同时打开file1file2result文件,with语句可以确保文件在使用后正确关闭。
      • lines1 = f1.readlines()lines2 = f2.readlines()分别读取file1file2中的所有行,并存储为列表。
      • len1 = len(lines1)len2 = len(lines2)获取两个文件的行数。
      • min_len = min(len1, len2)找到两个文件中行数较少的长度。
    2. 合并文件内容
      • 使用for循环遍历到行数较少的文件的行数:
        • for i in range(min_len):在每次循环中,将file1的第i行和file2的第i行依次写入result文件。
      • 处理行数不同的情况:
        • 如果len1 > len2,说明file1的行数更多,再通过一个for循环将file1中剩余的行写入result文件。
        • 如果len2 > len1,说明file2的行数更多,将file2中剩余的行写入result文件。
    3. 主程序
      • if __name__ == "__main__"语句中,定义file1file2result的文件名(这里假设它们在同一目录下)。
      • 调用merge_files函数进行文件合并。
  2. 编写程序,读取上一题的 merge.py,在每一行后加上行号并生成新文件 merge_new.py,要求加上行号之后的文件 merge_new.py 和原程序 merge.py 功能一样,并且所有行号对齐。

    下面是一个Python程序,它读取merge.py文件,并在每一行后加上行号,然后生成新的文件merge_new.py。新文件中的行号将对齐,并且保留原有的功能。

    def add_line_numbers(input_file, output_file):
        try:
            with open(input_file, 'r', encoding='utf-8') as file:
                lines = file.readlines()
            
            max_line_number_length = len(str(len(lines)))  # 计算最大行号的长度
            
            with open(output_file, 'w', encoding='utf-8') as new_file:
                for i, line in enumerate(lines, start=1):
                    # 格式化行号,使其右对齐
                    line_number = str(i).rjust(max_line_number_length)
                    new_file.write(f'{line_number} {line}')
            
            print(f"文件 '{input_file}' 已成功处理并生成新文件 '{output_file}'")
        
        except FileNotFoundError:
            print(f"错误:文件 '{input_file}' 未找到")
        except Exception as e:
            print(f"发生错误:{e}")
    
    # 调用函数
    add_line_numbers('merge.py', 'merge_new.py')
    

    详细说明

    1. 读取文件
      • 使用 open 函数以读取模式打开 merge.py 文件,并读取所有行到 lines 列表中。
    2. 计算最大行号长度
      • 使用 len(str(len(lines))) 计算最大行号的长度,以便后续对齐行号。
    3. 写入新文件
      • 使用 open 函数以写入模式打开 merge_new.py 文件。
      • 遍历 lines 列表,使用 enumerate 函数获取每一行的索引和内容。
      • 格式化行号,使其右对齐,使用 str(i).rjust(max_line_number_length)
      • 将行号和原始行内容写入新文件。
    4. 异常处理
      • 处理文件未找到的情况。
      • 处理其他可能的异常。
  3. 编写程序,把当前目录中所有的 Excel 文件合并为一个文件。假设所有 Excel 包含同样数量的列,第一行为表头,且不存在合并单元格或其他特殊格式。

    下面是一个Python程序,它会将当前目录中所有的Excel文件合并为一个文件。假设所有Excel文件包含相同数量的列,第一行为表头,且不存在合并单元格或其他特殊格式。

    import os
    import openpyxl
    
    def merge_excel_files(output_filename):
        # 获取当前目录
        current_dir = os.getcwd()
        
        # 获取当前目录下所有的 .xlsx 文件
        excel_files = [f for f in os.listdir(current_dir) if f.endswith('.xlsx')]
        
        if not excel_files:
            print("当前目录下没有找到任何 Excel 文件。")
            return
        
        # 创建一个新的工作簿和工作表
        merged_wb = openpyxl.Workbook()
        merged_ws = merged_wb.active
        merged_ws.title = "Merged Data"
        
        # 读取第一个文件的表头
        first_file = excel_files[0]
        first_wb = openpyxl.load_workbook(first_file)
        first_ws = first_wb.active
        header_row = [cell.value for cell in first_ws[1]]
        
        # 将表头写入合并的工作表
        merged_ws.append(header_row)
        
        # 遍历所有 Excel 文件
        for excel_file in excel_files:
            wb = openpyxl.load_workbook(excel_file)
            ws = wb.active
            
            # 跳过表头行
            for row in ws.iter_rows(min_row=2, values_only=True):
                merged_ws.append(row)
        
        # 保存合并后的文件
        merged_wb.save(output_filename)
        print(f"所有 Excel 文件已合并为 {output_filename}")
    
    # 调用函数,指定输出文件名
    merge_excel_files('merged_output.xlsx')
    

    详细说明

    1. 获取当前目录
      • 使用 os.getcwd() 获取当前工作目录。
    2. 获取所有 Excel 文件
      • 使用 os.listdir() 列出当前目录下的所有文件。
      • 使用列表推导式筛选出扩展名为 .xlsx 的文件。
    3. 创建新的工作簿和工作表
      • 使用 openpyxl.Workbook() 创建一个新的工作簿。
      • 获取活动工作表并设置其标题。
    4. 读取第一个文件的表头
      • 加载第一个 Excel 文件。
      • 读取第一个文件的第一行作为表头。
    5. 将表头写入合并的工作表
      • 使用 merged_ws.append(header_row) 将表头写入合并的工作表。
    6. 遍历所有 Excel 文件
      • 对于每个 Excel 文件,加载工作簿和工作表。
      • 跳过表头行,将剩余的行数据逐行写入合并的工作表。
    7. 保存合并后的文件
      • 使用 merged_wb.save(output_filename) 保存合并后的文件。
  4. 编写程序,读取一个 Word 文件,输出其中所有表格中所有单元格中的文本。

    下面是一个Python程序,它使用 python-docx 库来读取Word文件中的所有表格,并输出每个单元格中的文本。

    首先,确保你已经安装了 python-docx 库。如果没有安装,可以使用以下命令进行安装:

    pip install python-docx
    

    然后,使用以下代码读取Word文件中的所有表格,并输出每个单元格中的文本:

    from docx import Document
    
    def read_tables_from_word(doc_path):
        # 打开Word文档
        doc = Document(doc_path)
        
        # 遍历文档中的所有表格
        for table_index, table in enumerate(doc.tables, start=1):
            print(f"Table {table_index}:")
            for row_index, row in enumerate(table.rows, start=1):
                for cell_index, cell in enumerate(row.cells, start=1):
                    # 输出单元格中的文本
                    print(f"  Row {row_index}, Cell {cell_index}: {cell.text}")
    
    # 调用函数,指定Word文件路径
    read_tables_from_word('example.docx')
    

    详细说明

    1. 导入库
      • 使用 from docx import Document 导入 python-docx 库中的 Document 类。
    2. 定义函数
      • 定义 read_tables_from_word 函数,接受一个参数 doc_path,表示Word文件的路径。
    3. 打开Word文档
      • 使用 Document(doc_path) 打开指定路径的Word文档。
    4. 遍历所有表格
      • 使用 enumerate(doc.tables, start=1) 遍历文档中的所有表格,并为每个表格分配一个索引(从1开始)。
    5. 遍历表格中的行和单元格
      • 对于每个表格,使用 enumerate(table.rows, start=1) 遍历表格中的所有行,并为每行分配一个索引(从1开始)。
      • 对于每行,使用 enumerate(row.cells, start=1) 遍历行中的所有单元格,并为每个单元格分配一个索引(从1开始)。
    6. 输出单元格中的文本
      • 使用 print(f" Row {row_index}, Cell {cell_index}: {cell.text}") 输出每个单元格中的文本。
  5. 编写程序,检查 D:\ 文件夹及其子文件夹中是否存在一个名为 temp.txt 的文件。

    下面是一个Python程序,它会递归地检查 D:\ 文件夹及其子文件夹中是否存在一个名为 temp.txt 的文件。我们将使用 os 模块来遍历目录结构,并使用 os.path 模块来处理文件路径。

    import os
    
    def find_file(directory, filename):
        # 遍历指定目录及其子目录
        for root, dirs, files in os.walk(directory):
            if filename in files:
                # 如果找到文件,返回文件的完整路径
                return os.path.join(root, filename)
        # 如果没有找到文件,返回 None
        return None
    
    def main():
        directory = r'D:\'  # 指定要搜索的目录
        filename = 'temp.txt'  # 指定要查找的文件名
        
        result = find_file(directory, filename)
        
        if result:
            print(f"文件 {filename} 找到了,路径为: {result}")
        else:
            print(f"文件 {filename} 未找到。")
    
    if __name__ == "__main__":
        main()
    

    详细说明

    1. 导入模块
      • 使用 import os 导入 os 模块,该模块提供了许多与操作系统交互的功能。
    2. 定义 find_file 函数
      • 接受两个参数:directory(要搜索的目录)和 filename(要查找的文件名)。
      • 使用 os.walk(directory) 遍历指定目录及其子目录。
      • 对于每个目录,os.walk 返回一个三元组 (root, dirs, files),其中 root 是当前目录的路径,dirs 是当前目录下的子目录列表,files 是当前目录下的文件列表。
      • 检查 files 列表中是否包含 filename,如果找到,返回文件的完整路径。
      • 如果遍历完所有目录仍未找到文件,返回 None
    3. 定义 main 函数
      • 指定要搜索的目录 D:\ 和要查找的文件名 temp.txt
      • 调用 find_file 函数进行搜索。
      • 根据搜索结果输出相应的信息。
    4. 运行主程序
      • 使用 if __name__ == "__main__": 确保当脚本直接运行时才会执行 main 函数。
  6. 编写程序,检查 D:\ 文件夹及其子文件夹中所有创建日期为 2017 年 10 月 26 日的文件,输出这些文件的完整路径和创建日期。

    下面是一个Python程序,它会递归地检查 D:\ 文件夹及其子文件夹中所有创建日期为 2017 年 10 月 26 日的文件,并输出这些文件的完整路径和创建日期。我们将使用 os 模块来遍历目录结构,并使用 os.pathdatetime 模块来处理文件路径和日期。

    import os
    from datetime import datetime
    
    def get_creation_date(file_path):
        # 获取文件的创建时间戳
        timestamp = os.path.getctime(file_path)
        # 将时间戳转换为 datetime 对象
        creation_date = datetime.fromtimestamp(timestamp)
        return creation_date
    
    def find_files_with_creation_date(directory, target_date):
        # 将目标日期字符串转换为 datetime 对象
        target_date = datetime.strptime(target_date, '%Y-%m-%d').date()
        
        # 遍历指定目录及其子目录
        for root, dirs, files in os.walk(directory):
            for file in files:
                file_path = os.path.join(root, file)
                creation_date = get_creation_date(file_path).date()
                
                # 检查文件的创建日期是否为目标日期
                if creation_date == target_date:
                    print(f"文件路径: {file_path}, 创建日期: {creation_date}")
    
    def main():
        directory = r'D:\'  # 指定要搜索的目录
        target_date = '2017-10-26'  # 指定目标日期
        
        find_files_with_creation_date(directory, target_date)
    
    if __name__ == "__main__":
        main()
    

    详细说明

    1. 导入模块
      • 使用 import os 导入 os 模块,该模块提供了许多与操作系统交互的功能。
      • 使用 from datetime import datetime 导入 datetime 模块,用于处理日期和时间。
    2. 定义 get_creation_date 函数
      • 接受一个参数 file_path,表示文件的路径。
      • 使用 os.path.getctime(file_path) 获取文件的创建时间戳。
      • 使用 datetime.fromtimestamp(timestamp) 将时间戳转换为 datetime 对象。
      • 返回 datetime 对象。
    3. 定义 find_files_with_creation_date 函数
      • 接受两个参数:directory(要搜索的目录)和 target_date(目标日期的字符串形式)。
      • 将目标日期字符串转换为 datetime 对象。
      • 使用 os.walk(directory) 遍历指定目录及其子目录。
      • 对于每个文件,获取其完整路径和创建日期。
      • 检查文件的创建日期是否为目标日期,如果是,则输出文件的完整路径和创建日期。
    4. 定义 main 函数
      • 指定要搜索的目录 D:\ 和目标日期 2017-10-26
      • 调用 find_files_with_creation_date 函数进行搜索。
    5. 运行主程序
      • 使用 if __name__ == "__main__": 确保当脚本直接运行时才会执行 main 函数。
  7. 编写程序,实现文件夹增量备份。例如,第一次执行时把工作目录 D:\Working Directory 及其子文件夹中的所有内容都复制到备份目录 D:\backupDirectory 中,并且保持目录结构一致。然后在工作目录或其任意子目录中新建一个文件并修改一个已有文件的内容,再次执行程序则会自动对比工作目录和备份目录并只复制上次备份之后修改过的文件和新建的文件。

    下面是一个Python程序,它实现了文件夹的增量备份。该程序会在第一次执行时将工作目录 D:\Working Directory 及其子文件夹中的所有内容复制到备份目录 D:\backupDirectory 中,并且保持目录结构一致。之后每次执行时,程序会自动对比工作目录和备份目录,并只复制上次备份之后修改过的文件和新建的文件。

    我们将使用 osshutil 模块来处理文件和目录操作,并使用 hashlib 模块来生成文件的哈希值以便进行比较。

    import os
    import shutil
    import hashlib
    import json
    
    def get_file_hash(file_path):
        """获取文件的MD5哈希值"""
        hash_md5 = hashlib.md5()
        with open(file_path, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
        return hash_md5.hexdigest()
    
    def create_directory_structure(src_dir, dst_dir):
        """创建备份目录结构"""
        for root, dirs, _ in os.walk(src_dir):
            relative_path = os.path.relpath(root, src_dir)
            dst_path = os.path.join(dst_dir, relative_path)
            for dir_name in dirs:
                dir_path = os.path.join(dst_path, dir_name)
                if not os.path.exists(dir_path):
                    os.makedirs(dir_path)
    
    def backup_files(src_dir, dst_dir, state_file):
        """增量备份文件"""
        if not os.path.exists(state_file):
            # 第一次备份,初始化状态文件
            state = {}
        else:
            with open(state_file, 'r') as f:
                state = json.load(f)
    
        # 创建备份目录结构
        create_directory_structure(src_dir, dst_dir)
    
        # 遍历源目录中的所有文件
        for root, _, files in os.walk(src_dir):
            relative_path = os.path.relpath(root, src_dir)
            dst_path = os.path.join(dst_dir, relative_path)
            
            for file in files:
                src_file_path = os.path.join(root, file)
                dst_file_path = os.path.join(dst_path, file)
                file_key = os.path.relpath(src_file_path, src_dir)
                
                # 获取文件的哈希值
                file_hash = get_file_hash(src_file_path)
                
                # 检查文件是否已存在且未被修改
                if file_key in state and state[file_key] == file_hash:
                    continue
                
                # 复制文件
                shutil.copy2(src_file_path, dst_file_path)
                print(f"备份文件: {src_file_path} -> {dst_file_path}")
                
                # 更新状态文件
                state[file_key] = file_hash
    
        # 保存状态文件
        with open(state_file, 'w') as f:
            json.dump(state, f, indent=4)
    
    def main():
        src_dir = r'D:\Working Directory'
        dst_dir = r'D:\backupDirectory'
        state_file = r'D:\backupState.json'
    
        backup_files(src_dir, dst_dir, state_file)
    
    if __name__ == "__main__":
        main()
    

    详细说明

    1. 导入模块
      • 使用 import os 导入 os 模块,用于处理文件和目录操作。
      • 使用 import shutil 导入 shutil 模块,用于复制文件和目录。
      • 使用 import hashlib 导入 hashlib 模块,用于生成文件的哈希值。
      • 使用 import json 导入 json 模块,用于读写状态文件。
    2. 定义 get_file_hash 函数
      • 接受一个参数 file_path,表示文件的路径。
      • 使用 hashlib.md5() 生成文件的MD5哈希值。
      • 返回文件的哈希值。
    3. 定义 create_directory_structure 函数
      • 接受两个参数:src_dir(源目录)和 dst_dir(备份目录)。
      • 使用 os.walk(src_dir) 遍历源目录中的所有子目录。
      • 在备份目录中创建相应的目录结构。
    4. 定义 backup_files 函数
      • 接受三个参数:src_dir(源目录)、dst_dir(备份目录)和 state_file(状态文件的路径)。
      • 如果状态文件不存在,初始化为空字典。
      • 否则,读取状态文件中的内容。
      • 创建备份目录结构。
      • 遍历源目录中的所有文件,获取每个文件的相对路径和哈希值。
      • 检查文件是否已存在且未被修改,如果未被修改则跳过。
      • 复制文件到备份目录,并更新状态文件。
      • 保存状态文件。
    5. 定义 main 函数
      • 指定源目录 D:\Working Directory、备份目录 D:\backupDirectory 和状态文件 D:\backupState.json
      • 调用 backup_files 函数进行备份。
    6. 运行主程序
      • 使用 if __name__ == "__main__": 确保当脚本直接运行时才会执行 main 函数。
  8. 改进上一个题目中的程序,要求运行后可以由用户输入工作目录和备份目录的路径。

    下面是改进后的Python程序,它允许用户在运行时输入工作目录和备份目录的路径,并实现文件夹的增量备份。

    import os
    import shutil
    import hashlib
    import json
    
    def get_file_hash(file_path):
        """获取文件的MD5哈希值"""
        hash_md5 = hashlib.md5()
        with open(file_path, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
        return hash_md5.hexdigest()
    
    def create_directory_structure(src_dir, dst_dir):
        """创建备份目录结构"""
        for root, dirs, _ in os.walk(src_dir):
            relative_path = os.path.relpath(root, src_dir)
            dst_path = os.path.join(dst_dir, relative_path)
            for dir_name in dirs:
                dir_path = os.path.join(dst_path, dir_name)
                if not os.path.exists(dir_path):
                    os.makedirs(dir_path)
    
    def backup_files(src_dir, dst_dir, state_file):
        """增量备份文件"""
        if not os.path.exists(state_file):
            # 第一次备份,初始化状态文件
            state = {}
        else:
            with open(state_file, 'r') as f:
                state = json.load(f)
    
        # 创建备份目录结构
        create_directory_structure(src_dir, dst_dir)
    
        # 遍历源目录中的所有文件
        for root, _, files in os.walk(src_dir):
            relative_path = os.path.relpath(root, src_dir)
            dst_path = os.path.join(dst_dir, relative_path)
            
            for file in files:
                src_file_path = os.path.join(root, file)
                dst_file_path = os.path.join(dst_path, file)
                file_key = os.path.relpath(src_file_path, src_dir)
                
                # 获取文件的哈希值
                file_hash = get_file_hash(src_file_path)
                
                # 检查文件是否已存在且未被修改
                if file_key in state and state[file_key] == file_hash:
                    continue
                
                # 复制文件
                shutil.copy2(src_file_path, dst_file_path)
                print(f"备份文件: {src_file_path} -> {dst_file_path}")
                
                # 更新状态文件
                state[file_key] = file_hash
    
        # 保存状态文件
        with open(state_file, 'w') as f:
            json.dump(state, f, indent=4)
    
    def main():
        # 用户输入工作目录和备份目录
        src_dir = input("请输入工作目录路径: ").strip()
        dst_dir = input("请输入备份目录路径: ").strip()
        state_file = os.path.join(dst_dir, 'backupState.json')
    
        # 检查目录是否存在
        if not os.path.isdir(src_dir):
            print(f"错误:工作目录 {src_dir} 不存在。")
            return
        if not os.path.exists(dst_dir):
            os.makedirs(dst_dir)
        
        backup_files(src_dir, dst_dir, state_file)
    
    if __name__ == "__main__":
        main()
    

    详细说明

    1. 导入模块
      • 使用 import os 导入 os 模块,用于处理文件和目录操作。
      • 使用 import shutil 导入 shutil 模块,用于复制文件和目录。
      • 使用 import hashlib 导入 hashlib 模块,用于生成文件的哈希值。
      • 使用 import json 导入 json 模块,用于读写状态文件。
    2. 定义 get_file_hash 函数
      • 接受一个参数 file_path,表示文件的路径。
      • 使用 hashlib.md5() 生成文件的MD5哈希值。
      • 返回文件的哈希值。
    3. 定义 create_directory_structure 函数
      • 接受两个参数:src_dir(源目录)和 dst_dir(备份目录)。
      • 使用 os.walk(src_dir) 遍历源目录中的所有子目录。
      • 在备份目录中创建相应的目录结构。
    4. 定义 backup_files 函数
      • 接受三个参数:src_dir(源目录)、dst_dir(备份目录)和 state_file(状态文件的路径)。
      • 如果状态文件不存在,初始化为空字典。
      • 否则,读取状态文件中的内容。
      • 创建备份目录结构。
      • 遍历源目录中的所有文件,获取每个文件的相对路径和哈希值。
      • 检查文件是否已存在且未被修改,如果未被修改则跳过。
      • 复制文件到备份目录,并更新状态文件。
      • 保存状态文件。
    5. 定义 main 函数
      • 用户输入工作目录和备份目录的路径。
      • 检查工作目录是否存在,如果不存在则输出错误信息并退出。
      • 如果备份目录不存在,则创建备份目录。
      • 调用 backup_files 函数进行备份。
    6. 运行主程序
      • 使用 if __name__ == "__main__": 确保当脚本直接运行时才会执行 main 函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值