gpt4 book ai didi

javascript - 通过跳过引号之间包含的换行符来拆分 CSV 字符串

转载 作者:行者123 更新时间:2023-11-29 10:50:57 25 4
gpt4 key购买 nike

如果以下正则表达式可以按行拆分 csv 字符串。

var lines = csv.split(/\r|\r?\n/g);

如何调整它以跳过 CSV 值中包含的换行符(即引号/双引号之间)?

例子:

2,"Evans & Sutherland","230-132-111AA",,"Visual","P
CB",,1,"Offsite",

如果你没有看到它,这里有一个换行符可见的版本:

2,"Evans & Sutherland","230-132-111AA",,"Visual","P\r\nCB",,1,"Offsite",\r\n 

我试图跳过的部分是包含在“PCB”条目中间的换行符。

更新:

我之前可能应该提到过这个,但这是名为 jquery-csv 的专用 CSV 解析库的一部分。 .为了提供更好的上下文,我在下面添加了当前的解析器实现。

下面是验证和解析条目的代码(即一行):

$.csvEntry2Array = function(csv, meta) {
var meta = (meta !== undefined ? meta : {});
var separator = 'separator' in meta ? meta.separator : $.csvDefaults.separator;
var delimiter = 'delimiter' in meta ? meta.delimiter : $.csvDefaults.delimiter;

// build the CSV validator regex
var reValid = /^\s*(?:D[^D\\]*(?:\\[\S\s][^D\\]*)*D|[^SD\s\\]*(?:\s+[^SD\s\\]+)*)\s*(?:S\s*(?:D[^D\\]*(?:\\[\S\s][^D\\]*)*D|[^SD\s\\]*(?:\s+[^SD\s\\]+)*)\s*)*$/;
reValid = RegExp(reValid.source.replace(/S/g, separator));
reValid = RegExp(reValid.source.replace(/D/g, delimiter));

// build the CSV line parser regex
var reValue = /(?!\s*$)\s*(?:D([^D\\]*(?:\\[\S\s][^D\\]*)*)D|([^SD\s\\]*(?:\s+[^SD\s\\]+)*))\s*(?:S|$)/g;
reValue = RegExp(reValue.source.replace(/S/g, separator), 'g');
reValue = RegExp(reValue.source.replace(/D/g, delimiter), 'g');

// Return NULL if input string is not well formed CSV string.
if (!reValid.test(csv)) {
return null;
}

// "Walk" the string using replace with callback.
var output = [];
csv.replace(reValue, function(m0, m1, m2) {
// Remove backslash from any delimiters in the value
if (m1 !== undefined) {
var reDelimiterUnescape = /\\D/g;
reDelimiterUnescape = RegExp(reDelimiterUnescape.source.replace(/D/, delimiter), 'g');
output.push(m1.replace(reDelimiterUnescape, delimiter));
} else if (m2 !== undefined) {
output.push(m2);
}
return '';
});

// Handle special case of empty last value.
var reEmptyLast = /S\s*$/;
reEmptyLast = RegExp(reEmptyLast.source.replace(/S/, separator));
if (reEmptyLast.test(csv)) {
output.push('');
}

return output;
};

注意:我还没有测试过,但我想我可能会将最后一场比赛合并到主要的拆分/回调中。

这是执行按行拆分部分的代码:

$.csv2Array = function(csv, meta) {
var meta = (meta !== undefined ? meta : {});
var separator = 'separator' in meta ? meta.separator : $.csvDefaults.separator;
var delimiter = 'delimiter' in meta ? meta.delimiter : $.csvDefaults.delimiter;
var skip = 'skip' in meta ? meta.skip : $.csvDefaults.skip;

// process by line
var lines = csv.split(/\r\n|\r|\n/g);
var output = [];
for(var i in lines) {
if(i < skip) {
continue;
}
// process each value
var line = $.csvEntry2Array(lines[i], {
delimiter: delimiter,
separator: separator
});
output.push(line);
}

return output;
};

有关 reges 如何工作的详细信息,请查看 this answer .我的是一个稍微改编的版本。我合并了单引号和双引号匹配以仅匹配一个文本定界符并使定界符/分隔符动态化。它在验证实体方面做得很好,但我在上面添加的行拆分解决方案非常脆弱,并且打破了我上面描述的边缘情况。

我只是在寻找一种解决方案,它遍历字符串以提取有效条目(以传递给条目解析器)或在错误数据上失败并返回一个错误,指示解析失败的行。

更新:

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

分线器只需要 4 个状态:

  • 0:条目的开始
  • 1:引用以下内容
  • 2:遇到第二个引号
  • 3:以下未被引用

它几乎是一个完整的解析器。对于我的用例,我只需要一个分行器,这样我就可以提供一种更精细的方法来处理 CSV 数据。

注意:此方法归功于另一位开发人员,未经他的许可我不会公开其姓名。我所做的只是将它从一个完整的解析器改编为一个分行器。

更新:

在之前的 lineSplitter 实现中发现了一些边缘情况。提供的应该是完整的 RFC 4180合规。

最佳答案

正如我在评论中指出的那样,仅使用单个正则表达式并没有完整的解决方案。

描述了一种使用多个正则表达式的新方法,该方法通过在逗号上拆分并用嵌入的逗号连接回字符串 here: -

就我个人而言,我会使用描述的简单有限状态机 here

状态机的代码多了,但是代码更干净了,每段代码在干什么,一目了然。从长远来看,这将更加可靠和可维护。

关于javascript - 通过跳过引号之间包含的换行符来拆分 CSV 字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10407697/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com