Javascript中的CSV分隔符自动检测

时间:2013-09-27 14:53:56

标签: javascript node.js

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

哪种标准算法?

请注意,分隔符始终不是逗号。最常见的分隔符是;,\t(制表符)。

3 个答案:

答案 0 :(得分:13)

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

  1. 对于每个分隔符,
    1. 对于每一行,
      1. 按分隔符拆分行,检查length
      2. 如果length 等于最后一行的长度,则这不是有效的分隔符。
  2. 概念证明(不处理引用字段):

    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不仅仅返回整行。请注意,这会返回一个可能的分隔符数组 - 如果有多个项目,则会出现歧义问题。

答案 1 :(得分:1)

另一种解决方案是使用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"

答案 2 :(得分: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);
        });
    });
}