You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何清洗字段内含分隔符的TSV文件 替换引号内制表符/换行符为空格

问题描述

现有TSV文件的col2列被双引号包裹的内容中,包含字段分隔符(制表符Tab)或记录分隔符(回车换行符),原始文件示例如下:

$ printf '%b\n' 'col1\tcol2\tcol3' '1\t"A\tB"\t1234' '2\t"CD\nEF"\t567' | \cat -vet
col1^Icol2^Icol3$
1^I"A^IB"^I1234$
2^I"CD$
EF"^I567$

直接按普通TSV解析会出现列数错位,错误解析效果:

+------+---------+------+
| col1 | col2    | col3 |
+------+---------+------+
| 1    | "A   B" | 1234 |
| 2    | "CD     | 567  |
|      | EF"     |      |
+------+---------+------+

需求为:将引号包裹区域内的制表符、换行符替换为空格,最终得到正确解析结果:

+------+---------+------+
| col1 | col2    | col3 |
+------+---------+------+
| 1    | "A B"   | 1234 |
| 2    | "CD EF" | 567  |
+------+---------+------+

使用miller 6.2版本默认TSV模式处理时,工具无法识别引号内的制表符/CR/LF属于字段内容,会触发长度不匹配报错:

$ printf '%b\n' 'col1\tcol2\tcol3' '1\t"A\tB"\t1234' '2\t"CD\nEF"\t567' | mlr --opprint --barred --itsv cat
mlr: CSV header/data length mismatch 3 != 4 at filename (stdin) line  2.
可行解决方案

方案1:Perl 单行预处理(兼容性最好,逻辑最严谨)

通过状态机跟踪双引号开闭状态,仅替换引号内部的制表符、回车、换行符为空格,完全不破坏引号外的TSV结构,处理后的标准TSV可直接被任意工具解析。

perl -0777 -pe '
my $in_quote = 0;
s/(")|[\t\r\n]/$1 ? ($in_quote=!$in_quote, $1) : ($in_quote ? " " : $&)/ge;
' 输入文件.tsv > 清理后标准.tsv

配合miller直接输出目标格式的命令:

printf '%b\n' 'col1\tcol2\tcol3' '1\t"A\tB"\t1234' '2\t"CD\nEF"\t567' | \
perl -0777 -pe 'my $in_quote = 0; s/(")|[\t\r\n]/$1 ? ($in_quote=!$in_quote, $1) : ($in_quote ? " " : $&)/ge;' | \
mlr --itsv --opprint --barred cat

方案2:直接使用Miller(无需额外工具)

Miller默认的--itsv严格TSV模式不支持引号转义规则,但--csv模式遵循RFC4180标准,原生支持双引号包裹字段内的分隔符、换行符,只需手动指定字段分隔符为制表符即可正确解析,再替换字段内的特殊字符即可。

printf '%b\n' 'col1\tcol2\tcol3' '1\t"A\tB"\t1234' '2\t"CD\nEF"\t567' | \
mlr --csv --fs tab --allow-cr-in-quoted-fields put 'for (k in $*) {gsub($[k], /[\t\r\n]/, " ")}' | \
mlr --itsv --opprint --barred cat

方案3:Awk 状态机实现(无perl/mlr环境时使用)

通过Awk实现引号状态跟踪,逐字符处理内容,自动拼接引号内的跨行内容,替换内部特殊字符为空格:

awk '
{
    cur_line = $0
    output = ""
    for (i = 1; i <= length(cur_line); i++) {
        c = substr(cur_line, i, 1)
        if (c == "\"") {
            in_quote = !in_quote
            output = output c
        } else if (in_quote && (c == "\t" || c == "\r")) {
            output = output " "
        } else {
            output = output c
        }
    }
    if (in_quote) {
        printf "%s ", output
    } else {
        print output
    }
}
' 输入文件.tsv

以上三种方案处理示例输入,均可得到预期的正确解析结果。

内容的提问来源于stack exchange,提问作者Cyril Chaboisseau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 01:12:26