0

0

Python处理CSV文件列数不一致及编码错误的教程

DDD

DDD

发布时间:2025-11-17 14:13:19

|

717人浏览过

|

来源于php中文网

原创

Python处理CSV文件列数不一致及编码错误的教程

本教程旨在解决大型csv文件中常见的列数不一致和字符编码问题。我们将使用python的`csv`模块,介绍两种有效策略:首先,精确识别并报告每条列数不符的行及其详细信息;其次,针对海量数据,提供一种将连续异常行聚合成范围进行报告的方法。通过这些技术,用户能够高效地定位并理解数据质量问题,为后续的数据清洗和导入工作奠定基础,同时避免常见的`unicodedecodeerror`。

引言:CSV数据质量挑战

在数据处理和导入(例如将Excel数据导入Teradata等数据库)过程中,CSV文件因其简洁性而被广泛使用。然而,原始CSV数据往往存在各种“脏”数据问题,尤其是在手动输入或缺乏严格数据验证的场景下。其中最常见的挑战包括:

  1. 列数不一致:部分行包含的列数多于或少于预期,这会导致数据解析错误和导入失败。
  2. 字符编码问题:文件编码与读取编码不匹配时,可能出现乱码,甚至引发UnicodeDecodeError,导致程序中断。

对于包含数十万行、数十甚至上百列的大型CSV文件,手动检查和修复是不可行的。本教程将展示如何利用Python及其强大的csv模块,高效地识别并报告这些数据质量问题。

方法一:识别并报告单个异常行

处理列数不一致问题的第一步是准确识别哪些行存在问题。相比于简单地统计逗号数量,使用Python内置的csv模块是更健壮的方法,因为它能够正确处理包含逗号的引用字段以及嵌入的换行符。

核心思路

  1. 设定期望列数:明确CSV文件应该包含多少列。
  2. 逐行读取:使用csv.reader迭代器逐行读取数据。
  3. 比较列数:对于每一行,检查其解析后的列数是否与期望值一致。
  4. 记录异常:如果列数不符,则记录该行的行号和实际列数。

示例代码

以下代码演示了如何生成一个报告,列出所有列数不符合预期(例如,期望66列)的行及其对应的实际列数。

QIMI奇觅
QIMI奇觅

美图推出的游戏行业广告AI制作与投放一体化平台

下载

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

import csv

# 设定期望的列数,根据实际情况修改
EXPECTED_COLS = 66 
INPUT_FILE = 'Data.csv'
OUTPUT_FILE_FLAT = 'output_anomalous_rows.csv'

def report_single_anomalies(input_csv_path, output_report_path, expected_columns):
    """
    识别并报告CSV文件中列数不一致的单个行。

    Args:
        input_csv_path (str): 输入CSV文件的路径。
        output_report_path (str): 输出报告CSV文件的路径。
        expected_columns (int): CSV文件期望的列数。
    """
    print(f"开始分析文件: {input_csv_path}")
    print(f"期望列数: {expected_columns}")

    try:
        with open(output_report_path, 'w', newline='', encoding='utf-8') as f_out:
            writer = csv.writer(f_out)
            writer.writerow(["行号", "实际列数"]) # 写入报告头

            # 推荐使用 'utf-8' 编码,如果遇到 UnicodeDecodeError,可尝试 'latin-1' 或 'gbk'
            with open(input_csv_path, 'r', newline='', encoding='utf-8') as f_in:
                reader = csv.reader(f_in)

                # 跳过CSV文件的标题行(如果存在)
                try:
                    header = next(reader)
                    # 如果需要,可以在这里检查标题行的列数
                    print(f"已跳过标题行: {header}")
                except StopIteration:
                    print("文件为空或不含数据。")
                    return

                # 使用enumerate从1开始计数,对应文件中的行号
                for i, row in enumerate(reader, start=1):
                    actual_cols = len(row)
                    if actual_cols != expected_columns:
                        writer.writerow([i, actual_cols])
                        print(f"发现异常行 - 行号: {i}, 实际列数: {actual_cols}")
        print(f"异常行报告已生成至: {output_report_path}")

    except FileNotFoundError:
        print(f"错误:文件 '{input_csv_path}' 未找到。")
    except UnicodeDecodeError as e:
        print(f"字符编码错误:{e}。请尝试更改文件编码参数,例如 'latin-1' 或 'gbk'。")
    except Exception as e:
        print(f"发生未知错误:{e}")

# 调用函数进行分析
report_single_anomalies(INPUT_FILE, OUTPUT_FILE_FLAT, EXPECTED_COLS)

