致 CSV 格式的一封情书

原文:LOVE_LETTER.md

授权协议:MIT

翻译:gemini-2.5-pro,有初步校对。


致 CSV 格式的一封情书

或者说,为什么那些声称 CSV 已死的人是错的

差不多每个月,都会有一篇新的博客文章冒出来,宣称 CSV 即将消亡,取而代之的是某种“明显更优越”的格式(parquet、换行符分隔的 JSON、MessagePack 记录等)。可悲的是,这些文章通常只提供非常狭隘和带有偏见的比较,而且往往没有理解是什么让 CSV 成为一种似乎无法被杀死的、数据序列化的中流砥柱。

因此,我写这篇文章的意图,就是为这种数据格式写一封情书。它经常因为错误的原因而受到批评,尤其是当讨厌它在某种程度上被视为一种“酷”的行为时。我的观点远非是说 CSV 是什么万能灵药,而是希望阐明这种格式一些有时被忽视的优点。

1. CSV 极其简单

CSV 的规范就体现在它的标题里:“逗号分隔值”(comma separated values)。好吧,这是个谎言,但它的规范仍然可以用一条推文说清楚,并且可以在几秒钟内向任何人解释明白:逗号分隔值,换行符分隔行。现在,给包含逗号和换行符的值加上引号,将值中的引号加倍,就这样。这太简单了,你甚至可能在学习编程时,在不知道它已存在的情况下自己就发明了它。

当然,这并不意味着你不应该使用专门的 CSV 解析器/写入器,因为你肯定会把事情搞砸。

2. CSV 是一个集体创意

没有人拥有 CSV。它没有真正的规范(是的,我知道那个备受争议的事后追认的 RFC 4180),只有一套大家默契地同意遵守的规则。它现在是,并将永远是一种开放、自由的集体创意。

3. CSV 是文本

与 JSON、YAML 或 XML 一样,CSV 只是纯文本,你可以自由地选择任何你喜欢的编码方式。CSV 不是二进制格式,可以用任何文本编辑器打开,也不需要任何专门的程序来读取。这也就意味着,人类可以在某种程度上直接读取和编辑它。

4. CSV 是可流式处理的

CSV 可以非常容易地逐行读取,而需要的内存不会超过容纳单行数据所需的内存。这也意味着,任何一个普通人都能编写的简单程序,就能够用几千字节(kilobytes)的内存读取数 GB(gigabytes)的 CSV 数据。

相比之下,像 parquet 这样的列式(column-oriented)数据格式无法逐行流式传输文件,它们要么需要你在文件中来回跳转,要么需要你巧妙地缓冲内存,以免降低读取性能。

当然,如果你只对特定的列感兴趣,CSV 的表现就很糟糕,因为你确实需要读取整行数据才能访问你感兴趣的那部分。

列式数据格式当然非常适合 R、pandas 等工具的数据帧(dataframes)思维模式。但来自这些实践领域的 CSV 批评者,往往只关心那些期望所有数据都能装入内存的用例。

5. CSV 可以被追加写入

在 CSV 文件末尾添加新行是件轻而易举的事,而且效率非常高。只需以追加模式(a+)打开文件,然后开始写入就行了。

再次强调,列式数据格式做不到这一点,至少不能以一种直接的方式做到。它们实际上可以被看作是磁盘上的数据帧,就像数据帧一样,添加一个列非常高效,而添加一个新行则不然。

6. CSV 是动态类型的

请别走开。让我解释一下为什么这有时是件好事。有时在处理数据时,你可能希望有一些灵活性,尤其是在跨编程语言解析序列化数据时。

以 JavaScript 为例,它无法表示 64 位整数。或者想想各种语言、框架和库是如何看待空值(null values)的(别让我开始吐槽 pandas 和空值)。CSV 让你能够按照你认为合适的方式解析值,它实际上是动态类型的。但这既是一个优点,也可能成为一个潜在的坑,如果你不小心的话。

另外请注意,尽管这对于像 python 和 JavaScript 这样的高级语言可能很难做到,但你并非必须解码文本才能处理 CSV 单元格的值,出于性能考虑,你可以直接操作文本的二进制表示。

7. CSV 很简洁

由于表头(headers)只在文件开头写一次,这意味着格式本身的重复性自然非常低。想想 JSON 中的对象列表或 XML 中的等价物,你很快就会看到到处重复键(key)的代价。这并不是说 JSON 和 XML 压缩效果不好,但很少有格式能表现出这种自然的简洁性。

更重要的是,字符串通常已经被优化表示,而格式本身的开销(这里那里的一些逗号和引号)也保持在最低水平。当然,静态类型的数字可以用更简洁的方式表示,但你也无法在那里节省一个数量级。

8. 反转后的 CSV 仍然是有效的 CSV

这一点并不常被大家意识到,但一个反转(逐字节反转)后的 CSV 文件,仍然是有效的 CSV。这之所以成为可能,是因为通过双写引号来进行转义的绝妙设计,这意味着转义本身是回文(palindrome)的。如果 CSV 使用基于反斜杠的转义方案,就像表示字符串字面量时最常见的那样,这就行不通了。

但你为什么要关心这个呢?嗯,这意味着你可以非常高效且非常容易地读取 CSV 文件的最后几行。只需将文件的字节逆序输入给 CSV 解析器,然后反转生成的行和其单元格的字节,就大功告成了(不过,也许应该先读取表头行)。

这意味着你完全可以利用 CSV 的输出来高效地恢复一个被中止的进程。你确实可以在常数时间内读取和解析 CSV 文件的最后几行,因为你不需要读取整个文件,只需将自己定位到文件末尾,以反向缓冲字节并将其输入解析器即可。

9. Excel 讨厌 CSV

这清楚地表明 CSV 肯定做对了什么。

署名:xan,CSV 魔法师

请注意检查转载文章的版权情况。
最后更新于 2025-09-25 13:12:22
此处获取此页源码。
提交: 81240ebc 环境: production Hugo: 0.151.0 主题: 3.31.0-modified 时间: 1760521115101600 Cloudflare: Unknown
本博客内容仅供参考,作者不对其准确性、完整性或适用性作出任何明示或暗示的保证。因使用、引用或解读本博客内容所引发的任何直接或间接后果,作者概不承担任何责任。
本博客可能包含第三方转载内容,相关版权归原作者所有。转载内容仅为分享信息之目的,不代表作者观点。如涉及侵权,请联系删除。
使用 Hugo 构建
主题 StackJimmy 设计