使用Python将CSV文件按行拆分为多个独立文件并妥善管理

聖光之護
发布: 2025-09-19 16:39:34
原创
751人浏览过

使用python将csv文件按行拆分为多个独立文件并妥善管理

本文详细介绍了如何使用Python的csv模块将一个大型CSV文件中的每一行数据拆分并写入到单独的CSV文件中。核心内容包括利用csv.writer正确处理CSV格式,以及通过contextlib.ExitStack和字典管理多个文件写入器,以高效、健壮地解决文件名冲突和资源管理问题,确保数据准确无误地分散到指定的新文件中。

1. CSV数据拆分与写入基础

在Python中处理CSV文件时,如果需要将原始文件中的每一行或特定行的数据提取出来,并保存到以行内容命名的独立CSV文件中,一个常见的需求是将特定字段作为新文件的文件名,并将其他字段写入到这个新文件中。

假设我们有一个包含Order Number、Date和File Name三列的CSV文件,目标是为每一行创建一个新的CSV文件,文件名取自File Name字段,新文件中只包含Order Number和Date字段,且不带表头。

最初尝试可能直接使用f.write()方法将字段内容写入文件。然而,这种方法存在一个核心问题:f.write()仅仅是写入字符串,它不会自动添加CSV文件所需的字段分隔符(如逗号)。这会导致所有字段内容被连接成一个单一的字符串,而不是以逗号分隔的多个字段。

错误示例(仅供理解问题,不建议使用):

立即学习Python免费学习笔记(深入)”;

import csv

# 假设TestExport.csv存在于指定路径
# with open("//server2/shared/Data/TestExport.csv",'r') as csvfile:
#         reader = csv.DictReader(csvfile)
#         for row in reader:
#             file_name ='{0}.csv'.format(row['FileName'])
#             with open(file_name, 'w') as f:
#                    f.write(row['Order Number'])
#                    f.write(row['Date'])
登录后复制

上述代码的问题在于f.write(row['Order Number'])和f.write(row['Date'])会将两个字符串直接连接起来,例如123452023-01-01,而不是12345,2023-01-01。

2. 使用csv.writer正确写入CSV数据

解决上述问题的关键在于使用Python内置csv模块提供的csv.writer对象。csv.writer专门用于处理CSV格式的写入,它能够自动处理字段分隔符和行结束符。

核心改进点:

降重鸟
降重鸟

要想效果好,就用降重鸟。AI改写智能降低AIGC率和重复率。

降重鸟 308
查看详情 降重鸟
  • csv.writer(out_f, delimiter=','): 创建一个CSV写入器,并指定逗号作为字段分隔符。
  • writer.writerow([...]): 使用此方法写入一行数据。它接受一个列表作为参数,列表中的每个元素将作为一个字段写入,并自动添加分隔符。
  • newline='': 在打开文件时,对于csv.writer,必须指定newline=''。这是因为csv模块会自行处理换行符,如果Python的默认换行转换机制也介入,可能会导致文件中出现双重换行,或在不同操作系统间产生兼容性问题。

正确实现示例:

import csv

# 假设TestExport.csv是你的源文件
source_csv_path = "//server2/shared/Data/TestExport.csv"

with open(source_csv_path, 'r', encoding='utf-8') as in_f: # 建议指定编码
    reader = csv.DictReader(in_f)
    for row in reader:
        # 根据'FileName'字段生成新CSV的文件名
        file_name = '{0}.csv'.format(row['FileName'])

        # 以写入模式打开新文件,并指定newline=''
        with open(file_name, 'w', newline='', encoding='utf-8') as out_f: # 建议指定编码
            # 创建csv写入器,指定逗号为分隔符
            writer = csv.writer(out_f, delimiter=',')

            # 写入Order Number和Date字段。writerow接受一个列表
            writer.writerow([row['Order Number'], row['Date']])

print("所有行已成功拆分并写入独立CSV文件。")
登录后复制

这段代码能够正确地将每一行数据拆分并写入到各自的CSV文件中,每个新文件只包含Order Number和Date两个字段,并以逗号分隔。

3. 处理文件名冲突与资源管理:使用contextlib.ExitStack

