美文网首页JavaScript 进阶营让前端飞
【JS】将CSV分割成单元格

【JS】将CSV分割成单元格

作者: 来一斤BUG | 来源:发表于2023-12-03 20:32 被阅读0次

文章开始前,假设你已将csv读取并分割成了行,仅仅需要将单元格分割开来就好了。

这里引用百度百科的csv规则:

  1. 开头是不留空,以行为单位。
  2. 可含或不含列名,含列名则居文件第一行。
  3. 一行数据不跨行,无空行。
  4. 半角逗号(即,)作分隔符,列为空也要表达其存在。
  5. 列内容如存在半角引号(即"),替换成半角双引号("")转义,即用半角引号(即"")将该字段值包含起来。
  6. 文件读写时引号,逗号操作规则互逆。
  7. 内码格式不限,可为 ASCII、Unicode 或者其他。
  8. 不支持数字
  9. 不支持特殊字符

方法一:循环

方法内部处理了分隔符、字符串边界标识符及其转义字符、码点等主要逻辑。

/**
 * 分割csv单元格
 * @param row {string} csv行
 * @param stringBoundaryIdentifier {string} 字符串边界标识符,一般情况下用的是半角双引号
 * @param splitter {string} 单元格分隔符,一般情况下用的是半角逗号
 * @return {string[]}
 */
function splitCsvRow(row, stringBoundaryIdentifier = "\"", splitter = ",") {
    const result = [];
    // 是否在字符串边界标识符内
    let inStringBoundaryIdentifier = false;
    // 单元格内容
    let value = "";
    // 当前字符的索引
    let i = -1;
    for (const char of row) { // 用for...of遍历字符串,可以避免长度超过1的字符被拆分,下面codePointAt、String.fromCodePoint也是如此
        i++;
        const lastCodePoint = row.codePointAt(i - 1);
        const nextCodePoint = row.codePointAt(i + 1);
        const lastChar = lastCodePoint !== void 0 ? String.fromCodePoint(lastCodePoint) : void 0;
        const nextChar = nextCodePoint !== void 0 ? String.fromCodePoint(nextCodePoint) : void 0;
        
        if (char === stringBoundaryIdentifier && (lastChar === splitter || nextChar === splitter)) { // 判断是否在两个边界标识符内,且其包裹了整个单元格
            inStringBoundaryIdentifier = !inStringBoundaryIdentifier;
            continue;
        }
        if (char === splitter && !inStringBoundaryIdentifier) { // 当遇到分隔符且不在单元格边界标识符内时,表示单元格结束,将单元格内容加入结果数组
            result.push(value);
            value = "";
            continue;
        }
        if (char === stringBoundaryIdentifier && nextChar === stringBoundaryIdentifier) { // 当出现连续两个边界标识符时,表示转义,只保留第一个
            i++;
        }
        value += char; // 将字符加入单元格内容
    }
    result.push(value);
    return result;
}

方法二:正则表达式

不建议使用这个方法,正则表达式由于过于复杂,可能消耗大量内存,导致JavaScript heap out of memory错误。

/**
 * 用正则表达式分割csv单元格
 * @param row {string} csv行
 * @param sbi {string} 字符串边界标识符,一般情况下用的是半角双引号
 * @param splitter {string} 单元格分隔符,一般情况下用的是半角逗号
 * @return {string[]}
 */
function splitCsvRowByRegex(row, sbi = "\"", splitter = ",") {
    // const splitCsvRowRegex = /(?:^|,)(?:"([^"]*(?:""[^"]*)*)"|([^,]*))/g;
    const splitCsvRowRegex = new RegExp(
        `(?:^|${splitter})(?:${sbi}([^${sbi}]*(?:${sbi}${sbi}[^${sbi}]*)*)${sbi}|([^${splitter}]*))`,
        "g",
    );
    const result = [];
    let match;
    const escapeCharacter = new RegExp(sbi + sbi, "g");
    while (match = splitCsvRowRegex.exec(row)) {
        const value = match[1] || match[2] || "";
        const valueWithoutQuote = value.replace(escapeCharacter, sbi);
        result.push(valueWithoutQuote);
    }
    return result;
}

相关文章

网友评论

    本文标题:【JS】将CSV分割成单元格

    本文链接:https://www.haomeiwen.com/subject/wqqlgdtx.html