![修复 .csv 文件中某些行缺少列的问题](https://linux22.com/image/133860/%E4%BF%AE%E5%A4%8D%20.csv%20%E6%96%87%E4%BB%B6%E4%B8%AD%E6%9F%90%E4%BA%9B%E8%A1%8C%E7%BC%BA%E5%B0%91%E5%88%97%E7%9A%84%E9%97%AE%E9%A2%98.png)
我目前正在处理许多合并的 .csv 文件的数据。不幸的是,这些合并有时是错误的。
这个例子可以最好地解释这一点:
“var1”,“var2”,“var3”,“var4”,“var5”
“2001”,“黄色”,“123”,“abc”,“bcdefg”
“2002”,“黄色”,“123”, “abw”、“asdfkl”
“2001”、“绿色”、“abe”
“2002”、“绿色”、“abp”
“2001”、“蓝色”、“324”、“abx”、“badsf”
“2002 ”、“蓝色”、“231”、“abl”、“cpq”
因此,在第 3 行和第 4 行中,缺少 var3 和 var5 的值。哪些变量(列)缺失的错误总是相同的。
我希望我的 csv 看起来像这样:
“var1”,“var2”,“var3”,“var4”,“var5”
“2001”,“黄色”,“123”,“abc”,“bcdefg”
“2002”,“黄色”,“123”, “abw”,“asdfkl”
“2001”,“绿色”,,“abe”,
“2002”,“绿色”,,“abp”,
“2001”,“蓝色”,“324”,“abx”,“ badsf”
“2002”、“蓝色”、“231”、“abl”、“cpq”
所以现在第 3 行和第 4 行实际上缺少 var3 和 var5 的值。错误并不总是发生在(在本例中)“绿色”上,但也可能发生在另一组上。
我的想法是扫描行中的列,如果列数与标题中的列数不同(“var1”、“var2”等),则添加新的空列。
我必须对许多不同的文件执行此操作,但是一旦我知道如何执行此操作,我想我可以使用 bash 脚本循环。
[编辑]:我想澄清一下,数据集相当大。至少有 19 个变量(列)。 (我需要检查的另一个文件有超过 60 个变量)
现在我正在考虑使用 awk 的解决方案。像这样的东西:
awk '{ if (NF<19) {$7=$7","#NA","#NA}}' 文件 1 > 文件 2
如果没有 19 列(它应该有),则应在第 7 列之后插入两列。稍后会尝试这个...
答案1
想到的最简单的事情就是用逗号分割行,并在只有两个逗号的地方插入额外的逗号。明显的限制是,如果实际值中有逗号,这就会中断。
$ cat test.csv | sed -r 's/^([^,]*),([^,]*),([^,]*)$/\1,\2, ,\3, /g'
"var1", "var2", "var3", "var4", "var5"
"2001", "yellow", "123", "abc", "bcdefg"
"2002", "yellow", "123", "abw", "asdfkl"
"2001", "green", , "abe",
"2002", "green", , "abp",
"2001", "blue", "324", "abx", "badsf"
"2002", "blue", "231", "abl", "cpq"
对于更一般的东西,我可能会编写一个Python脚本(它有CSV 功能内置)。例如,从 stdin 读取 CSV 并输出到 stdout:
#!/usr/bin/env python
import sys
import csv
missing = [3, 5] # 1-indexed positions of missing values
missing.sort() # enforce the increasing order
reader = csv.reader(sys.stdin, delimiter=',', skipinitialspace=True)
writer = csv.writer(sys.stdout)
header = next(reader) # get first row (header)
writer.writerow(header) # write it back
for row in reader:
if len(row) < len(header):
# row shorter than header -> insert empty strings
# inserting changes indices so `missing` must be sorted
for idx in missing:
row.insert(idx - 1, '')
writer.writerow(row)
使用真正的 CSV 解析器的好处是它可以正确处理值和其他边缘情况中的逗号或引号。输出格式也将是正确的 CSV,但与您的格式略有不同:
$ cat test.csv | python test.py
var1,var2,var3,var4,var5
2001,yellow,123,abc,bcdefg
2002,yellow,123,abw,asdfkl
2001,green,,abe,
2002,green,,abp,
2001,blue,324,abx,badsf
2002,blue,231,abl,cpq
正如您所看到的,逗号后面没有多余的引号或空格。如果您确实需要它们,我可以考虑为编写器配置 CSV 方言。