如何使用JavaScript解析CSV字符串(数据中包含逗号)?

如何使用JavaScript解析CSV字符串(数据中包含逗号)?,javascript,regex,split,Javascript,Regex,Split,我有以下类型的字符串 var string = "'string, duppi, du', 23, lala" 我想在每个逗号上将字符串分割成一个数组,但仅限于单引号外的逗号 我找不出适合分割的正则表达式 string.split(/,/) 会给我 ["'string", " duppi", " du'", " 23", " lala"] 但结果应该是: ["s

我有以下类型的字符串

var string = "'string, duppi, du', 23, lala"
我想在每个逗号上将字符串分割成一个数组,但仅限于单引号外的逗号

我找不出适合分割的正则表达式

string.split(/,/)
会给我

["'string", " duppi", " du'", " 23", " lala"]
但结果应该是:

["string, duppi, du", "23", "lala"]

有跨浏览器解决方案吗?

如果可以将引号分隔符设置为双引号,则这是的副本

您可以先将所有单引号转换为双引号:

string = string.replace( /'/g, '"' );
…或者您可以编辑该问题中的正则表达式以识别单引号而不是双引号:

// Quoted fields.
"(?:'([^']*(?:''[^']*)*)'|" +
但是,这假设您的问题中不清楚某些标记。根据我对您问题的评论,请澄清标记的各种可能性。

根据,此功能应能:

String.prototype.splitCSV = function(sep) {
  for (var foo = this.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) {
    if (foo[x].replace(/'\s+$/, "'").charAt(foo[x].length - 1) == "'") {
      if ((tl = foo[x].replace(/^\s+'/, "'")).length > 1 && tl.charAt(0) == "'") {
        foo[x] = foo[x].replace(/^\s*'|'\s*$/g, '').replace(/''/g, "'");
      } else if (x) {
        foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep));
      } else foo = foo.shift().split(sep).concat(foo);
    } else foo[x].replace(/''/g, "'");
  } return foo;
};
你可以这样称呼它:

var string = "'string, duppi, du', 23, lala";
var parsed = string.splitCSV();
alert(parsed.join("|"));

这是一种工作方式,但看起来有些元素前面有空格。

我的答案假定您的输入是来自web源的代码/内容的反映,其中单引号和双引号字符是完全可互换的,只要它们作为非转义匹配集出现

您不能为此使用正则表达式。实际上,您必须编写一个微型解析器来分析要拆分的字符串。为了回答这个问题,我将把字符串的引用部分称为子字符串。你需要特别地穿过绳子。考虑以下情况:

var a = "some sample string with \"double quotes\" and 'single quotes' and some craziness like this: \\\" or \\'",
    b = "sample of code from JavaScript with a regex containing a comma /\,/ that should probably be ignored.";
在这种情况下,通过简单地分析字符模式的输入,您完全不知道子字符串从何处开始或结束。相反,您必须编写逻辑来决定引号字符是否用作引号字符、本身是否未引号以及引号字符是否在转义之后

我不打算为您编写如此复杂的代码,但您可以看看我最近编写的具有您需要的模式的代码。这段代码与逗号无关,但在编写自己的代码时,它是一个足够有效的微解析器。查看以下应用程序的asifix功能:

免责声明 2014-12-01更新:以下答案仅适用于一种非常特定的CSV格式。正如正确指出的,此解决方案不符合RFC 4180对CSV的定义,也不符合Microsoft Excel格式。此解决方案简单地演示了如何解析一行(非标准)CSV输入,其中包含多种字符串类型,其中字符串可能包含转义引号和逗号

非标准CSV解决方案 如前所述,如果希望正确处理可能包含转义字符的带引号的字符串,则确实需要从头到尾解析字符串。此外,OP没有明确定义什么是“CSV字符串”。首先,我们必须定义构成有效CSV字符串的内容及其各个值

给定:“CSV字符串”定义 在本讨论中,“CSV字符串”由零个或多个值组成,其中多个值用逗号分隔。每个值可能包括:

  • 双引号字符串(可能包含未转换的单引号)
  • 单引号字符串(可能包含未转换的双引号)
  • 不带引号的字符串(不能包含引号、逗号或反斜杠)
  • 空值。(全空白值视为空。)
  • 规则/说明:

    • 引用的值可能包含逗号
    • 引用的值可能包含转义的任何内容,例如
      “太酷了”
    • 包含引号、逗号或反斜杠的值必须加引号
    • 必须引用包含前导或尾随空格的值
    • 反斜杠将从单引号中的所有:
      \'
      中删除
    • 反斜杠将从双引号中的所有:
      \“
      中删除
    • 不带引号的字符串将修剪任何前导空格和尾随空格
    • 逗号分隔符可能有相邻的空格(忽略)
    查找: 一个JavaScript函数,用于将有效的CSV字符串(如上所述)转换为字符串值数组

    解决方案: 此解决方案使用的正则表达式很复杂。和(IMHO)所有非平凡的正则表达式都应该以自由间距模式呈现,并带有大量注释和缩进。不幸的是,JavaScript不允许自由间距模式。因此,此解决方案实现的正则表达式首先以本机正则表达式语法呈现(使用Python方便的
    r'''.'''.''.
    raw多行字符串语法表示)

    首先,这里是一个正则表达式,用于验证CVS字符串是否满足上述要求:

    验证“CSV字符串”的正则表达式:
    re_valid=r”“”
    #验证具有单引号、双引号或无引号值的CSV字符串。
    ^#锚定至管柱起点。
    \s*#在值之前允许空白。
    (?:#为价值选择分组。
    “[^'\]*(?:\\[\S\S][^'\]*)*”\35;单引号字符串,
    |“[^”\]*(?:\\[\S\S][^”\\]*)*”或双引号字符串,
    |[^,“\s\\]*(?:\s+[^,“\s\\]+)*”或非逗号、非引号的内容。
    )#价值选择的最终群体。
    \s*#允许值后面有空格。
    (?:#零个或更多附加值
    ,#用逗号分隔的值。
    \s*#在值之前允许空白。
    (?:#为价值选择分组。
    “[^'\]*(?:\\[\S\S][^'\]*)*”\35;单引号字符串,
    |“[^”\]*(?:\\[\S\S][^”\\]*)*”或双引号字符串,
    |[^,“\s\\]*(?:\s+[^,“\s\\]+)*”或非逗号、非引号的内容。
    )#价值选择的最终群体。
    \*
    
    start
      = [\n\r]* first:line rest:([\n\r]+ data:line { return data; })* [\n\r]* { rest.unshift(first); return rest; }
    
    line
      = first:field rest:("," text:field { return text; })*
        & { return !!first || rest.length; } // ignore blank lines
        { rest.unshift(first); return rest; }
    
    field
      = '"' text:char* '"' { return text.join(''); }
      / text:[^\n\r,]* { return text.join(''); }
    
    char
      = '"' '"' { return '"'; }
      / [^"]
    
    <?php
        session_start(); // Optional
        header("content-type: text/xml");
        header("charset=UTF-8");
        // Set the delimiter and the End of Line character of your CSV content:
        echo json_encode(array_map('str_getcsv', str_getcsv($_POST["csv"], "\n")));
    ?>
    
    function csvToArray(csv) {
        var oXhr = new XMLHttpRequest;
        oXhr.addEventListener("readystatechange",
            function () {
                if (this.readyState == 4 && this.status == 200) {
                    console.log(this.responseText);
                    console.log(JSON.parse(this.responseText));
                }
            }
        );
        oXhr.open("POST","path/to/csv.php",true);
        oXhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
        oXhr.send("csv=" + encodeURIComponent(csv));
    }
    
    stringLine = stringLine.replace(/\0/g, "" );
    
    "some ""value"" that is on xlsx file",123
    
    function parse(text) {
      const csvExp = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|"([^""]*(?:"[\S\s][^""]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g;
    
      const values = [];
    
      text.replace(csvExp, (m0, m1, m2, m3, m4) => {
        if (m1 !== undefined) {
          values.push(m1.replace(/\\'/g, "'"));
        }
        else if (m2 !== undefined) {
          values.push(m2.replace(/\\"/g, '"'));
        }
        else if (m3 !== undefined) {
          values.push(m3.replace(/""/g, '"'));
        }
        else if (m4 !== undefined) {
          values.push(m4);
        }
        return '';
      });
    
      if (/,\s*$/.test(text)) {
        values.push('');
      }
    
      return values;
    }
    
    String.prototype.splitCSV = function() {
            var matches = this.match(/(\s*"[^"]+"\s*|\s*[^,]+|,)(?=,|$)/g);
            for (var n = 0; n < matches.length; ++n) {
                matches[n] = matches[n].trim();
                if (matches[n] == ',') matches[n] = '';
            }
            if (this[0] == ',') matches.unshift("");
            return matches;
    }
    
    var string = ',"string, duppi, du" , 23 ,,, "string, duppi, du",dup,"", , lala';
    var parsed = string.splitCSV();
    alert(parsed.join('|'));
    
    address,pincode
    foo,baar , 123456
    
    [{
     address: 'foo',
     pincode: 'baar',
     'field3': '123456'
    }]
    
    function csv2arr(str: string) {
        let line = ["",];
        const ret = [line,];
        let quote = false;
    
        for (let i = 0; i < str.length; i++) {
            const cur = str[i];
            const next = str[i + 1];
    
            if (!quote) {
                const cellIsEmpty = line[line.length - 1].length === 0;
                if (cur === '"' && cellIsEmpty) quote = true;
                else if (cur === ",") line.push("");
                else if (cur === "\r" && next === "\n") { line = ["",]; ret.push(line); i++; }
                else if (cur === "\n" || cur === "\r") { line = ["",]; ret.push(line); }
                else line[line.length - 1] += cur;
            } else {
                if (cur === '"' && next === '"') { line[line.length - 1] += cur; i++; }
                else if (cur === '"') quote = false;
                else line[line.length - 1] += cur;
            }
        }
        return ret;
    }
    
    function parseCsv(data, fieldSep, newLine) {
        fieldSep = fieldSep || ',';
        newLine = newLine || '\n';
        var nSep = '\x1D';
        var qSep = '\x1E';
        var cSep = '\x1F';
        var nSepRe = new RegExp(nSep, 'g');
        var qSepRe = new RegExp(qSep, 'g');
        var cSepRe = new RegExp(cSep, 'g');
        var fieldRe = new RegExp('(?<=(^|[' + fieldSep + '\\n]))"(|[\\s\\S]+?(?<![^"]"))"(?=($|[' + fieldSep + '\\n]))', 'g');
        var grid = [];
        data.replace(/\r/g, '').replace(/\n+$/, '').replace(fieldRe, function(match, p1, p2) {
            return p2.replace(/\n/g, nSep).replace(/""/g, qSep).replace(/,/g, cSep);
        }).split(/\n/).forEach(function(line) {
            var row = line.split(fieldSep).map(function(cell) {
                return cell.replace(nSepRe, newLine).replace(qSepRe, '"').replace(cSepRe, ',');
            });
            grid.push(row);
        });
        return grid;
    }
    
    const csv = 'A1,B1,C1\n"A ""2""","B, 2","C\n2"';
    const separator = ',';      // field separator, default: ','
    const newline = ' <br /> '; // newline representation in case a field contains newlines, default: '\n' 
    var grid = parseCsv(csv, separator, newline);
    // expected: [ [ 'A1', 'B1', 'C1' ], [ 'A "2"', 'B, 2', 'C <br /> 2' ] ]
    
    function csvRowToArray(row, delimiter = ',', quoteChar = '"'){
        let nStart = 0, nEnd = 0, a=[], nRowLen=row.length, bQuotedValue;
        while (nStart <= nRowLen) {
            bQuotedValue = (row.charAt(nStart) === quoteChar);
            if (bQuotedValue) {
                nStart++;
                nEnd = row.indexOf(quoteChar + delimiter, nStart)
            } else {
                nEnd = row.indexOf(delimiter, nStart)
            }
            if (nEnd < 0) nEnd = nRowLen;
            a.push(row.substring(nStart,nEnd));
            nStart = nEnd + delimiter.length + (bQuotedValue ? 1 : 0)
        }
        return a;
    }