代码解析

  • import csv: 导入Python的csv模块,这是处理CSV文件的标准库。
  • EXPECTED_COLS = 66: 定义一个常量来存储期望的列数。请根据您的实际数据调整此值。
  • with open(..., newline='', encoding='utf-8'):
    • newline='':这是csv模块推荐的最佳实践,可以防止在Windows系统上写入时出现额外的空行,并正确处理包含换行符的字段。
    • encoding='utf-8':这是解决UnicodeDecodeError的关键。指定正确的字符编码至关重要。如果utf-8不起作用,您的文件可能使用了其他编码,例如latin-1、gbk或cp1252。请根据文件实际编码进行尝试。
  • writer = csv.writer(f_out): 创建一个csv.writer对象,用于将异常信息写入报告文件。
  • reader = csv.reader(f_in): 创建一个csv.reader对象,它将CSV文件视为一个迭代器,每次迭代返回一行数据(作为一个列表)。
  • next(reader): 用于跳过CSV文件的标题行。如果您的CSV文件没有标题行,请删除此行。
  • for i, row in enumerate(reader, start=1):
    • enumerate函数在迭代reader时提供一个计数器i,start=1确保行号从1开始,与大多数文件编辑器中的行号一致。
    • row变量是一个列表,其中包含了当前行的所有字段。
  • len(row): 获取当前行的实际列数。
  • if actual_cols != expected_columns:: 条件判断,如果实际列数与期望列数不符,则将其写入报告文件。

方法二:聚合异常行范围报告

对于拥有数十万行甚至更多数据的大型CSV文件,如果异常行非常多,生成一个包含每一条异常行的报告可能会非常庞大且难以阅读。在这种情况下,将连续的、具有相同异常列数的行聚合成一个范围进行报告,会更加高效和实用。

核心思路

  1. 跟踪状态:维护一个状态变量,记录当前是否正在跟踪一个异常行范围,以及该范围的起始行号和列数。
  2. 状态切换:当遇到列数与前一行不同,或者从异常状态切换到正常状态时,结束当前范围的记录并写入报告。
  3. 聚合报告:将连续的具有相同异常列数的行合并为“起始行 - 结束行”的格式。

示例代码

import csv

# 设定期望的列数,根据实际情况修改
EXPECTED_COLS = 66
INPUT_FILE = 'Data.csv'
OUTPUT_FILE_RANGES = 'output_anomalous_ranges.csv'

def write_report_row(writer, cols_ct, row_start, row_end):
    """
    将异常行范围写入报告文件。
    如果起始行和结束行相同,则只写入起始行。
    """
    if row_start == row_end:
        writer.writerow([cols_ct, row_start, ""]) # 单行异常,结束行留空
    else:
        writer.writerow([cols_ct, row_start, row_end])

def report_anomalous_ranges(input_csv_path, output_report_path, expected_columns):
    """
    识别并报告CSV文件中列数不一致的行范围。

    Args:
        input_csv_path (str): 输入CSV文件的路径。
        output_report_path (str): 输出报告CSV文件的路径。
        expected_columns (int): CSV文件期望的列数。
    """
    print(f"开始分析文件 (范围报告): {input_csv_path}")
    print(f"期望列数: {expected_columns}")

    # 定义追踪状态常量
    NO_TRACK = -1 

    try:
        with open(output_report_path, 'w', newline='', encoding='utf-8') as f_out:
            writer = csv.writer(f_out)
            writer.writerow(["实际列数", "起始行", "结束行"]) # 写入报告头

            with open(input_csv_path, 'r', newline='', encoding='utf-8') as f_in:
                reader = csv.reader(f_in)

                # 处理标题行
                try:
                    header = next(reader)
                    # 可以在这里根据标题行确定期望列数,或检查其列数
                    print(f"已跳过标题行: {header}")
                except StopIteration:
                    print("文件为空或不含数据。")
                    return

                # 初始化追踪变量
                tracking = False # 是否正在追踪一个异常范围
                row_num_start = NO_TRACK # 当前异常范围的起始行号
                cols_count_in_range = NO_TRACK # 当前异常范围的列数

                last_processed_row_index = 0 # 记录循环中最后处理的行索引 (用于最终flush)

                for i, row in enumerate(reader, start=1):
                    last_processed_row_index = i # 更新最后处理的行索引
                    current_cols = len(row)

                    # 如果当前行的列数与当前追踪的列数不同
                    if current_cols != cols_count_in_range:
                        # 如果正在追踪一个范围,则结束它并写入报告
                        if tracking:
                            write_report_row(writer, cols_count_in_range, row_num_start, i - 1) # i-1 是前一行

                        # 判断当前行是否为异常行
                        if current_cols == expected_columns:
                            # 当前行正常,停止追踪
                            tracking = False
                            row_num_start = NO_TRACK
                            cols_count_in_range = NO_TRACK
                        else:
                            # 当前行异常,开始新的追踪
                            tracking = True
                            row_num_start = i
                            cols_count_in_range = current_cols
                    # 如果当前行的列数与当前追踪的列数相同,则继续追踪,无需操作

                # 循环结束后,如果还在追踪一个范围,需要将其写入报告
                if tracking:
                    write_report_row(writer, cols_count_in_range, row_num_start, last_processed_row_index)
        print(f"异常行范围报告已生成至: {output_report_path}")

    except FileNotFoundError:
        print(f"错误:文件 '{input_csv_path}' 未找到。")
    except UnicodeDecodeError as e:
        print(f"字符编码错误:{e}。请尝试更改文件编码参数,例如 'latin-1' 或 'gbk'。")
    except Exception as e:
        print(f"发生未知错误:{e}")

