看到对 csv 的讨论,顺便记录一下 CSV 中一些有趣的知识点。

背景

传统认知中,CSV 就是简单地使用逗号分割,然后一行就代表一行数据。就这么简单,但很自然就会想到,如果数据本身有逗号怎么办?数据本身有换行怎么办?

所以,本文简单实用 Numbers 来测试一下数据,并导出真正的保存其中的格式,来看看解决实现上述中的问题。

换行

在 Numbers 中编辑一个表格,然后查看源码:

image

$ xxd test.csv
00000000: 612c 620d 0a41 2c22 410a 4222 0d0a 4132  a,b..A,"A.B"..A2
00000010: 2c22 4132 0a42 3222 0d0a                 ,"A2.B2"..

可以看到 0a 0d 就是换行,也就是 new linecarriage return。按 0a 0d 拆解后,就是如下源码

61 2c 62 0d 0a                        a,b..
41 2c 22 410a 42 22 0d0a              A,"A.B"..
41 32 2c 22 41 32 0a 42 32 22 0d 0a   A2,"A2.B2"..

有换行的单元格,会使用 0x22 双引号包裹,其中的单元格的换行,则仅使用了 0x0a 来做换行的声明。

逗号

编辑一个含有逗号的表格,并导出,查看源码如下:

00000000: 412c 420d 0a41 2c22 2c22 0d0a 2222 222c  A,B..A,","

可以看到原有逗号的单元格 22 2c22 ,会被双引号包裹,从而解决特殊字符的问题。

引号

从上面看出,逗号和换行等特殊字符,都会被双引号包裹,那自然就会引申出,如果单元格有双引号,又该怎么办呢?

编辑引号,并导出:

00000000: 2222 2222 2c22 2222 2222 220d 0a         """",""""""..

其实原理也很简单,如果有双引号,则用「两个双引号」来做转义,即 ""。直到遇到下一个「单个」双引号(第一个单个双引号表示开始),则表示结束。

放到一起

编辑如下表格,并导出:

image

$ xxd test.csv
00000000: 412c 420d 0a41 2c22 2c22 0d0a 2222 222c  A,B..A,","..""",
00000010: 2222 222c 2222 222c 2222 2222 2c22 2222  """,""","""","""
00000020: 0d0a 2222 222c 2222 0a22 222c 2222 222c  ..""",""."",""",
00000030: 2222 222c 2222 2222 2c22 220a 2222 2c22  ""","""",""."","
00000040: 2222 222c 2222 220d 0a                   ""","""..
  • 2c 逗号,用于切分单元格
  • 0d 0a 表示数据换行
  • 有逗号、引号、换行等特殊字符,则会被双引号包裹,即 22
    • 任何双引号开头的单元格,会持续识别为单元格的内容,直到遇到下一个「单个」双引号(第一个单个双引号表示开始)为止,(中间的每两个双引号都会被转义为 "
  • 0a 表示单元格内的换行
  • 22 22 表示单元格中双引号转义,即表示单元格中有「一个」双引号