复杂的CSV到数组

时间:2011-10-28 08:11:59

标签: javascript arrays csv

我有一个由MSExcel创建的CSV文件,其中包含元素内的随机回车。每行由最终CSV中的回车符分隔(我不知道它是否特定于MSExcel)。

假设我有10列和100行,最后一列的某些元素本身具有回车符(因此在最终输出中用双引号引用),有些则没有。

无论如何我可以正确分割这100行吗?

即使我已经准备好牺牲那些没有回车并且在最后手动添加一个,使所有10号,20号,30号元素以“+ \ n结束,JS似乎无法识别“\”\ n“作为有效的语法。

编辑:这种操作最好用php +数据库完成吗?如果是的话,我应该从哪里开始?

3 个答案:

答案 0 :(得分:4)

如果您正在考虑使用.split()函数(对每个逗号进行简单拆分)来解析csv数据,那么您会以错误的方式思考它。典型的通用Split()函数对于csv是错误的,因为有各种边缘情况可以使它们绊倒,并且因为性能不好。

解析csv数据的正确方法是使用专用的状态机(而不是由正则表达式定义的状态机)。好消息是您不必自己编写该状态机。坏消息是你必须要小心,因为谷歌充斥着 javascript csv解析器...例如,我做的快速搜索的当前第一个结果是这里托管的一个坏例子在Stack Overflow本身。该示例过于依赖于正则表达式,后者使用嵌套的引用文本很难。可以构建一个可以正常运行的正则表达式,但它也容易出错,难以维护,表达式的性能通常不会那么好(因为表达式需要进行反向跟踪)。使用正则表达式解析CSV数据几乎与parsing html with regular expressions一样糟糕。

这是我在Google中看到的第一个好的(基于状态机的)示例:

  

http://yawgb.blogspot.com/2009/03/parsing-comma-separated-values-in.html

这个特殊的解析器假定逗号作为分隔符(与标签,分号或管道相对),假定双重转义引号(引用内引号)文本字段由它们自行转义,如下所示:"")。如果这与您的数据匹配,那么这个例子可能会很好 - 但同样,这是一个快速搜索;我自己看得太近了。否则,请继续使用谷歌或使用此示例编写自己的。

从那里开始,我很好奇,因为它听起来有点像你可能使用Excel或平面csv文件作为网站的主要数据存储。这也是一个非常糟糕的主意。当您开始让几个人几乎同时使用该页面时,Excel和平面文件都会出现巨大的并发问题。表演也可能是一个问题,尽管我犹豫不决这一点;最好说性能将是你的表现,但平面文件很容易出错。

答案 1 :(得分:1)

这比你想象的要难......

好吧,我的第一篇文章被一个mod方便地删除,因为我发布了一个外部链接到我的OSS CSV解析器项目。

所以...我将发布完整的ND-FSM(非确定性有限状态机)分线器,它是处理包含字符串的值所必需的。

我们走了:

splitLines: function(csv, delimiter) {
  var state = 0;
  var value = "";
  var line = "";
  var lines = [];
  function endOfRow() {
    lines.push(value);
    value = "";
    state = 0;
  };
  csv.replace(/(\"|,|\n|\r|[^\",\r\n]+)/gm, function (m0){
    switch (state) {
      // the start of an entry/value
      case 0:
        if (m0 === "\"") {
          state = 1;
        } else if (m0 === "\n") {
          endOfRow();
        } else if (/^\r$/.test(m0)) {
          // carriage returns are ignored
        } else {
          value += m0;
          state = 3;
        }
        break;
      // delimited input  
      case 1:
        if (m0 === "\"") {
          state = 2;
        } else {
          value += m0;
          state = 1;
        }
        break;
      // delimiter found in delimited input
      case 2:
        // is the delimiter escaped?
        if (m0 === "\"" && value.substr(value.length - 1) === "\"") {
          value += m0;
          state = 1;
        } else if (m0 === ",") {
          value += m0;
          state = 0;
        } else if (m0 === "\n") {
          endOfRow();
        } else if (m0 === "\r") {
          // Ignore
        } else {
          throw new Error("Illegal state");
        }
        break;
      // un-delimited input
      case 3:
        if (m0 === ",") {
          value += m0;
          state = 0;
        } else if (m0 === "\"") {
          throw new Error("Unquoted delimiter found");
        } else if (m0 === "\n") {
          endOfRow();
        } else if (m0 === "\r") {
          // Ignore
        } else {
          throw new Error("Illegal data");
        }
          break;
      default:
        throw new Error("Unknown state");
    }
    return "";
  });
  if (state != 0) {
    endOfRow();
  }
  return lines;
}

如果你理解复杂性理论,这里是映射:

NDFSM Diagram

<强>国:

  • 0:新值/条目的开始
  • 1:引用(即用双引号括起来)
  • 2:遇到双重报价
  • 3:未引用

注意:这仅处理行分割部分。其余的由一个单独的(并且非常复杂的)正则表达式例程处理。

我不能因为提出使用词法分析器的想法而受到赞扬。这是另一个我未经许可公开命名的开发者。

如果您希望能够处理任何 RFC 4180兼容的CSV数据,而无需亲自实施,请查看我的个人资料中提到的项目。祝你好运......

答案 2 :(得分:0)

我可能会建议在PHP中执行此操作,除非您确实需要在JS中执行此操作;我很确定无论如何都有一个用于处理CSV文件的PHP库。