本地 CSV 清洗的小细节:先标准化,再去重,并保留可检查的报告

AI 辅助创作说明:本文及示例由 Codex 生成并在本地运行测试,使用虚构数据;不代表作者的生产环境使用经历。示例只处理内存中的文本记录,不是完整 CSV 文件工具。

同样是"清空格、删重复",操作顺序不同,输出可能不同。

假设 CSV 已按正确编码读成文本,一行是 ("001", " 阿青 "),另一行是 ("001", "阿青")。如果先精确去重,两行不同,都会留下;之后再去掉首尾空格,结果里反而出现两行一模一样的内容。

如果业务已经确认这些首尾空格没有意义,就应该先按约定清理空格,再根据清理后的值做比较。反过来,若空格本来就是编号的一部分,不能直接套用这个顺序去删数据。

把顺序和计数放在同一个小函数里

下面固定为两列文本记录。它不做数值推断、不转大小写、不覆盖输入;只演示去首尾空格、排除全空行、整行精确去重,并保留首次出现的顺序。

python 复制代码
def clean_rows(rows, *, trim=True):
    result = []
    seen = set()
    report = {"input": 0, "blank_removed": 0, "duplicate_removed": 0, "output": 0}
    for row in rows:
        if not isinstance(row, (tuple, list)) or len(row) != 2:
            raise ValueError("expected two text fields")
        if not all(isinstance(value, str) for value in row):
            raise ValueError("fields must be text")
        report["input"] += 1
        normalized = tuple(value.strip() if trim else value for value in row)
        if all(value == "" for value in normalized):
            report["blank_removed"] += 1
        elif normalized in seen:
            report["duplicate_removed"] += 1
        else:
            seen.add(normalized)
            result.append(normalized)
    report["output"] = len(result)
    return result, report


rows = [("001", " 阿青 "), ("001", "阿青"), ("1", "阿青"),
        ("", ""), ("A", "X"), ("a", "X")]
result, report = clean_rows(rows)
print(result)
print(report)

本地运行输出:

text 复制代码
[('001', '阿青'), ('1', '阿青'), ('A', 'X'), ('a', 'X')]
{'input': 6, 'blank_removed': 1, 'duplicate_removed': 1, 'output': 4}

0011 仍是不同编号,Aa 也没有被合并。全空行先被分类为"空行排除",不再重复计入"重复排除"。这让报告满足一个可检查的等式:

输入条数 = 输出条数 + 空行排除条数 + 重复排除条数。

这个等式只能核对计数有没有漏记或重记,不能证明业务规则正确。规则错了,计数仍然可以完全对上。

最小验收不只看删除条数

下面接在前一段代码后执行,核对结果、顺序、输入不变和非法字段。

python 复制代码
assert result == [("001", "阿青"), ("1", "阿青"), ("A", "X"), ("a", "X")]
assert report == {"input": 6, "blank_removed": 1, "duplicate_removed": 1, "output": 4}
assert rows[0] == ("001", " 阿青 ")
assert clean_rows([]) == ([], {"input": 0, "blank_removed": 0, "duplicate_removed": 0, "output": 0})
assert clean_rows([( " ", "	")])[1]["blank_removed"] == 1
assert clean_rows(rows, trim=False)[1]["output"] == 5
assert clean_rows([("x", "1"), ("x", "1"), ("x", "1")])[1]["duplicate_removed"] == 2
assert report["input"] == report["output"] + report["blank_removed"] + report["duplicate_removed"]
for invalid in [[("a",)], [("a", 1)]]:
    try:
        clean_rows(invalid)
    except ValueError:
        pass
    else:
        raise AssertionError("invalid input should be rejected")
print("10 checks passed")

这两段示例在 Python 3.13 本地运行,通过以上 10 项检查。这里只检查展示的例子和边界,不代表所有编码、文件大小或真实业务数据都经过验证。

接回文件处理时,还缺哪些边界

这个函数没有读写磁盘,也不处理表头、编码错误、不同列数或大文件内存上限。接到 CSV 文件上时,需要单独检查这些边界:

  • 表头是否符合约定,数据行是否等宽;不能把表头参与去重。
  • 编码是否正确;乱码不应被当作成功清洗后的值。
  • 输出路径不得等于原文件,已有输出文件要有明确的冲突处理。
  • 数据文件与报告分别是否保存成功;不能只成功写了其中一个就报告全部完成。
  • 另做面向表格软件的公式风险处理时,要说明是否会改变原始文本,不能把导出安全转义偷偷算作业务标准化。

先定义字段含义和操作顺序,再检查具体保留内容,最后核对报告。一个能说明"改了什么、为什么改、还有什么没处理"的小流程,比单独显示一个删除数量更容易验收。

相关推荐
AC赳赳老秦2 小时前
文旅市场公开数据分析:基于 OpenClaw 采集景区客流与门票公示数据,生成区域文旅热度监测报告
java·c语言·python·php·symfony·deepseek·openclaw
青 春 记 忆2 小时前
零基础入门python68:FastAPI 完整博客运行与项目验收
python·fastapi·后端开发
鹿鹿学长2 小时前
发题前三天,组委会在过三道关:赛题七渠道首发、知网统一收卷、AI 详情 PDF 首进支撑材料
python·自动化
geovindu2 小时前
python: Face Recognition
开发语言·后端·python·人脸识别
2601_954811823 小时前
AI科学实验室MHS标准解读:AI智能体如何统一控制实验室设备接口
人工智能·python
一位正在转型AI全栈的前端工程师3 小时前
AI 全栈学习之旅 -Week 9:什么是AI Agent?从Function Calling到LangGraph
前端·python
用户0332126663673 小时前
使用 Python 为 PowerPoint 设置背景色和背景图【附代码示例】
python
ever_up9733 小时前
LangChain基础知识概述1
人工智能·python·langchain
asdzx673 小时前
Python 实现 PDF 文本查找与高亮标注
开发语言·python·pdf