
本文详细介绍了如何使用python的csv模块处理大规模csv文件中常见的列数不一致和unicodedecodeerror问题。通过示例代码,演示了如何准确识别并报告不符合预期列数的行,包括逐行报告和将连续的异常行合并为范围报告的两种策略。教程强调了csv模块的优势、正确的文件编码处理以及数据清洗前的错误识别方法,旨在帮助用户提升数据预处理的效率和准确性。
在数据处理流程中,尤其是当数据源自人工录入或不同系统导出时,CSV文件常常会出现各种格式问题。其中,最常见的挑战包括行与行之间列数不一致以及文件编码错误导致的UnicodeDecodeError。这些问题会严重阻碍数据导入到数据库(如Teradata)或进行后续分析。本教程将指导您如何利用Python的csv模块来高效地识别和报告这些问题,为后续的数据清洗奠定基础。
当CSV文件中的某些行包含的列数多于或少于预期时,通常是由于数据输入不规范、分隔符使用混乱或文本字段中包含未正确转义的分隔符。例如,如果一个CSV文件应有66列,但某些行只有65列或多达67列,这就会导致解析错误。
此外,UnicodeDecodeError: 'charmap' codec can't decode byte ... 错误通常发生在尝试以错误的字符编码读取文件时。例如,如果文件实际上是UTF-8编码,但尝试以默认的charmap(通常是操作系统的默认编码,如cp936或cp1252)读取,就会出现此错误。
简单的通过line.count(',')来计数分隔符的方法存在局限性,因为它无法正确处理包含逗号的带引号字段(例如"Hello, World"会被错误地计为两个逗号)。因此,使用Python内置的csv模块是更健壮和推荐的做法。
立即学习“Python免费学习笔记(深入)”;
Python的csv模块专为处理CSV文件而设计,能够正确处理带引号的字段、不同分隔符和换行符。
在开始之前,您需要明确CSV文件预期的列数。例如,如果您的数据应该有66列,那么N_COLS就设置为66。
import csv # 定义预期的列数 N_COLS = 66 # 定义输入和输出文件名 INPUT_FILE = 'Data.csv' OUTPUT_REPORT_FILE = 'malformed_rows_report.csv'
最直接的方法是遍历CSV文件的每一行,检查其解析后的列数是否与预期相符。如果不符,则记录该行的行号和实际列数。
import csv
# 定义预期的列数
N_COLS = 66
# 定义输入和输出文件名
INPUT_FILE = 'Data.csv'
OUTPUT_REPORT_FILE = 'malformed_rows_report.csv'
try:
with open(OUTPUT_REPORT_FILE, "w", newline='', encoding='utf-8') as f_out:
writer = csv.writer(f_out)
writer.writerow(["行号", "实际列数"]) # 写入报告头
# 注意:在这里指定正确的编码,例如 'utf-8' 或 'latin-1',以避免 UnicodeDecodeError
# newline='' 参数对于 csv 模块是必需的,以防止在 Windows 上出现额外的空行
with open(INPUT_FILE, "r", newline='', encoding='utf-8') as f_in:
reader = csv.reader(f_in)
# 跳过CSV文件的标题行(如果存在)
# 如果文件没有标题行,请注释掉或删除下一行
try:
header = next(reader)
# 如果需要,可以检查标题行的列数
if len(header) != N_COLS:
print(f"警告: 标题行 ({header}) 的列数 ({len(header)}) 与预期 ({N_COLS}) 不符。")
except StopIteration:
print("文件为空或只有标题行。")
# 如果文件只有标题行,我们可能不需要继续处理
# 可以选择在这里退出或继续,取决于具体需求
for i, row in enumerate(reader, start=1): # start=1 表示从数据行第一行开始计数
if len(row) != N_COLS:
writer.writerow([i, len(row)])
print(f"检测到异常行: 行号 {i}, 实际列数 {len(row)}")
print(f"异常行报告已生成到: {OUTPUT_REPORT_FILE}")
except FileNotFoundError:
print(f"错误: 输入文件 '{INPUT_FILE}' 未找到。")
except UnicodeDecodeError as e:
print(f"错误: 读取文件时发生编码问题。请尝试指定不同的编码 (例如 'latin-1')。详细信息: {e}")
except Exception as e:
print(f"发生未知错误: {e}")
代码解析与注意事项:
对于包含大量数据的CSV文件(如125,000行),逐行报告可能导致报告文件过大且难以阅读。更高效的方式是将具有相同异常列数的连续行合并为一个范围进行报告。
import csv
# 定义输入和输出文件名
INPUT_FILE = 'Data.csv'
OUTPUT_RANGES_FILE = 'malformed_ranges_report.csv'
def write_row_range(writer_obj, col_count, start_row, end_row):
"""
将列数、起始行和结束行写入报告。
如果起始行和结束行相同(单行异常),则结束行留空。
"""
if start_row == end_row:
writer_obj.writerow([col_count, start_row, ""])
else:
writer_obj.writerow([col_count, start_row, end_row])
try:
with open(OUTPUT_RANGES_FILE, "w", newline='', encoding='utf-8') as f_out:
writer = csv.writer(f_out)
writer.writerow(["实际列数", "起始行号", "结束行号"]) # 写入报告头
with open(INPUT_FILE, "r", newline='', encoding='utf-8') as f_in:
reader = csv.reader(f_in)
# 尝试读取标题行并确定期望的列数
try:
header_row = next(reader)
EXPECTED_COLS = len(header_row)
print(f"根据标题行确定预期列数为: {EXPECTED_COLS}")
except StopIteration:
print("错误: 文件为空或不包含数据行。无法确定预期列数。")
exit()
except Exception as e:
print(f"读取标题行时发生错误: {e}")
print("请手动设置 EXPECTED_COLS 变量或检查文件格式。")
# 如果无法从标题行确定,可以手动设置 EXPECTED_COLS
# 例如: EXPECTED_COLS = 66
exit()
# 初始化追踪状态
tracking = False
current_range_start_row = -1
current_range_cols_count = -1
total_rows_processed = 0
for i, row in enumerate(reader, start=1):
total_rows_processed = i # 记录当前处理到的总行数
current_row_cols = len(row)
if current_row_cols != EXPECTED_COLS: # 当前行是异常行
if not tracking: # 如果是新异常范围的开始
tracking = True
current_range_start_row = i
current_range_cols_count = current_row_cols
elif current_row_cols != current_range_cols_count: # 异常列数发生变化,结束前一个范围
write_row_range(writer, current_range_cols_count, current_range_start_row, i - 1)
# 开始新的异常范围
current_range_start_row = i
current_range_cols_count = current_row_cols
else: # 当前行是正常行
if tracking: # 如果之前正在追踪异常范围,现在结束它
write_row_range(writer, current_range_cols_count, current_range_start_row, i - 1)
tracking = False
current_range_start_row = -1
current_range_cols_count = -1
# 循环结束后,检查是否有未写入的异常范围
if tracking:
write_row_range(writer, current_range_cols_count, current_range_start_row, total_rows_processed)
print(f"异常行范围报告已生成到: {OUTPUT_RANGES_FILE}")
except FileNotFoundError:
print(f"错误: 输入文件 '{INPUT_FILE}' 未找到。")
except UnicodeDecodeError as e:
print(f"错误: 读取文件时发生编码问题。请尝试指定不同的编码 (例如 'latin-1')。详细信息: {e}")
except Exception as e:
print(f"发生未知错误: {e}")代码解析与注意事项:
通过上述Python脚本,您可以有效地识别CSV文件中列数不一致的问题,并生成易于分析的报告。这对于数据清洗和数据导入前的预处理至关重要。
关键注意事项:
通过遵循这些指导原则和使用提供的Python代码,您可以显著提高处理复杂CSV数据的效率和准确性,确保数据在进入下游系统前具备更高的质量。
以上就是使用Python高效识别和处理CSV文件中的列数不一致及编码问题的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号