--- categories: Repost date: "2025-03-29T00:00:00Z" tags: - 信息技术 - 翻译 slug: A-love-letter-to-the-CSV-format title: 致 CSV 格式的一封情书 --- 原文:[LOVE_LETTER.md](https://github.com/medialab/xan/blob/master/docs/LOVE_LETTER.md) 授权协议:[MIT](https://github.com/medialab/xan/blob/master/LICENSE-MIT) 翻译:`gemini-2.5-pro`,有初步校对。 --- ## 致 CSV 格式的一封情书 _或者说,为什么那些声称 CSV 已死的人是错的_ 差不多每个月,都会有一篇新的博客文章冒出来,宣称 CSV 即将消亡,取而代之的是某种“明显更优越”的格式([parquet](https://parquet.apache.org/)、换行符分隔的 JSON、[MessagePack](https://msgpack.org/) 记录等)。可悲的是,这些文章通常只提供非常狭隘和带有偏见的比较,而且往往没有理解是什么让 CSV 成为一种似乎无法被杀死的、数据序列化的中流砥柱。 因此,我写这篇文章的意图,就是为这种数据格式写一封情书。它经常因为错误的原因而受到批评,尤其是当讨厌它在某种程度上被视为一种“酷”的行为时。我的观点远非是说 CSV 是什么万能灵药,而是希望阐明这种格式一些有时被忽视的优点。 ## 1. CSV 极其简单 CSV 的规范就体现在它的标题里:“逗号分隔值”(comma separated values)。好吧,这是个谎言,但它的规范仍然可以用一条推文说清楚,并且可以在几秒钟内向任何人解释明白:逗号分隔值,换行符分隔行。现在,给包含逗号和换行符的值加上引号,将值中的引号加倍,就这样。这太简单了,你甚至可能在学习编程时,在不知道它已存在的情况下自己就发明了它。 当然,这并不意味着你不应该使用专门的 CSV 解析器/写入器,因为你*肯定*会把事情搞砸。 ## 2. CSV 是一个集体创意 没有人拥有 CSV。它没有真正的规范(是的,我知道那个备受争议的事后追认的 [RFC 4180](https://datatracker.ietf.org/doc/html/rfc4180)),只有一套大家默契地同意遵守的规则。它现在是,并将永远是一种开放、自由的集体创意。 ## 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](https://github.com/medialab/xan#readme),CSV 魔法师