CSV文件读写的严格定义

6

我使用C代码编写了自己的CSV读写程序,用于将记录存储在ODBC数据库中的字符列中。不幸的是,我发现我的实现有许多边缘情况,而且我得出结论,我的问题在于我没有严格定义CSV的规则。我已经阅读了RFC4180,但它似乎不完整,也不能解决歧义。

例如,""应该被视为空标记还是双引号?引号从外到内匹配还是从左到右匹配?对于具有不匹配单引号的输入字符串该怎么处理?当我有嵌套标记时,真正的麻烦开始了,这会使转义引号字符加倍。

我真正需要的是一个明确的CSV标准,以便我可以在代码中实现它。每次我觉得我已经解决了每个角落的问题,我又会发现另一个问题。我相信这个问题已经被比我更聪明的人反复思考和解决了许多次,有没有人编写过一个严格的CSV定义,可以让我在代码中实现?我意识到C语言在这里不是理想的语言,但在这个阶段我选择不了编译器;我也无法使用第三方库(除非它与C-90编译)。 Boost不是选项,因为我的编译器不支持C ++。我曾考虑放弃CSV而改用XML,但对于将几个标记存储在256字符数据库记录中来说,这似乎过于复杂了。有人制定了一个明确的CSV规范吗?


1
这里建议使用的库符合ANSI C89标准。 - Nobilis
3个回答

1

没有标准(请参阅维基百科的文章,特别是http://en.wikipedia.org/wiki/Comma-separated_values#Lack_of_a_standard),因此为了使用CSV,您需要遵循在生成时保守,在接受时开放的一般原则。具体来说:

  • 不要为空字段使用引号。只需写入一个空字段(两个相邻分隔符或行首/行尾位置的分隔符)。
  • 引用任何包含引号、逗号或换行符的字段。

1
我所看到的复杂性的一个例子是当你有像这样的输入字符串: "5,5"" pipe","6"" pipe" 很明显,你解释这个字符串的方式完全取决于你应用规则的顺序。例如,由于它以引号开头和结尾,整个字符串是文字吗?还是我首先用单引号替换双引号,然后将单引号解释为限定符,并将第一个标记读为: 5,5"管道 - Piers
@Piers "5,5"" pipe" 是一个字符串字面量。您不能使用分词函数来读取这个数据 - 您必须从左到右解析它,当您这样做时,就清楚了如何读取它。"5,5"" pipe","6"" pipe" 表示 2 个字段值。逗号在 5,5 中不是字段分隔符,因为当您到达逗号字符时,您已经在引用的字段内部 - 没有必要交换双引号。为什么您说这很复杂?我可能漏掉了什么 - 请阅读下面的答案,其中包含所有规则(实际上是来自 RFC 的规则)。 - xxbbcc
@xxbbcc - 你说这不复杂,但你刚刚声明了一堆规则要按照特定顺序应用:首先将""视为空字符串(或不是),然后查找前导引号,然后替换2个双引号,最后匹配结尾未转义的引号。如果更改这些顺序,则解释和结果字符串会有所不同。RFC在读/写CSV时没有指定应用这些规则的顺序(也没有说明如何解释""的歧义),这就是我所说的“严格定义”。我不确定你所说的不进行标记化是什么意思,你是指没有使用strtok()吗? - Piers
@Piers,我认为您误解了记录(和字段)的处理方式。我没有制定任何规则 - 我只是简单地从RFC翻译了规则。在CSV中处理单个记录的方法是逐个字符从左到右进行解析。这可以使用非常简单的状态机来完成 - 这是一个微不足道的任务。但是,您无法使用像strtok()split(),正则表达式或其变量之类的函数。我真的不明白RFC怎么可能更清晰。如果您想聊天,请告诉我 - 我不想再劫持 @R.. 的回答 :) - xxbbcc
@Piers 聊天链接:http://chat.stackoverflow.com/rooms/info/31366/csv-record-parsing?tab=general如果你想快速聊一下,我会在这里待一段时间。 - xxbbcc
显示剩余4条评论

0

寻找你信任的最权威的 CSV 库并阅读源代码。CSV 并不是那么复杂,你可以通过全面阅读一个实现源码来理解它的规则。我一直很喜欢 Java 的 opencsv。Perl 的在这里,等等。


0
根据RFC 4180,应该从左到右解析字段以正确解释双引号。在某些情况下,""是一个转义的双引号(当在引用字段内部时),否则它要么是一个空字符串,要么是两个双引号(当在非空字段值内部时)。
例如,考虑一个有4条记录(1列)的文件:
"field""value" CRLF
"" CRLF
field""value CRLF
"field value" extra CRLF
  1. "field""value" - 应该被解读为 field"value
  2. "" - 应该被解读为空字符串
  3. field""value - 应该被解读为 field""value
  4. "field value" extra - 可以解读为 field value extra 或者你可以拒绝它

第四个记录实际上是一个无效的字段,所以你可以接受它或者拒绝它。

当你开始读取一个字段时,你需要检查读取的第一个字符是否为双引号。如果第一个字符是双引号,则该字段值被引用,你需要读取直到找到一个未转义的闭合双引号。在这种情况下,你可以忽略换行符和逗号字符,因为该字段被引用-只有当你遇到一个闭合双引号时才会结束。

如果第一个字符不是双引号,则字段值中的所有双引号应视为文字双引号。在这种情况下,当你遇到逗号或换行符时,你就达到了字段的结尾。

基于这个,我建议在编写记录时始终引用所有字段,并编写一个适当的解析器来解析数据。 这样,您可以将任何数据存储在CSV文件中(甚至是带有嵌入式引号的多行文本),并且您的格式将清晰明了。 当阅读CSV文件时,如果无法正确解析,则应失败所有文件。 如果这是一个数据库,用户除非知道自己在做什么,否则不会手动操作记录。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接