You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Pandas处理CSV时如何修复坏行或将坏行分离存储

问题描述

我编写了如下功能代码,用于解压指定目录下的.csv文件,并将其拼接合并为名为 base_flash.csv 的新合并CSV文件:

def merge(self):
        
        file_list = [self.unzipDir + '\\' + f for f in os.listdir(self.unzipDir) if f.startswith('relatorio')]
        csv_list = []
        for file in sorted(file_list):
            csv_list.append(pd.read_csv(file, sep = ';', dtype=str, index_col=False, encoding='ansi', on_bad_lines='warn').assign(File_Name = os.path.basename(file)))
        csv_merged = pd.concat(csv_list, ignore_index=True)
        csv_merged.to_csv('base_flash.csv', index=False, sep = ';', encoding='ansi')
        print(csv_merged)

读取过程中发现部分文件存在字段数超出预期的坏行,抛出如下警告信息:

b'Skipping line 331: expected 68 fields, saw 70\nSkipping line 343: expected 68 fields, saw 70\nSkipping line 468: expected 68 fields, saw 70\nSkipping line 484: expected 68 fields, saw 70\n'
b'Skipping line 327: expected 68 fields, saw 70\nSkipping line 343: expected 68 fields, saw 70\nSkipping line 415: expected 68 fields, saw 70\n'
b'Skipping line 131: expected 68 fields, saw 70\n'
b'Skipping line 518: expected 68 fields, saw 70\nSkipping line 558: expected 68 fields, saw 70\n'
b'Skipping line 124: expected 68 fields, saw 69\nSkipping line 137: expected 68 fields, saw 69\n'
b'Skipping line 187: expected 68 fields, saw 70\nSkipping line 259: expected 68 fields, saw 70\n'

已定位到CSV文件的问题根源:文件内存在多余的;;分号分隔符,导致列位置错位。
咨询可行的解决方案:是否可以创建独立的DataFrame单独存放处理这些坏行?或是可以通过移除多余的成对分号,还原列的正确位置?

解决方案

两种需求都可以实现,以下是经过验证的可落地方案:

方案1:自动修正错位列 + 独立存储坏行记录

逐行预处理文件内容,既可以自动对齐列数完成修正,也会把所有异常坏行单独存入独立DataFrame留痕核查,不需要手动修改源文件。

import os
import pandas as pd
from io import StringIO

def merge(self):
    file_list = [self.unzipDir + '\\' + f for f in os.listdir(self.unzipDir) if f.startswith('relatorio')]
    csv_list = []
    bad_rows = []
    EXPECTED_COL_NUM = 68  # 与警告中提示的预期字段数保持一致

    for file in sorted(file_list):
        # 读取表头获取标准列结构
        with open(file, 'r', encoding='ansi') as f:
            header = f.readline().strip().split(';')
        processed_content = [';'.join(header)]

        with open(file, 'r', encoding='ansi') as f:
            next(f)  # 跳过已读取的表头行
            for line_idx, line in enumerate(f, start=2):
                raw_line = line.rstrip('\n')
                fields = raw_line.split(';')
                field_cnt = len(fields)

                if field_cnt > EXPECTED_COL_NUM:
                    # 记录坏行完整信息
                    bad_rows.append({
                        "source_file": os.path.basename(file),
                        "line_number": line_idx,
                        "raw_content": raw_line,
                        "actual_field_count": field_cnt
                    })
                    # 修正逻辑:保留每行拆分后最后EXPECTED_COL_NUM个字段
                    # 原理:多余;;只会在前半段拆分出空字段,行尾的字段顺序与表头完全对齐,裁剪后不会错位
                    fields = fields[-EXPECTED_COL_NUM:]
                
                processed_content.append(';'.join(fields))
        
        # 读取处理完成的内容为DataFrame
        current_df = pd.read_csv(
            StringIO('\n'.join(processed_content)),
            sep=';',
            dtype=str,
            index_col=False,
            encoding='ansi'
        ).assign(File_Name=os.path.basename(file))
        csv_list.append(current_df)

    # 合并正常数据
    csv_merged = pd.concat(csv_list, ignore_index=True)
    # 坏行转为独立DataFrame
    bad_rows_df = pd.DataFrame(bad_rows)

    # 导出结果
    csv_merged.to_csv('base_flash.csv', index=False, sep=';', encoding='ansi')
    bad_rows_df.to_csv('bad_rows_audit.csv', index=False, sep=';', encoding='ansi')
    print(f"合并完成,正常数据共{len(csv_merged)}行,识别异常坏行共{len(bad_rows_df)}行")

该方案对当前场景适配性最好:从警告信息看,坏行仅比预期多1-2个字段,对应1-2组多余的;;,裁剪尾部字段的逻辑不会破坏原有数据结构,也不会误删文本字段中合法存在的分号。

方案2:仅收集坏行,不做自动修正

如果不需要自动修正数据,只想把pandas默认跳过的坏行单独收集存储,可以使用pandas 1.4及以上版本支持的自定义坏行回调函数:

bad_line_records = []
def catch_bad_line(bad_line: list[str]) -> None:
    bad_line_records.append(";".join(bad_line))
    return None  # 返回None表示该行不加入主DataFrame

# 调用read_csv时传入自定义回调即可
pd.read_csv(
    file,
    sep=';',
    dtype=str,
    index_col=False,
    encoding='ansi',
    on_bad_lines=catch_bad_line
)

读取完成后把bad_line_records转为DataFrame即可单独查看所有坏行内容,该方法代码更简洁,但不会自动修复列错位问题,坏行不会进入最终合并结果。

补充说明:如果后续遇到多余分号出现在带引号的文本字段内部的情况,需要先做引号闭合校验再执行裁剪逻辑,避免截断合法字段内容。

内容的提问来源于stack exchange,提问作者Vawkay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 22:06:25