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

使用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格式的写入,它能够自动处理字段分隔符和行结束符。

核心改进点:

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

Sitekick

一个ai登陆页面自动构建器

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

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

  • 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 csv文件 red Python Resource if date 字符串 循环 number 对象 ASCII

上一篇
下一篇