我有一个 CSV 表,其中包含 100 行和数列的数字。我有另一个列表,它是一个 1 列的文件,其中包含表中的一些数字。有没有办法可以从 CSV 中删除列表中没有的所有值?
我以为我可以用于grep -f
列表文件,但我很挣扎,因为我想要删除的一些值与我想要保留的值位于同一行。
例如
CSV 表:
11,12,13
11,10,12,13
列表文件:
13
11
输出:
11,,13
11,,,,13
或者替代地
11,13
11,13
答案1
以下awk
程序假设您的 CSV 字段不包含前导或尾随空格:
awk 'BEGIN {FS=OFS=","}
NR==FNR{valid[$1];next}
{for (i=1;i<=NF;i++) {if (!($i in valid)) {$i=""}}} 1' validvalues.txt input.csv
它将首先处理validvalues.txt
包含有效值的文件,然后处理实际的 CSV 文件。
- 在
BEGIN
部分中,输入和输出的字段分隔符设置为,
。 - 在处理第一个文件时(由 表示
NR
,全局行计数器,等于FNR
,每个文件行计数器),我们只需将允许的值记录为数组 中的索引valid
,否则跳过处理到下一个输入行。 - 处理第二个文件时,我们迭代所有字段并检查字段内容是否是 的“数组索引”的一部分
valid
。如果没有,我们将字段值设置为空字段。 - 看似杂乱的
1
打印当前行,包括迄今为止所做的所有修改。
关键点是($i in valid)
测试是基于字符串的比较,因此如果“有效值”文件中的列条目或 CSV 文件中的字段包含前导/尾随空格,则比较将要求相同的空格也在其中相应的其他文件,这可能会导致意外的行为。
正如@glenn jackman 提到的该程序可以简化如下:
awk 'BEGIN {FS=OFS=","}
NR==FNR{valid[$1]=$1;next}
{for (i=1;i<=NF;i++) {$i=valid[$i]}} 1' validvalues.txt input.csv
在这里,我们实际上也将有效值注册为“数组值”。这个想法是,由于无效值在 中没有条目valid
,valid[$i]
因此将自动计算为空字符串,而对于有效值,它将返回值本身。
但请注意,性能会稍慢,因为它不必要地替换“字段值本身”,并且如果“有效值”文件很大,则需要更多内存,这可能会成为问题。
答案2
如果您有用于多字符 RS 和 RT 的 GNU awk:
$ awk -v RS='[,\n]' 'NR==FNR{a[$0]; next} {ORS=RT} $0 in a' list file.csv
11,13
11,13
答案3
您可以创建一个列表,用不需要的值的管道分隔
list=$(<list.txt tr '\n' '|')
你将会拥有13|11|
。
然后运行一个磨坊主搜索和替换
mlr --nidx --fs "," -S put 'for (k in $*) {if($[k]!=~"^('"$list"')$"){$[k] = gsub($[k], $[k], "")}}' input.csv
具有
11,,13
11,,,13