Data Cleaning Horror Stories: Lessons from 10 Years of Messy CSVs

March 2026 · 19 min read · 4,565 words · Last Updated: March 31, 2026Advanced
# 数据清理恐怖故事:10年混乱CSV的教训 2019年,我曾与一家电信公司合作,一字符不可见的Unicode字符引发了34万美元的账单错误。这个文件在Excel、Notepad++、VS Code中看起来完美,甚至在终端用`cat`管道时也是如此。每次视觉检查都显示干净、格式正确的列名。但账单系统总是拒绝“customer_id”字段,声称它不存在。 三周。这就是五名工程师、两名数据分析师和一位非常紧张的项目经理找到问题所花费的时间。我们重写了导入脚本,质疑我们的数据库架构,甚至怀疑我们的ETL管道中存在错误。答案是?“customer”和“_id”之间隐形的零宽非连接符(U+200C)字符。标题并不是“customer_id”——而是“customer‌_id”。对每个人眼睛来说,它们是相同的。对每个计算机系统来说,它们是完全不同的字段。 这次事件给公司带来了34万美元的延迟账单周期、紧急外包费用和因发票延迟而给予客户的信用。它也教会了我职业生涯中最重要的一课:CSV文件并不简单。它们是伪装成电子表格的地雷,每位数据工程师都需要以应有的偏执对待它们。 在过去的十年中,我为财富500强公司清理数据集,涉及金融、医疗、零售和电信领域。我见过腐蚀患者记录的编码噩梦,破坏财务对账的虚幻空白,以及如此创意的日期格式,以至于只有那些真正仇恨未来数据工程师的人才能设计出来。本文是我尝试让你免受同样痛苦的尝试。

编码大灾难:当UTF-8不是UTF-8时

我见过的最严重的数据灾难发生在2017年的一家跨国零售连锁公司。他们将来自12个国家的47个区域数据库的客户数据整合到一个数据仓库中。听起来简单,对吧?导出为CSV,导入到仓库,运行一些去重逻辑,然后完成。 但来自法国分部的CSV文件总是破坏名称。“François”变成了“François”。“Chloé”变成了“Chloé”。德国分部对变音符号也有类似问题。日本分部的数据完全无法读取—只是一行行的问号和替代字符。 根本原因?每个区域团队使用不同的编码导出了他们的CSV。法国使用ISO-8859-1(Latin-1)。德国使用Windows-1252。日本使用Shift-JIS。英国和美国团队使用UTF-8,但有些是带BOM(字节顺序标记)的UTF-8,有些则没有。西班牙有一个团队不知怎么地将他们的数据导出为UTF-16LE。 整合项目原本计划三个月,实际花费了十一个月。我们不得不构建一个自定义编码检测管道,来: 1. 尝试使用多个库(chardet、charset-normalizer和自定义启发式方法)检测编码 2. 通过检查每种语言中的常见字符模式来验证检测结果 3. 将所有内容转换为不带BOM的UTF-8 4. 记录每个转换并带有置信度分数以供人工审核 即使有了这个管道,我们仍然有3%的错误率需要手动修正。这是4700万客户记录中的3%——140万条名称需要人工审查。 教训是什么?决不要相信CSV的编码。绝对不要。即使有人告诉你“它绝对是UTF-8”,也要验证它。我见过声称在元数据中为UTF-8的文件,但实际上是带有高ASCII字符的Windows-1252。我见过带有随机ISO-8859-1块的UTF-8文件,这是因为有人从旧系统复制粘贴。我甚至见过一个文件在导出脚本崩溃并在不同区域设置下重启时,编码中途切换。 现在,任何跨过我桌子的CSV文件在我查看数据之前都会运行编码验证脚本。这为我节省了无数时间,并防止了至少十几次重大事件。

虚幻的空白(除非真的存在)

2018年,我被请来修复一个已经失败了六个月的财务对账系统。该公司是一家处理数十亿美元交易的支付处理商。它们的对账过程将从数据库提取的交易记录与来自银行合作伙伴的CSV报告进行比较。系统每天报告数千个不匹配——在银行报告中出现但在他们的数据库中没有的交易,或反之亦然。 财务团队正在手动对账这些不匹配,每周工作60小时以跟上进度。他们会检查每个标记的交易,发现它确实在两个系统中都存在。交易ID完美匹配。但自动化系统仍然将它们标记为不匹配。 我花了两天时间分析代码、数据库查询和CSV解析逻辑。一切看起来都正确。然后我做了一件从一开始就应该明显的事情:我在十六进制编辑器中打开了CSV。 果然,每个银行的CSV文件中的交易ID都有一个结尾空格。在Excel中不可见。在大多数文本编辑器中也不可见。但在十六进制转储中可以看到:`54 52 41 4E 53 31 32 33 34 35 20`而不是`54 52 41 4E 53 31 32 33 34 35`。最后的`20`是一个空格字符。 数据库中存储的交易ID没有结尾空格。比较逻辑在执行精确字符串匹配。“TRANS12345” ≠ “TRANS12345 ”。成千上万的错误不匹配,数百小时的浪费,都是因为一个简单的结尾空格字符。 但这就更糟糕了:结尾空格并不一致。有些交易ID有,有些没有。有些有结尾空格,有些则有前导空格,还有一些两种都有。有些用制表符代替空格。有一个不容小觑的文件包含空格、制表符和不换行空格(U+00A0)的混合。 解决方案很简单——在导入时修剪所有空白。但教训是深刻的:CSV中的空白从来不是意外,总是有问题,并且常常是隐形的。我现在有一条规则:每个字符串字段在导入时都要修剪,不例外。我不在乎业务逻辑说字段应该保留空白。我不在乎有人坚持数据是干净的。修剪一切。 我还学会了关注其他隐形字符:零宽空格(U+200B)、零宽非连接符(U+200C)、零宽连接符(U+200D)以及有时出现在文件中间的可怕字节顺序标记(U+FEFF)。这些字符是机器中的幽灵,对人类来说是不可见的,但对计算机却非常真实。

