如何用JavaScript解析一个CSV字符串,它在数据中包含逗号
How can I parse a CSV string with JavaScript, which contains comma in data?
我有以下类型的字符串
var string = "'string, duppi, du', 23, lala"
我想在每个逗号上将字符串分割成一个数组,但只有单引号外的逗号。
I can't figure out for the split…
string.split(/,/)
会给我
["'string", " duppi", " du'", " 23", " lala"]
,但结果应该是:
["string, duppi, du", "23", "lala"]
是否有跨浏览器的解决方案?
免责声明
2014-12-01更新:下面的答案只适用于一种非常特定的CSV格式。正如DG在评论中正确指出的那样,这个解决方案不适合RFC 4180对CSV的定义,也不适合MS Excel格式。这个解决方案简单地演示了如何解析一个(非标准的)CSV输入行,其中包含字符串类型的混合,其中字符串可能包含转义的引号和逗号。
非标准CSV解决方案
正如austincheney正确指出的那样,如果您希望正确处理可能包含转义字符的引号字符串,您确实需要从头到尾解析字符串。此外,OP没有明确定义什么是"CSV字符串"。真的是。首先,我们必须定义什么构成了一个有效的CSV字符串和它的各个值。
给定:"CSV字符串"定义
出于本讨论的目的,一个"CSV字符串"由零个或多个值组成,其中多个值用逗号分隔。每个值可以包含:
- 双引号字符串。(可能包含未转义的单引号)
- 单引号字符串。(可能包含未转义的双引号)
- 不带引号的字符串。(不可包含引号、逗号或反斜杠)
- 空值。
规则/注:
- 引号可以包含逗号。
- 引号可以包含转义的任何内容,例如
'that''s cool'
。 - 包含引号、逗号或反斜杠的值必须引号。
- 包含空格的值必须加引号。
- 从所有:
''
的单引号中删除反斜杠。 - 从所有:
'"
的双引号值中删除反斜杠。 - 未加引号的字符串将删除任何前导和尾随空格。
- 逗号分隔符可以有相邻的空格(忽略)。
:
将有效的CSV字符串(如上所定义)转换为字符串值数组的JavaScript函数。
解决方案:
此解决方案使用的正则表达式比较复杂。而且(恕我冒昧)所有重要的正则表达式都应该以自由间距模式显示,并带有大量注释和缩进。不幸的是,JavaScript不允许自由空格模式。因此,由该解决方案实现的正则表达式首先以原生正则表达式语法呈现(使用Python方便的:r'''...'''
raw-多行字符串语法表示)。
首先是一个正则表达式,它验证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字符串中的一个值。它被重复应用,直到没有找到更多的匹配(并且所有的值都被解析)。
Regex从有效的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.
"""
请注意,有一个特殊情况值是这个正则表达式不匹配的——当该值为空时的最后一个值。这个特殊的"empty last value"情况由后面的js函数测试和处理。
输入输出示例:
在以下示例中,花括号用于分隔{result strings}
。(这是为了帮助可视化前后空格和零长度字符串。)
// 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字符串为";valid"。例如,未加引号的值可能不包含反斜杠或引号,例如以下CSV字符串是无效的:
var invalid1 = "one, that's me!, escaped ', comma"
这实际上并不是一个限制,因为任何子字符串都可以用单引号或双引号表示。还请注意,此解决方案仅表示"逗号分隔值"的一种可能定义。
编辑:2014-05-19:新增免责声明。编辑:2014-12-01:将免责声明移至顶部
RFC 4180解决方案
这并不能解决问题中的字符串,因为它的格式不符合RFC 4180;可接受的编码是用双引号转义双引号。下面的解决方案可以正确地使用来自google电子表格的CSV文件d/l。
更新(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);
我喜欢FakeRainBrigand的回答,但是它包含几个问题:它不能处理引号和逗号之间的空白,并且不支持连续的2个逗号。我试着编辑他的答案,但是我的编辑被评论者拒绝了,他们显然不理解我的代码。这是我的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('|'));
人们似乎反对RegEx。为什么?
('s*'[^']+'|'s*[^,]+)(?=,|$)
下面是代码。我还做了一把小提琴。
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; }
我有一个非常具体的用例,我想从谷歌表格复制单元格到我的web应用程序。单元格可以包括双引号和新行字符。使用复制和粘贴,单元格由制表符分隔,奇数数据的单元格用双引号括起来。我尝试了这个主要的解决方案,链接的文章使用regexp, Jquery-CSV和CSVToArray。http://papaparse.com/是唯一一个可以正常运行的。复制和粘贴是无缝的与谷歌表格默认自动检测选项。
处理RFC 4180示例的PEG(.js)语法: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/10或https://pegjs.org/online
在https://gist.github.com/3362830下载生成的解析器
再添加一个到列表中,因为我发现上面的所有内容都不够"KISS"。
这个使用正则表达式查找逗号或换行符,同时跳过引号项。希望这是新手可以自己阅读的东西。splitFinder
regexp有三件事(由|
分割):
-
,
-查找逗号 -
'r?'n
-查找新行,(如果出口商很好,可能会有回车) -
"(''"|[^"])*?"
-跳过引号包围的任何内容,因为逗号和换行符在那里无关紧要。如果引号中有转义的引号''"
,它将在找到结束引号之前被捕获。
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);
无regexp,可读,并根据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;
}
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' ] ]
指出:
- 可以配置字段分隔符,例如TSV (tab分隔值)的
't
- 内嵌的换行符可以转换成其他东西,比如
<br/>
的HTML使用 - 这个
parseCsv
函数避免消极/积极的向后看,例如也适用于Safari浏览器。
除非另有说明,否则您不需要有限状态机。正则表达式可以正确地处理RFC 4180,这要归功于使用临时替换/恢复、捕获组和正向向前看的函数式编程方法。
克隆/下载代码https://github.com/peterthoeny/parse-csv-js
了解regex的更多信息: 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' ] ]
如果你可以让你的引号分隔符是双引号,那么这是一个复制的示例JavaScript代码来解析CSV数据。
你可以先把所有的单引号翻译成双引号:
string = string.replace( /'/g, '"' );
…或者您可以编辑该问题中的正则表达式以识别单引号而不是双引号:
// Quoted fields.
"(?:'([^']*(?:''[^']*)*)'|" +
但是,这假定了您的问题中不清楚的某些标记。
我已经多次使用正则表达式,但每次我都必须重新学习它,这是令人沮丧的:-)
所以这里有一个非正则表达式的解决方案:
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;
}
工作原理:
- 在
row
中传入csv字符串。 - 当下一个值的起始位置在行内时,执行以下操作:
- 如果该值已经加了引号,则将
nEnd
设置为结束引号。 - 如果value没有被引用,将
nEnd
设置为下一个分隔符 - 将值添加到数组中
- 设置
nStart
为nEnd
+分隔线长度
- 如果该值已经加了引号,则将
我在解析CSV文件时也遇到过同样的问题。
文件包含一个包含','的列地址。
将CSV文件解析为JSON后,在将其转换为JSON文件时,我得到了键的不匹配映射。
我使用Node.js来解析文件和库,如baby parse和csvtojson。
文件示例-
address,pincode
foo,baar , 123456
当我在JSON中直接解析而不使用婴儿解析时,我得到:
[{
address: 'foo',
pincode: 'baar',
'field3': '123456'
}]
因此,我编写了删除逗号(,)和任何其他分隔符的代码每个字段:
/*
csvString(input) = "address, pincode''nfoo, bar, 123456''n"
output = "address, pincode''nfoo {YOUR DELIMITER} bar, 123455''n"
*/
const removeComma = function(csvString){
let delimiter = '|'
let Baby = require('babyparse')
let arrRow = Baby.parse(csvString).data;
/*
arrRow = [
[ 'address', 'pincode' ],
[ 'foo, bar', '123456']
]
*/
return arrRow.map((singleRow, index) => {
//the data will include
/*
singleRow = [ 'address', 'pincode' ]
*/
return singleRow.map(singleField => {
//for removing the comma in the feild
return singleField.split(',').join(delimiter)
})
}).reduce((acc, value, key) => {
acc = acc +(Array.isArray(value) ?
value.reduce((acc1, val)=> {
acc1 = acc1+ val + ','
return acc1
}, '') : '') + ''n';
return acc;
},'')
}
返回的函数可以传递到csvtojson库中,因此可以使用结果。
const csv = require('csvtojson')
let csvString = "address, pincode''nfoo, bar, 123456''n"
let jsonArray = []
modifiedCsvString = removeComma(csvString)
csv()
.fromString(modifiedCsvString)
.on('json', json => jsonArray.push(json))
.on('end', () => {
/* do any thing with the json Array */
})
现在您可以得到如下输出:
[{
address: 'foo, bar',
pincode: 123456
}]
我的回答假设您的输入是来自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函数:
https://github.com/austincheney/Pretty-Diff/blob/master/fulljsmin.js
补充这个答案
如果需要解析用另一个引号转义的引号,例如:
"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;
}
当将CSV文件读取为字符串时,它在字符串之间包含空值,因此尝试逐行使用'0。
stringLine = stringLine.replace(/'0/g, "" );
试试这个。
function parseCSV(csv) {
let quotes = [];
let token = /(?:(['"`])(['s'S]*?)'1)|([^'t,'r'n]+)'3?|(['r'n])/gm;
let text = csv.replace(/''?(['"`])'1?/gm, s => s.length != 2 ? s : `_r#${quotes.push(s) - 1}`);
return [...text.matchAll(token)]
.map(t => (t[2] || t[3] || t[4])
.replace(/^_r#'d+$/, "")
.replace(/_r#'d+/g, q => quotes[q.replace(/'D+/, '')][1]))
.reduce((a, b) => /^['r'n]$/g.test(b)
? a.push([]) && a
: a[a.length - 1].push(b) && a, [[]])
.filter(d => d.length);
}
使用npm库csv-string来解析字符串,而不是split: https://www.npmjs.com/package/csv-string
这将处理引号中的逗号和空条目
这是基于nity的答案,但对于分号:
'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 and semicolons";four with no quotes;"five with CRLF'r'n"';
console.log(csvToArray(test));
除了ridgerrunner给出的出色而完整的答案外,我还想到了一个非常简单的解决方案,用于后端运行PHP。
将此PHP文件添加到您的域名后端(例如:csv.php
)
<?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")));
?>
现在将这个函数添加到JavaScript工具包中(我相信应该稍微修改一下以实现跨浏览器)。
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));
}
它将花费您一个Ajax调用,但至少您不会重复代码或包含任何外部库。
Ref: http://php.net/manual/en/function.str-getcsv.php
你可以像下面的例子那样使用papaparse.js:
<!DOCTYPE html>
<html lang="en">
<head>
<title>CSV</title>
</head>
<body>
<input type="file" id="files" multiple="">
<button onclick="csvGetter()">CSV Getter</button>
<h3>The Result will be in the Console.</h3>
<script src="papaparse.min.js"></script>
<script>
function csvGetter() {
var file = document.getElementById('files').files[0];
Papa.parse(file, {
complete: function(results) {
console.log(results.data);
}
});
}
</script>
</body>
</html>
不要忘记在同一个文件夹中包含papaparse.js。
根据这篇博文,这个函数应该这样做:
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("|"));
这个jsfiddle还行,但是看起来有些元素前面有空格
- 停止JSON.parse()从JSON字符串数据中删除尾随零
- 如何解析文本区域中的结构化字符串数据(接近JSON)以检索其所需的属性
- 如何使用 jQuery 发送包含特殊字符的查询字符串数据
- 带有 base64 字符串(数据 URI)的 HTML 图像标记
- 在 JQuery ajax POST 调用中传递非字符串数据
- 将大型字符串数据从 HTML/Javascript 发送到代码隐藏
- 如何对字符串数据进行分类 javascript
- 用javascript上的字符串数据对数组进行排序
- 如何在ajax成功调用的html表中显示字符串数据
- 将字符串数据作为类别传递给Highcharts
- 将字符串数据从 C# 传递到 JS
- Javascript:如何从字符串数据动态构建方法
- 正在尝试仅打印字符串数据
- 如何将字符串数据从ajax成功转换为javascript数据
- 如何从JarClassLoader和返回字符串数据的applet Launcher调用小程序中的方法
- Jquery$.ajax没有在ASP.NET MVC中发布字符串数据
- 字符串数据转换为数组问题
- 我无法在流星正确显示字符串数据
- 如何在angularjs中最小化一组字符串数据
- 过滤json字符串数据