使用awk修复损坏的csv文件

时间:2016-10-07 13:44:51

标签: csv awk

我有一些csv文件被破坏,因为在某些字段中存在诸如控制字符,输入和分隔符之类的垃圾。没有控制字符的示例模型数据:

id;col 1;col 2;col 3
1;data 11;good 21;data 31
2;data 12;cut
in two;data 32
3;data 13;good 23;data 33
4;data 14;has;extra delimiter;data 34
5;data 15;good 25;data 35
6;data 16;cut
and;extra delimiter;data 36
7;data 17;data 27;data 37
8;data 18;cut
in 
three;data 38
9;data 19;data 29;data 39

我用awk处理上面的垃圾:

BEGIN { FS=OFS=";" }       # delimiters
NR==1 { nf=NF; }           # header record is fine, use the NF
NR>1 {
    if(NF<nf) {            # if NF less that header's NF
        prev=$0            # store $0
        if(getline==1) {   # read the "next" line
            succ=$0        # set the "next" line to succ
            $0=prev succ   # rebuild a current record
        }
    }
    if(NF!=nf)             # if NF is still not adequate
        $0=succ            # expect original line to be malformed
    if(NF!=nf)             # if the "next" line was malformed as well
        next               # well skip "next" line and move to next
} 1

当然,上面的程序将失败记录46(因为实际数据有几个字段,其中额外的分隔符可能潜伏)和8(因为我只阅读下一行,如果NF太短了。我可以忍受失去46,但8可能会失败吗?

此外,if循环连续三次for的尖叫声,但是星期五下午在这里,我的日子已接近$,我无法旋转我的脑袋了。你们有没有脑力储备我可以借用吗?我没有想到的任何最佳实践?

1 个答案:

答案 0 :(得分:2)

她的关键是保留一个包含仍未“完整”的行的缓冲区;一旦它们打印出来并清除缓冲区:

awk -F';' 'NF>=4 && !nf {print; next}   # normal lines are printed
           {                            # otherwise,
                if (nf>0) {             # continue with a "broken" line by...
                    buff=buff OFS $0      # appending to the buffer
                    nf+=NF-1              # and adding NF
                } else {                # new "broken" line, so...
                    buff=$0               # start buffer
                    nf=NF                 # set number of fields already seen
                }
            }
           nf>=4{                       # once line is complete
              print buff                # print it
              buff=""; nf=0             # and remove variables
           }' file

在这里,buff是这样的缓冲区,nf是一个内部计数器,用于跟踪当前记录中已经看到的字段数(就像您在尝试时所做的那样)。

我们在追加到缓冲区时(即从损坏的流的第二行)添加NF-1,因为NF==1的行不添加任何记录但只是与最后一个字段连接上一行:

8;data 18;cut        # NF==3                           |
in                   # NF==1 but it just continues $3  | all together, NF==4
three;data 38        # NF==2 but $1 continues $3       |

使用您的示例输入:

$ awk -F';' 'NF>=4 && !nf {print; next} {buff=(nf>0 ? buff OFS : "") $0; nf+=(nf>0 ? NF-1 : NF)} nf>=4{print buff; buff=""; nf=0}' a
id;col 1;col 2;col 3
1;data 11;good 21;data 31
2;data 12;cut in two;data 32
3;data 13;good 23;data 33
4;data 14;has;extra delimiter;data 34
5;data 15;good 25;data 35
6;data 16;cut and;extra delimiter;data 36
7;data 17;data 27;data 37
8;data 18;cut in  three;data 38
9;data 19;data 29;data 39