8

如何从 Javascript/NodeJS 中的字符串中检测 CSV 分隔符?

哪个是标准算法?

请注意,分隔符并不总是逗号。最常见的分隔符是;,,\t(tab)。

4

3 回答 3

20

获取可能的分隔符的可能算法非常简单,并假设数据格式正确:

  1. 对于每个分隔符,
    1. 对于每一行,
      1. 用分隔符分割行,检查length.
      2. 如果它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不只是返回整行。请注意,这将返回一组可能的分隔符 - 如果有多个项目,则存在歧义问题。

于 2013-09-28T18:26:25.087 回答
3

另一种解决方案是使用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"
于 2018-04-06T09:59:10.627 回答
0

此解决方案允许仅检测顶行的 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);
        });
    });
}
于 2019-06-04T15:48:51.603 回答