破坏国际商业的日期格式

让我告诉你,我遇到过一个如此诅咒、如此根本性破损的日期格式,它至今仍萦绕在我梦中。这发生在一家协调亚洲制造商与北美和欧洲零售商之间运输的物流公司。 系统如此工作:制造商会上传包含交货细节的CSV文件,包括提货日期、预计交付日期和报关日期。系统会解析这些日期,计算运输时间,并与运输公司和报关代理协调。 多年来一切都很好。然后,在2016年3月,系统开始为过去的日期安排运输。应该在2016年3月15日提货的集装箱被安排在1916年3月15日。报关文件被提交的日期早于集装箱运输的发明。 根本原因?Excel的自动日期格式化结合地区日期格式差异和对日期工作方式的真正惊人误解。 发生的事情是: 1. 一名中国制造商输入日期,如“3/15/2016”(2016年3月15日,MM/DD/YYYY格式) 2. Excel会将其解释为日期,并内部将其存储为一个序列号(42444,表示2016年3月15日) 3. 导出为CSV时,Excel会根据系统区域格式化它 4. 中国系统区域使用YYYY-MM-DD格式,因此导出为“2016-03-15” 5. 我们的导入系统配置为MM/DD/YYYY格式,因此会将“2016-03-15”解析为“2016/03/15”(第2016个月,第3天,第15年) 6. 由于第2016个月无效,解析器会回退到将其解释为“20/16/03/15” 7. 通过一系列越来越绝望的解析尝试,它最终会停留在“03/15/1916” 但等等,更糟糕的是。一些制造商使用DD/MM/YYYY格式。一些使用YYYY-MM-DD。一些使用MM/DD/YY(两位数年份)。还有一位来自台湾的制造商使用民国历法,其中第105年对应于2016年公历(1912 + 105)。 我们最终得到了跨越1916年至2116年的日期,1970年(Unix纪元)周围尤其密集,因为一些系统以Unix时间戳导出日期,而我们的解析器将其解释为YYYYMMDD格式。 解决方案需要: - 实施多策略日期解析器,尝试检测格式 - 添加验证规则(拒绝2000年之前或2050年之后的日期) - 要求制造商只能使用ISO 8601格式(YYYY-MM-DD) - 为CSV上传构建一个web界面,预览导入前解析的日期 - 创建一个包含各种可能格式日期的全面测试套件 即使有了所有这些安全措施,我们仍然偶尔遇到日期解析错误。就在上个月,我遇到一个CSV,某人输入了“2/29/2023”(2023年2月29日——一个不存在的日期,因为2023年不是闰年)。Excel乐于接受,存储为序列号45000,并导出为“2023-02-29”。我们的系统导入后,验证该格式正确,并安排在一个不存在的日期进行运输。
“日期的问题在于每个人都认为自己理解它们,但实际上没有人能真正理解。日期是文化构造,而不是数学构造。它们有时区、夏令时、闰年、闰秒和日历改革。在不同国家,它们有不同格式,在不同年代有不同起点,在不同上下文中具有不同含义。而CSV文件由于完全缺乏元数据,没法知道哪个解释是正确的。”
这位同事的这句话完美地捕捉了日期问题。CSV没有数据类型。没有架构。它们只是文本。当你在CSV中看到“01/02/03”时,表示2003年1月2日?2003年2月1日?2001年3月2日?2001年2月3日?没有上下文就无法知道,而上下文正是CSV所不提供的。

不是数字的数字

以下是我遇到的最常见数值数据问题的表格,包括它们的频率和典型影响:
问题类型 频率 典型影响 示例
千位分隔符 非常高(60%) 导入失败、类型错误 “1,234.56”被解析为字符串
货币符号 高(45%) 导入失败、计算错误 “$1,234.56”或“€1.234,56”
小数分隔符差异 高(40%) 灾难性计算错误 “1.234,56”(欧洲)与“1,234.56”(美国)
科学记数法 中等(25%) 精度损失、误解 “1.23E+05”或“1.23456789E-10”
前导零 中等(30%) 数据丢失、ID损坏 “00123”变为“123”
百分号 中等(20%) 100倍计算错误 “15%”存储为15而不是0.15
负数格式 低(15%) 符号丢失、不正确计算 “(123”
C

Written by the CSV-X Team

Our editorial team specializes in data analysis and spreadsheet management. We research, test, and write in-depth guides to help you work smarter with the right tools.

Share This Article

Twitter LinkedIn Reddit HN

Put this into practice

Try Our Free Tools →

📬 Stay Updated

Get notified about new tools and features. No spam.