如何有力地解析格式错误的CSV?

Sai*_*Sai 15 ruby regex csv fastercsv

我正在处理政府来源(FEC,州选民数据库等)的数据.它的格式不一致,这会以各种令人愉快的方式破坏我的CSV解析器.

它是外部采购和权威的.我必须解析它,我不能重新输入,在输入时验证等.就是这样; 我不控制输入.

属性:

  1. 字段包含格式错误的UTF-8(例如Foo \xAB bar)
  2. 一行的第一个字段指定已知集合中的记录类型.知道记录类型,您知道有多少字段及其各自的数据类型,但直到您知道.
  3. 文件中的任何给定行可能使用带引号的字符串("foo",123,"bar")或不带引号(foo,123,bar).我还没有遇到任何在给定行(即"foo",123,bar)中混合的地方,但它可能在那里.
  4. 字符串可以包括内部换行符,引号和/或逗号字符.
  5. 字符串可以包括逗号分隔的数字.
  6. 数据文件可能非常大(数百万行),因此这仍然需要相当快.

我正在使用Ruby FasterCSV(在1.9中仅称为CSV),但这个问题应该与语言无关.

我的猜测是,解决方案需要使用明确的记录分隔符/引号字符(例如ASCII RS,STX)进行预处理替换.我已经在这里开始了一点但它并不适用于我得到的一切.

如何可靠地处理这种脏数据?

ETA:以下是单个文件中可能包含的简化示例:

"this","is",123,"a","normal","line"
"line","with "an" internal","quote"
"short line","with
an
"internal quote", 1 comma and
linebreaks"
un "quot" ed,text,with,1,2,3,numbers
"quoted","number","series","1,2,3"
"invalid \xAB utf-8"

Jon*_*ray 6

在将CSV文件传递给Ruby的CSV解析器之前,可以将Ruby的文件子类化为处理CSV文件的每一行.例如,以下是我使用此技巧用标准双引号替换非标准反斜杠转义引号""

class MyFile < File
  def gets(*args)
    line = super
    if line != nil
      line.gsub!('\\"','""')  # fix the \" that would otherwise cause a parse error
    end
    line
  end
end

infile = MyFile.open(filename)
incsv = CSV.new(infile)

while row = incsv.shift
  # process each row here
end
Run Code Online (Sandbox Code Playgroud)

原则上你可以进行各种额外的处理,例如UTF-8清理.这种方法的好处是您可以逐行处理文件,因此您无需将其全部加载到内存中或创建中间文件.


Kob*_*obi 2

首先,这是一个相当幼稚的尝试:http://rubular.com/r/gvh3BJaNTc

/"(.*?)"(?=[\r\n,]|$)|([^,"\s].*?)(?=[\r\n,]|$)/m
Run Code Online (Sandbox Code Playgroud)

这里的假设是:

  • 字段可以以引号开头。在这种情况下,它应该以以下引号结尾:
    • 逗号之前
    • 在新行之前(如果它是该行的最后一个字段)
    • 文件末尾之前(如果是最后一行的最后一个字段)
  • 或者,它的第一个字符不是引号,因此它包含字符,直到满足与之前相同的条件为止。

几乎可以满足您的要求,但在这些字段上失败:

1 个逗号和
换行符”

正如TC 在评论中指出的那样,您的文字不明确。我相信您已经知道了,但为了完整起见:

  • "a"- 那是a还是"a"?如何表示想要引号引起来的值?
  • "1","2"- 可能被解析为12或 as 1","2- 两者都是合法的。
  • ,1 \n 2,- 值中的行尾或换行符?您无法判断,特别是这是否应该是其行的最后一个值。
  • 1 \n 2 \n 3- 一个带有换行符的值?两个值(1\n2312\n3)?三个值?

如果检查每行的第一个值,您可能会得到一些线索,正如您所说,它应该告诉您列数及其类型 - 这可以为您提供解析文件时缺少的附加信息(例如,如果您知道该行中应该有另一个字段,那么所有换行符都属于当前值)。即便如此,看起来这里还是存在严重的问题......