如何从 Javascript/NodeJS 中的字符串中检测 CSV 分隔符?
哪个是标准算法?
请注意,分隔符并不总是逗号。最常见的分隔符是;
,,
和\t
(tab)。
如何从 Javascript/NodeJS 中的字符串中检测 CSV 分隔符?
哪个是标准算法?
请注意,分隔符并不总是逗号。最常见的分隔符是;
,,
和\t
(tab)。
获取可能的分隔符的可能算法非常简单,并假设数据格式正确:
length
.length
不等于最后一行的长度,则这不是有效的分隔符。概念证明(不处理引用字段):
function guessDelimiters (text, possibleDelimiters) {
return possibleDelimiters.filter(weedOut);
function weedOut (delimiter) {
var cache = -1;
return text.split('\n').every(checkLength);
function checkLength (line) {
if (!line) {
return true;
}
var length = line.split(delimiter).length;
if (cache < 0) {
cache = length;
}
return cache === length && length > 1;
}
}
}
检查是为了length > 1
确保split
不只是返回整行。请注意,这将返回一组可能的分隔符 - 如果有多个项目,则存在歧义问题。
另一种解决方案是使用csv-string包中的detect
方法:
detect(input : String) : String 检测最佳分隔符。
var CSV = require('csv-string');
console.log(CSV.detect('a,b,c')); // OUTPUT : ","
console.log(CSV.detect('a;b;c')); // OUTPUT : ";"
console.log(CSV.detect('a|b|c')); // OUTPUT : "|"
console.log(CSV.detect('a\tb\tc'));// OUTPUT : "\t"
此解决方案允许仅检测顶行的 csv 分隔符并使用csv-parse处理引用字段。
对于大型 csv 文件,避免多次读取整个文件可能很有用。
const parse = require('csv-parse/lib/sync');
const fs = require('fs')
function detectCsvDelimiter(file, maxLineCount, delimiters = [',', ';', '\t']) {
return new Promise((resolve, reject) => {
// Read only maxLineCount lines
let stream = fs.createReadStream(file, {
flags: 'r', encoding: 'utf-8', bufferSize: 64 * 1024 });
let lineCounter = 0;
let data = '';
stream.on("data", (moreData) => {
data += moreData;
lineCounter += data.split("\n").length - 1;
if (lineCounter > maxLineCount + 1) {
stream.destroy();
// Remove invalid last line
resolve(data.split('\n').slice(0, maxLineCount));
}
});
stream.on("error", (err) => reject(err));
stream.on("end", () => resolve(data.split("\n")));
}).then(lines => {
return new Promise(resolve => {
const csvData = lines.join("\n");
const validDelimiters = delimiters.filter(delimiter => {
let isValid = true;
// csv-parse throw error by default
// if the number of columns is inconsistent between lines
try {
const rows = parse(csvData, {delimiter});
isValid = rows.some(row => row.length > 1);
} catch (e) {
isValid = false;
}
return isValid;
});
resolve(validDelimiters);
});
});
}