跳转至

文件格式

CSV 文件格式

CSV 格式的诞生甚至比个人计算机(PC)更早。在上个世纪 70 年代的 IBM FORTRAN 77 已经定义了用逗号或空格作为文本定界符的处理方式,当时它被称为列表导向的 IO(List-Directed Input/Output )。

CSV 的名称大约是在 1983 年出现的。伴随着电子表格软件的兴起,人们发现该格式和电子表格存在天然的关联。并且由于使用人类可读的文本形式,而不是计算机可读的二进制格式,不仅使得操作员能够理解、分析和处理它们,也避免了不同机器之间的体系差异带来的问题,所以很快成为数据交换的流行选择。在早期电子表格软件 SuperCalc 手册中首次给了它名字:逗号分隔的值(Comma-Separated-Value),并规定了文件格式和处理方法。

CSV(即 Comma Separate Values)是以文本形式记录数据的文件(通常以逗号为分隔符),这种格式经常用来作为不同程序之间的数据交互的格式。

当然,用户手册是从具体使用角度出发的,并未给 CSV 格式一个非常严格的定义。

  • 2005 年,国际标准化组织 IETF 通过 RFC4180正式定义了 CSV 文件的格式要求。

  • 2014 年,又通过 RFC7111 补充规定了 CSV 文件在 Web 应用中的一些规范,主要是 MIME TypeURL 片段的正式约定。

  • 2015 年,W3C 给表格化的数据/元数据制定了一个推荐草案,试图将数据的格式和使用方法进行规范化,其中也包含了对 CSV 格式的要求。

CSV 格式在发展过程中也逐渐演变出其他一些分支和变种。从名字(Comma-Separated-Value)可以看出,坚持使用逗号才是正规的做法。但有些程序也开始使用 Tab 或管道符(|),甚至其他更多符号来进行分割。

在格式变化的同时,有的程序继续沿用 CSV 的名称,有的则引入了其他稍有变化的名字,比如 DSV(Delimiter-Separated Values)。这使得局面变得有点混乱,而这种混乱,到现在仍然在延续。

格式规范

CSV 的基本格式是逗号分隔的一系列字段。同时规范要求,所有字段都可以用引用符号(通常是双引号 " )包围起来。

shenzhen,2019,china
"shenzhen","2019","china"

至于何时应该加引号、何时不加,规范并无明确要求,而是把这个决定留给应用程序。但如果字段内容中出现了逗号,为了避免歧义,就必须加上引号。此外,如果字段内容中存在空白字符的话,为了明确起见,一般也建议加上:

shenzhen,"Oct 2019","Guangdong, China"

规范也没有指定 CSV 应使用何种字符编码。但 RFC7111 有一个补充说明:在 Web 服务器上使用的时候,标准的 MIME Type 应该为 text/csv,像其他文本格式一样,可以有一个可选的编码。

  • 所有行应该拥有相同数量的字段
  • 换行符应统一使用 CRLF
  • 可以有一个可选的标题行。但文件中到底是否包含标题,并无确定的方法来指定,而是留给开发者自己判断。

可以看到,CSV 是一个非常松散的文件规范,都只是应该。在现实世界中,不规范的 CSV 文件才是常态。

比如说,规范要求 CSV 的所有行应该拥有相同数量的字段。但如果真的遇到数量不同的内容,又该怎么处理?有些库会返回长度不定的数组,有些库则会出错,还有些库通过提供更丰富的参数来让开发者自定义行为。这就带来了在实际应用中,哪怕对于相同的 CSV,不同的库也可能彼此表现不同,无法互相兼容。如果要处理来自外部的 CSV 文件,我们也必须小心提防风险。了解 CSV 的具体规范有助于让我们知道在哪些环节可能发生问题,并作出必要的防范。假如对 CSV 的支持是一个非常重要的需求,不妨考虑把它提取出来作为单独的库/服务,对外提供统一的接口,避免让每个程序都实现一套自己的 CSV 读写逻辑,以防止可能出现的兼容性问题。

CSV 和 JSON 格式的缺点

CSV 和 JSON 都是属于文本存储。优点一在于易于人类理解。另一个优点就是直接兼容其他支持 CSV 和 JSON 的数据库。缺点也很明显,存储效率不高,读取效率也会随之降低。

另一个问题在于,有些 CSV 是,没有 type 和 size(metadata),这些信息在后续操作如校验中是很重要的。表是每个关系型数据库最基本的存储对象。

给定一张表,应该怎么存在文件中呢?当然可以用 CSV 格式的文件来存储。确实,CSV 文件中的每一行对应于一条 row,每个 row 的不同 column 用逗号隔开,一个文件对应了一张表。

专业数据库肯定不会选择用 CSV 或 JSON 作为默认存储,但几乎都支持 CSV 和 JSON 数据作为 external table

如果要追求更高的性能,我们可以选择更高效的编码方式把数据以字节流的形式存储在文件中;只要数据库系统自身能够读取这些数据即可。

外部表PostgreSQL 引入外部数据的入口,任何的外部数据源都可以使用该接口把数据引入到数据库中。用户可以像访问表一样读写外部数据源上的数据。很多引擎都支持外部表,比如 StarRocks,hive 等等。

行存储

看一下 CSV 或者 JSON 这样的基于行的存储格式是如何存储数据的。

顾名思义,数据是按行存储在磁盘上的。每一行的数据都存储在一个连续的块中,这使得整体读取和写入比较容易且直观。基于行的存储格式非常适合事务型工作负载,因为记录通常是单条插入、更新或者删除操作。

Parquet仅仅是一种存储格式,它是语言、平台无关的,并且不需要和任何一种数据处理框架绑定,目前能够和Parquet适配的组件包括下面这些,可以看出基本上通常使用的查询引擎和计算框架都已适配,并且可以很方便的将其它序列化工具生成的数据转换成Parquet格式。