# 调用函数进行分析
report_anomalous_ranges(INPUT_FILE, OUTPUT_FILE_RANGES, EXPECTED_COLS)

代码解析

  • write_report_row 函数: 这是一个辅助函数,用于格式化写入报告行。它会判断一个范围是单行还是多行,并相应地调整输出。
  • tracking, row_num_start, cols_count_in_range: 这三个变量是状态机核心。
    • tracking:布尔值,表示当前是否正在记录一个异常行范围。
    • row_num_start:当前被追踪的异常范围的起始行号。
    • cols_count_in_range:当前被追踪的异常范围中所有行的列数。
  • if current_cols != cols_count_in_range:: 这是状态切换的主要逻辑。当遇到一个列数与当前追踪的范围不同(或开始新的追踪)的行时,会触发处理。
    • 如果tracking为True,说明之前的范围结束了,调用write_report_row写入报告。
    • 然后根据current_cols是否等于expected_columns来决定是停止追踪(当前行正常)还是开始新的追踪(当前行异常)。
  • 循环结束后的if tracking:: 循环结束后,如果最后一个范围是异常的且正在被追踪,需要额外调用write_report_row来将其写入报告。

关键注意事项与最佳实践

  1. 文件编码处理
    • UnicodeDecodeError是处理文本文件时常见的错误。务必在open()函数中明确指定encoding参数。
    • encoding='utf-8'是现代Web和大多数系统推荐的通用编码。
    • 如果utf-8失败,常见的替代编码包括latin-1 (ISO-8859-1的超集,能表示256个字符,不会报错但可能乱码)、gbk (简体中文编码)、cp1252 (Windows默认编码)。您可能需要根据文件来源尝试不同的编码。
    • 对于不确定编码的文件,可以使用chardet等库进行编码检测。
  2. csv模块与手动计数比较
    • 不要使用line.count(',')来判断列数。这种方法无法正确处理包含逗号的引用字段(例如"City, State")或字段内部的换行符。
    • csv模块是专门为CSV格式设计的,能够正确解析这些复杂情况。
  3. 数据清洗策略:识别与修复分离
    • 本教程提供的是识别和报告异常行的工具。直接在读取过程中“当场修复”通常非常复杂且容易出错。
    • 推荐的工作流程是:首先使用这些脚本生成详细的异常报告,然后根据报告手动或通过另一个专门的脚本进行数据清洗和转换,最后再进行导入。
  4. 可扩展性与性能
    • 上述两种方法都采用逐行处理的方式,避免一次性将整个大文件加载到内存中,因此对于百万级别甚至千万级别的行数都具有良好的可扩展性。
    • Python的csv模块本身是C语言实现的,性能较高。

总结

通过本教程,您学习了如何使用Python的csv模块高效地识别CSV文件中的数据质量问题,特别是列数不一致和字符编码错误。我们提供了两种报告策略:针对小型数据集的单个异常行报告,以及针对大型数据集的聚合异常行范围报告。这些工具能够帮助您在数据导入前,清晰地了解数据质量状况,为后续的数据清洗和转换工作提供精确的指引,从而确保数据导入的顺利进行和数据质量的可靠性。记住,数据清洗是一个迭代的过程,从识别到修复,每一步都至关重要。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

401

2023.06.20

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

620

2023.07.25

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

354

2023.08.02

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

259

2023.08.09

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

606

2023.09.05

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

531

2023.09.20

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

646

2023.09.20

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

604

2023.09.22

clawdbot ai使用教程 保姆级clawdbot部署安装手册
clawdbot ai使用教程 保姆级clawdbot部署安装手册

Clawdbot是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

11

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Excel 教程
Excel 教程

共162课时 | 14.2万人学习

成为PHP架构师-自制PHP框架
成为PHP架构师-自制PHP框架

共28课时 | 2.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

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