上述解决方案虽然正确,但存在一个潜在问题:如果源CSV文件中有两行或多行具有相同的File Name字段值,那么后一行的数据将覆盖前一行的数据,因为每次循环都会重新打开并清空同名文件。此外,频繁地打开和关闭文件也可能影响性能。

为了解决这个问题,我们可以采用更高级的策略:

  1. 复用写入器: 使用一个字典来存储已经创建的csv.writer对象。当遇到一个已存在文件名的行时,直接使用字典中对应的写入器追加数据,而不是重新创建文件。
  2. 统一文件管理: 由于我们会打开多个文件并保持它们处于打开状态以供复用,我们需要一种机制来确保所有这些文件最终都能被正确关闭。contextlib.ExitStack是处理这种情况的理想工具。它允许你在一个with语句块中管理多个上下文管理器(如文件对象),并在with块结束时自动关闭所有被管理的资源。

健壮的解决方案示例:

import csv
import contextlib

source_csv_path = "//server2/shared/Data/TestExport.csv"

with open(source_csv_path, 'r', encoding='utf-8') as in_f:
    # writers字典用于存储每个文件对应的csv.writer对象
    # 键是文件名,值是对应的csv.writer实例
    writers = {}

    # 使用ExitStack来管理所有打开的文件对象
    with contextlib.ExitStack() as stack:
        reader = csv.DictReader(in_f)
        for row in reader:
            file_name = '{0}.csv'.format(row['FileName'])

            # 尝试从writers字典中获取当前文件名的写入器
            writer = writers.get(file_name)

            # 如果该文件名的写入器尚未创建
            if writer is None:
                # 使用stack.enter_context()打开新文件。
                # ExitStack会负责在with块结束时关闭此文件。
                out_f = stack.enter_context(open(file_name, 'w', newline='', encoding='utf-8'))

                # 创建新的csv写入器并存储到writers字典中
                writer = csv.writer(out_f)
                writers[file_name] = writer

                # (可选)为新创建的文件写入表头
                # 如果不需要表头,可以删除下面这行
                writer.writerow(['OrderNumber', 'Date'])

            # 使用获取到的(或新创建的)写入器写入数据行
            writer.writerow([row['Order Number'], row['Date']])

print("所有行已成功拆分并写入独立CSV文件,重复文件名的数据已追加。")
登录后复制

4. 代码详解与注意事项

  • contextlib.ExitStack: 这是一个强大的上下文管理器。通过stack.enter_context(resource),你可以将任何上下文管理器(如open()返回的文件对象)注册到ExitStack中。当最外层的with contextlib.ExitStack() as stack:块结束时,无论以何种方式(正常退出、异常),ExitStack都会确保所有注册的资源按LIFO(后进先出)顺序被正确关闭。这避免了手动管理多个文件句柄的复杂性。
  • writers 字典: 这个字典是实现写入器复用的关键。它将文件名映射到对应的csv.writer对象。当处理一行数据时,程序首先检查writers中是否已有该文件名的写入器。如果没有,就创建一个新的文件和写入器,并将其添加到字典中;如果已经存在,则直接使用已有的写入器进行写入。
  • 可选的表头写入: 在if writer is None:块中,我们可以在文件首次被创建时写入一个表头(writer.writerow(['OrderNumber', 'Date']))。这确保了每个新生成的CSV文件都有一个清晰的表头,并且表头只会被写入一次。如果不需要表头,可以删除这行代码。
  • 编码(encoding='utf-8'): 在打开文件时,显式指定编码是一个好习惯,尤其是处理包含非ASCII字符的数据时,utf-8是推荐的通用编码。
  • 性能: 相比于每次循环都打开和关闭文件,使用ExitStack和写入器复用可以显著提高处理大量数据时的性能,因为它减少了文件I/O操作的开销。

总结

通过本教程,我们学习了如何使用Python的csv模块将一个CSV文件按行拆分为多个独立的CSV文件。从基础的csv.writer使用到更高级的contextlib.ExitStack和字典组合,我们解决了文件名冲突和资源管理问题,确保了数据拆分过程的准确性、健壮性和高效性。掌握这些技术将帮助你在处理CSV数据时更加灵活和专业。

以上就是使用Python将CSV文件按行拆分为多个独立文件并妥善管理的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号