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

128

我有以下类型的字符串

var string = "'string, duppi, du', 23, lala"
我想将字符串按逗号拆分为数组,但仅限于单引号之外的逗号。我无法找到适合拆分的正则表达式...
string.split(/,/)

会给我

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

但结果应该是:

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

有没有跨浏览器的解决方案?


它总是单引号吗?在引用的字符串中是否有单引号?如果有,如何转义(反斜杠、双倍)? - Phrogz
一个值可以是双引号字符串吗? - ridgerunner
你需要处理空值吗?例如:var string = "'du',23,,,lala" - ridgerunner
这里有一个JavaScript函数,用于解析CSV数据并考虑到引号内的逗号。 - curran
1
Papa Parse做得很好。使用JavaScript和Papa Parse解析本地CSV文件:http://www.joyofdata.de/blog/parsing-local-csv-file-with-javascript-papa-parse/ - Raffael
显示剩余2条评论
21个回答

256

免责声明

2014-12-01 更新:下面的答案仅适用于一种非常特定的 CSV 格式。正如 DG 在评论中正确指出的那样,此解决方案不符合 RFC 4180 定义的 CSV 格式,也不符合 MS Excel 格式。此解决方案仅演示了如何解析包含字符串类型混合的一个(非标准)CSV 输入行,其中字符串可能包含转义引号和逗号。

非标准 CSV 解决方案

正如 austincheney 正确指出的那样,如果您希望正确处理可能包含转义字符的引号字符串,则确实需要从头到尾解析字符串。此外,OP 没有清楚地定义“CSV 字符串”是什么。首先,我们必须定义什么构成有效 CSV 字符串及其各个值。

给定:“CSV 字符串”定义

为了讨论目的,一个“CSV 字符串”由零个或多个值组成,其中多个值由逗号分隔。每个值可以包含:

  1. 双引号字符串(可能包含未转义的单引号)。
  2. 单引号字符串(可能包含未转义的双引号)。
  3. 非引用字符串(不得包含引号、逗号或反斜杠)。
  4. 空值。(全为空格值被视为为空)

规则/注释:

  • 带引号的值可以包含逗号。
  • 带引号的值可能包含转义字符,例如:'that\'s cool'
  • 包含引号、逗号或反斜杠的值必须用引号括起来。
  • 包含前导或尾随空格的值必须用引号括起来。
  • 在单引号值中删除所有: \' 的反斜杠。
  • 在双引号值中删除所有:\" 的反斜杠。
  • 非引用字符串会去除所有前导和尾随空格。
  • 逗号分隔符可能具有相邻的空格(将被忽略)。

查找:

一种JavaScript函数,可将有效的CSV字符串(如上所定义)转换为字符串值的数组。

解决方案:

这个解决方案使用的正则表达式非常复杂。而且(在我看来)所有的非平凡正则表达式都应该以自由空间模式呈现,包含大量注释和缩进。不幸的是,JavaScript 不允许自由空间模式。因此,此解决方案实现的正则表达式首先以本地正则表达式语法呈现(使用 Python 的便捷的 r'''...''' 原始多行字符串语法表示)。

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

用于验证“CSV 字符串”的正则表达式:

re_valid = r"""
# Validate a CSV string having single, double or un-quoted values.
^                                   # Anchor to start of string.
\s*                                 # Allow whitespace before value.
(?:                                 # Group for value alternatives.
  '[^'\\]*(?:\\[\S\s][^'\\]*)*'     # Either Single quoted string,
| "[^"\\]*(?:\\[\S\s][^"\\]*)*"     # or Double quoted string,
| [^,'"\s\\]*(?:\s+[^,'"\s\\]+)*    # or Non-comma, non-quote stuff.
)                                   # End group of value alternatives.
\s*                                 # Allow whitespace after value.
(?:                                 # Zero or more additional values
  ,                                 # Values separated by a comma.
  \s*                               # Allow whitespace before value.
  (?:                               # Group for value alternatives.
    '[^'\\]*(?:\\[\S\s][^'\\]*)*'   # Either Single quoted string,
  | "[^"\\]*(?:\\[\S\s][^"\\]*)*"   # or Double quoted string,
  | [^,'"\s\\]*(?:\s+[^,'"\s\\]+)*  # or Non-comma, non-quote stuff.
  )                                 # End group of value alternatives.
  \s*                               # Allow whitespace after value.
)*                                  # Zero or more additional values
$                                   # Anchor to end of string.
"""

如果一个字符串与上述正则表达式匹配,则该字符串是一个有效的 CSV 字符串(根据先前规定的规则),并且可以使用以下正则表达式进行解析。然后使用以下正则表达式来匹配 CSV 字符串中的一个值。它会一直应用,直到找不到更多匹配项(并且所有值都已解析)。

从有效的 CSV 字符串中解析一个值的正则表达式:

re_value = r"""
# Match one value in valid CSV string.
(?!\s*$)                            # Don't match empty last value.
\s*                                 # Strip whitespace before value.
(?:                                 # Group for value alternatives.
  '([^'\\]*(?:\\[\S\s][^'\\]*)*)'   # Either $1: Single quoted string,
| "([^"\\]*(?:\\[\S\s][^"\\]*)*)"   # or $2: Double quoted string,
| ([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)  # or $3: Non-comma, non-quote stuff.
)                                   # End group of value alternatives.
\s*                                 # Strip whitespace after value.
(?:,|$)                             # Field ends on comma or EOS.
"""

请注意,这个正则表达式不匹配一个特殊情况的值 - 即在该值为空时的最后一个值。紧随其后的 JavaScript 函数会测试并处理这个特殊的“空的最后一个值”情况。
示例输入和输出:
在下面的示例中,花括号用于界定 {结果字符串}。(这是为了可视化前导/尾随空格和零长度字符串。)

    // Return array of string values, or NULL if CSV string not well formed.
    function CSVtoArray(text) {
        var re_valid = /^\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*(?:,\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*)*$/;
        var re_value = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g;
        // Return NULL if input string is not well formed CSV string.
        if (!re_valid.test(text)) return null;
        var a = [];                     // Initialize array to receive values.
        text.replace(re_value, // "Walk" the string using replace with callback.
            function(m0, m1, m2, m3) {
                // Remove backslash from \' in single quoted values.
                if      (m1 !== undefined) a.push(m1.replace(/\\'/g, "'"));
                // Remove backslash from \" in double quoted values.
                else if (m2 !== undefined) a.push(m2.replace(/\\"/g, '"'));
                else if (m3 !== undefined) a.push(m3);
                return ''; // Return empty string.
            });
        // Handle special case of empty last value.
        if (/,\s*$/.test(text)) a.push('');
        return a;
    };

    console.log('Test 1: Test string from original question.');
    console.log(CSVtoArray("'string, duppi, du', 23, lala"));

    console.log('Test 2: Empty CSV string.');
    console.log(CSVtoArray(""));

    console.log('Test 3: CSV string with two empty values.');
    console.log(CSVtoArray(","));

    console.log('Test 4: Double quoted CSV string having single quoted values.');
    console.log(CSVtoArray("'one','two with escaped \' single quote', 'three, with, commas'"));

    console.log('Test 5: Single quoted CSV string having double quoted values.');
    console.log(CSVtoArray('"one","two with escaped \" double quote", "three, with, commas"'));

    console.log('Test 6: CSV string with whitespace in and around empty and non-empty values.');
    console.log(CSVtoArray("   one  ,  'two'  ,  , ' four' ,, 'six ', ' seven ' ,  "));

    console.log('Test 7: Not valid');
    console.log(CSVtoArray("one, that's me!, escaped \, comma"));

附加说明:

此解决方案要求CSV字符串是“有效的”。例如,未引用的值不能包含反斜杠或引号,例如以下CSV字符串是无效的:

var invalid1 = "one, that's me!, escaped \, comma"

这并不是真正的限制,因为任何子字符串都可以表示为单引号或双引号的值。请注意,此解决方案仅代表“逗号分隔值”的一种可能定义。

编辑:2014-05-19:添加免责声明。 编辑:2014-12-01:将免责声明移到顶部。


1
@Evan Plaice,当然可以使用任何分隔符。只需将我的正则表达式中的每个逗号替换为所选的分隔符(但分隔符不能是空格)。 - ridgerunner
@Alan Moore - 当然,你是正确的。尽管主要问题特别假定输入只有一行(在这种情况下,未引用的值中不应该有换行符),但最好还是按照你的建议进行更改。不幸的是,我现在没有时间自己进行更改。 - ridgerunner
@Zach Smith - 容易使其动态化吗?不是的。您需要在两个正则表达式中将所有逗号实例替换为所需的分隔符(在re_valid中出现5次,在re_value中出现3次)。话虽如此,可以通过使用RegExp()构造函数而不是/RegExp字面量/来构建正则表达式来完成这项工作。 - ridgerunner
我已经找到了库csv-parse/lib/sync来实现我想要的功能(即拆分格式为CSV的行)。 - Zach Smith
1
我认为使用正则表达式解析csv数据并不是最好的方法。我尝试过,但我的一些数据文件被解析为 null,因为它们使用 " 作为引号,而某些值包含单引号 '。它还没有考虑到换行和行。我知道我可以扩展代码以包括这些用例,但那将导致一些笨重的大表达式,这变得极其耗时。此页面上@Bachor的答案不使用正则表达式,更易于维护和速度快两倍。 - BdR
显示剩余5条评论

78

RFC 4180解决方案

这并不能解决问题中的字符串,因为它的格式不符合RFC 4180;可接受的编码方式是用双引号来转义双引号。下面的解决方案可以正确地处理从Google电子表格下载的CSV文件。

更新(3/2017)

解析单行将是错误的。根据RFC 4180,字段可能包含CRLF,这将导致任何行读取器中断CSV文件。下面是一个更新的版本,用于解析CSV字符串:

'use strict';

function csvToArray(text) {
    let p = '', row = [''], ret = [row], i = 0, r = 0, s = !0, l;
    for (l of text) {
        if ('"' === l) {
            if (s && l === p) row[i] += l;
            s = !s;
        } else if (',' === l && s) l = row[++i] = '';
        else if ('\n' === l && s) {
            if ('\r' === p) row[i] = row[i].slice(0, -1);
            row = ret[++r] = [l = '']; i = 0;
        } else row[i] += l;
        p = l;
    }
    return ret;
};

let test = '"one","two with escaped """" double quotes""","three, with, commas",four with no quotes,"five with CRLF\r\n"\r\n"2nd line one","two with escaped """" double quotes""","three, with, commas",four with no quotes,"five with CRLF\r\n"';
console.log(csvToArray(test));

旧答案

(单行解决方案)

function CSVtoArray(text) {
    let ret = [''], i = 0, p = '', s = true;
    for (let l in text) {
        l = text[l];
        if ('"' === l) {
            s = !s;
            if ('"' === p) {
                ret[i] += '"';
                l = '-';
            } else if ('' === p)
                l = '-';
        } else if (s && ',' === l)
            l = ret[++i] = '';
        else
            ret[i] += l;
        p = l;
    }
    return ret;
}
let test = '"one","two with escaped """" double quotes""","three, with, commas",four with no quotes,five for fun';
console.log(CSVtoArray(test));

为了好玩,这里是如何从数组创建CSV的方法:

function arrayToCSV(row) {
    for (let i in row) {
        row[i] = row[i].replace(/"/g, '""');
    }
    return '"' + row.join('","') + '"';
}

let row = [
  "one",
  "two with escaped \" double quote",
  "three, with, commas",
  "four with no quotes (now has)",
  "five for fun"
];
let text = arrayToCSV(row);
console.log(text);


4
这个对我起了作用,而不是另一个。 - WtFudgE
支持一个 CSV 文件,其中第一行是列名怎么样? - vsync

12

我喜欢FakeRainBrigand的答案,但它存在一些问题:无法处理引号和逗号之间的空格,也不支持两个连续的逗号。我尝试编辑他的答案,但是我的修改被审核人员拒绝了,他们显然没有理解我的代码。这是我对FakeRainBrigand代码的版本。 还有一个演示:http://jsfiddle.net/xTezm/46/

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('|'));

谢谢,它对我部分地起作用。 有人知道如何去掉这个过滤器后留下的额外逗号吗?这里有一个文本“text,text,,text,,,text”,结果我仍然有最后一个逗号作为值,所以我有三个逗号作为解析值。 - m.zhelieznov
@m.zhelieznov 你可以尝试通过另一个正则表达式来运行它,例如:text = text.replace(/,+/,',') - HammerNL
谢谢,我做了类似于你建议的事情。我正在使用下面的脚本将.xml转换为.xlsx:https://dev59.com/questions/danka4cB1Zd3GeqPU92x#51094040,然后我只是检查单元格的值是否不等于逗号,然后添加它。 - m.zhelieznov

7

人们似乎反对在这种情况下使用正则表达式。为什么?

(\s*'[^']+'|\s*[^,]+)(?=,|$)

这是代码。我还制作了一个 fiddle

String.prototype.splitCSV = function(sep) {
  var regex = /(\s*'[^']+'|\s*[^,]+)(?=,|$)/g;
  return matches = this.match(regex);    
}

var string = "'string, duppi, du', 23, 'string, duppi, du', lala";

console.log( string.splitCSV()  );
.as-console-wrapper { max-height: 100% !important; top: 0; }


4
嗯,你的正则表达式确实有些问题:它不能处理引号和逗号之间的空格,并且不支持连续两个逗号。我已经更新了你的答案代码来解决这两个问题,并创建了一个新的 fiddle:http://jsfiddle.net/xTezm/43/。 - HammerNL
1
由于某种原因,我的代码编辑被拒绝了,因为它会“偏离帖子的原意”。非常奇怪!?我只是拿了你的代码并修复了其中的两个问题。这如何改变帖子的意图呢?无论如何...我已经在这个问题下添加了一个新答案。 - HammerNL
你的回答中提出了一个很好的问题,@FakeRainBrigand。我本人非常赞成正则表达式的使用,但因此也承认它不适合这个工作。 - niry
3
@niry 我的代码写得太糟糕了。我保证在过去的6年里我已经变得更好了 :-p - Brigand
你对csv逗号后可能存在的空格不做处理吗(如示例数据中)? - Mister Jojo

6

我有一个非常具体的用例,需要将Google Sheets中的单元格复制到我的Web应用程序中。这些单元格可能包括双引号和换行符。使用复制和粘贴,单元格由制表符分隔,并且具有奇怪数据的单元格被双引号引用。我尝试了主要解决方案,即使用regexp、Jquery-CSV和CSVToArray的链接文章。而http://papaparse.com/是唯一一个可以直接使用的解决方案。使用默认的自动检测选项,与Google Sheets进行复制和粘贴非常顺畅。


1
这个应该排名更高,永远不要尝试自己编写CSV解析器,它将无法正确工作 - 特别是在使用正则表达式时。Papaparse真的很棒 - 使用它吧! - cbley

6

PEG(.js)语法,处理RFC 4180示例的内容在http://en.wikipedia.org/wiki/Comma-separated_values

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 '"'; }
  / [^"]

请在http://jsfiddle.net/knvzk/10https://pegjs.org/online 进行测试。

下载生成的解析器,请访问https://gist.github.com/3362830


5

我已经多次使用正则表达式,但每次都需要重新学习,这很令人沮丧 :-)

所以这里提供一个非正则表达式的解决方案:

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;
}

工作原理:

  1. 将CSV字符串传递给row
  2. 当下一个值的起始位置在行内时,执行以下操作:
    • 如果此值已经被引用,则将nEnd设置为闭合引号。
    • 否则,如果值没有被引用,则将nEnd设置为下一个定界符。
    • 将该值添加到数组中。
    • nStart设置为nEnd加上分隔符的长度。

有时编写自己的小函数,而不是使用库,是很好的选择。自己的代码性能会很好,并且足迹很小。此外,您可以轻松地调整它以满足自己的需要。


2
谢谢@paul!有一个小改进。我用if (nEnd < 0 ) { if (bQuotedValue == true) {nEnd = nRowLen - 1} else {nEnd = nRowLen} }替换了if (nEnd < 0) nEnd = nRowLen,以适应以引号结尾的行。希望这样说得通。 - Blueraaga

5

RFC 4180 解决方案使用正则表达式

正则表达式来拯救!这几行代码可以根据RFC 4180标准,正确处理带有嵌入逗号、引号和换行符的引号字段。

function parseCsv(data, fieldSep, newLine) {
  fieldSep = fieldSep || ',';
  newLine = newLine || '\n';
  const nSep = '\x1D'; const nSepRe = new RegExp(nSep, 'g');
  const qSep = '\x1E'; const qSepRe = new RegExp(qSep, 'g');
  const cSep = '\x1F'; const cSepRe = new RegExp(cSep, 'g');
  const fieldRe = new RegExp('(^|[' + fieldSep + '\\n])"([^"]*(?:""[^"]*)*)"(?=($|[' + fieldSep + '\\n]))', 'g');
  return data
    .replace(/\r/g, '')
    .replace(/\n+$/, '')
    .replace(fieldRe, (match, p1, p2) => {
      return p1 + p2.replace(/\n/g, nSep).replace(/""/g, qSep).replace(/,/g, cSep)
    })
    .split(/\n/)
    .map(line => {
      return line
        .split(fieldSep)
        .map(cell => cell.replace(nSepRe, newLine).replace(qSepRe, '"').replace(cSepRe, ','))
    });
}

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' 
let grid = parseCsv(csv, separator, newline);
// expected: [ [ 'A1', 'B1', 'C1' ], [ 'A "2"', 'B, 2', 'C <br /> 2' ] ]

注意:

  • 字段分隔符可以进行配置,例如使用\t表示TSV(制表符分隔值)
  • 嵌入式换行符可以转换为其他内容,例如在HTML中使用<br/>
  • parseCsv函数避免了负/正向后顾,例如在Safari浏览器上也可以工作。

除非另有说明,否则您不需要有限状态机。正则表达式通过使用临时替换/恢复、捕获组和正向前瞻的函数式编程方法正确处理RFC 4180。

克隆/下载代码https://github.com/peterthoeny/parse-csv-js

了解更多关于正则表达式的知识:https://twiki.org/cgi-bin/view/Codev/TWikiPresentation2018x10x14Regex

旧答案使用后顾

(这在Safari浏览器上无法工作)

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' ] ]

警告:如果您需要在Safari中使用您的解决方案,那么该浏览器存在一个错误,JS正则表达式中的lookbehinds功能无法正常工作。即将发布的版本应该会修复它,但这已经是多年的问题了。 - tobybot
1
添加了一个新的解决方案,不使用回顾后发表达式,例如在Safari上运行。 - Peter Thoeny

4

再增加一个选项,因为我发现以上所有方法都不够简单。这个方法使用正则表达式来查找逗号或换行符,同时可以跳过带引号的项目。希望这对新手来说是容易理解的。 splitFinder 正则表达式有三个功能(用 | 分隔):

  1. , - 查找逗号
  2. \r?\n - 查找换行符(如果导出程序做得好可能还会包括回车符)
  3. "(\\"|[^"])*?" - 跳过带引号的任何内容,因为逗号和换行符在引号中无关紧要。如果引号内有转义引号 \\",它会在找到结束引号之前被捕获。

const splitFinder = /,|\r?\n|"(\\"|[^"])*?"/g;

function csvTo2dArray(parseMe) {
  let currentRow = [];
  const rowsOut = [currentRow];
  let lastIndex = splitFinder.lastIndex = 0;
  
  // add text from lastIndex to before a found newline or comma
  const pushCell = (endIndex) => {
    endIndex = endIndex || parseMe.length;
    const addMe = parseMe.substring(lastIndex, endIndex);
    // remove quotes around the item
    currentRow.push(addMe.replace(/^"|"$/g, ""));
    lastIndex = splitFinder.lastIndex;
  }


  let regexResp;
  // for each regexp match (either comma, newline, or quoted item)
  while (regexResp = splitFinder.exec(parseMe)) {
    const split = regexResp[0];

    // if it's not a quote capture, add an item to the current row
    // (quote captures will be pushed by the newline or comma following)
    if (split.startsWith(`"`) === false) {
      const splitStartIndex = splitFinder.lastIndex - split.length;
      pushCell(splitStartIndex);

      // then start a new row if newline
      const isNewLine = /^\r?\n$/.test(split);
      if (isNewLine) { rowsOut.push(currentRow = []); }
    }
  }
  // make sure to add the trailing text (no commas or newlines after)
  pushCell();
  return rowsOut;
}

const rawCsv = `a,b,c\n"test\r\n","comma, test","\r\n",",",\nsecond,row,ends,with,empty\n"quote\"test"`
const rows = csvTo2dArray(rawCsv);
console.log(rows);


如果我通过fileReader读取我的文件并获得以下结果:Id, Name, Age 1, John Smith, 65 2, Jane Doe, 30如何根据我指定的列进行解析? - bluePearl
在获取到二维数组后,删除第一个索引(如果它是属性名称),然后迭代剩余的数组,使用每个值作为属性创建对象。结果将类似于这样:[{Id: 1, Name: "John Smith", Age: 65}, {Id: 2, Name: "Jane Doe", Age: 30}] - Seph Reed

4

不使用正则表达式,易读,并遵循https://en.wikipedia.org/wiki/Comma-separated_values#Basic_rules的基本规则:

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;
}

1
在我看来,这应该是最佳答案。我使用大数据集进行了测试,与@ridgerunner目前最佳答案相比,此函数的性能约快一倍。此外,它返回一个2D数组,并且不依赖于正则表达式,这使得调试更加容易,并且对于数据输入的要求也不那么苛刻。 - BdR
支持一个CSV文件,其中第一行是列名怎么样? - vsync

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