如果只有非空字段用双引号括起来,我该如何读取CSV文件?

时间:2016-02-19 14:39:19

标签: bash csv unix awk

我试图在Bash脚本中读取CSV文件。我使用gawk并指定FPAT成功实现了这一目标:

gawk -v LOGFILE="${LOGFILE}" 'BEGIN {
    FPAT = "([^,]+)|(\"[^\"]+\")"
    }
NR == 1{
    # doing some logic with header
}
NR >= 2{
    # doing some logic with fields
}' <filename>

问题在于,该文件包含如下数据:

"RAM","31st street, Bengaluru, India",,,,"7865431234",,"VALID"

现在,有了这些数据,我得到了错误的数据,因为它忽略了逗号,这给了我错误的提取数据位置数。 例如,它告诉&#34; 7865431234&#34;出现在第3位,而在第6位。

有人可以建议更改以获得正确的字段位置吗?

2 个答案:

答案 0 :(得分:3)

您的FPAT要求每个字段至少包含一个字符,但您想要识别零字符的空字段。添加允许零字符的FPAT替代方案:

gawk 'BEGIN { FPAT = "([^,]+)|(\"[^\"]+\")|" }
{ printf "%d:%d:", NR, NF; for (i = 1; i <= NF; i++) printf("[%s]", $i); print "" }'

请注意|末尾的额外FPAT。该操作只是标识记录编号,字段数,并用方括号括起每个字段的值。

当您的数据字符串提供给该脚本时,输出为:

1:8:["RAM"]["31st street, Bengaluru, India"][][][]["7865431234"][]["VALID"]

这清楚地显示了四个空字段。

现在你所要做的就是处理:

"Mr ""Manipulator"", the Artisan","29th Street, Delhi, India",,,"",,,"INVALID"

引用值中有双引号。这不是很难管理:

gawk 'BEGIN { FPAT = "([^,]+)|(\"([^\"]|\"\")*\")[^,]*|" }
{ printf "%d:%d:", NR, NF; for (i = 1; i <= NF; i++) printf("%d[%s]", i, $i); print "" }' "$@"

FPAT表示字段为:

  • 一系列非逗号,
  • 或者是以双引号开头的字段,包含零个或多个实例:

    • 非引用,或
    • 两个双引号

    后跟双引号和可选的非逗号数据

  • 或者它是空的

请注意,&#39;可选的非逗号数据&#39;应该为空,并且只会出现在格式错误的CSV数据中。

给定输入数据:

"RAM","31st street, Bengaluru, India",,,,"7865431234",,"VALID"
"Mr ""Manipulator"", the Artisan","29th Street, Delhi, India",,,,,,"INVALID"
"Some","","Empty","",Fields "" Wrapped,"",in quotes
"Malformed" CSV,Data,"Note it has data after" a close quote,"and before a comma,",,"INVALID"

这会产生:

1:8:1["RAM"]2["31st street, Bengaluru, India"]3[]4[]5[]6["7865431234"]7[]8["VALID"]
2:8:1["Mr ""Manipulator"", the Artisan"]2["29th Street, Delhi, India"]3[]4[]5[]6[]7[]8["INVALID"]
3:7:1["Some"]2[""]3["Empty"]4[""]5[Fields "" Wrapped]6[""]7[in quotes]
4:6:1["Malformed" CSV]2[Data]3["Note it has data after" a close quote]4["and before a comma,"]5[]6["INVALID"]

请注意,字段编号作为括号数据的前缀包含在内(因此我略微调整了打印格式)。

关于唯一没有处理的格式是可以在字段数据中嵌入换行符的格式 - 根据基于行的输入的性质,它假定没有字段在多行上分割。 (这也意味着它不能正确识别以双引号开头的字段,并且在行尾之前没有匹配的双引号。我想你可以添加一个替代方案来识别它。它最好只是为了使数据正确。)

请注意Sobrique answer中的建议,以使用旨在处理CSV以处理CSV的工具。这通常是一个好主意,你必须处理的变体集越复杂,它就越好。这接近于您应该考虑使用的复杂正则表达式。另请注意,尽管RFC 4180正式且严格地定义了CSV版本,但有多个程序(包括MS Office)可以处理不同但相关的格式。

答案 1 :(得分:3)

如果你有需要解析的csv,那么虽然你通常可以使用正则表达式来破解它,但是使用解析器要容易得多。

这样的事情:

#!/usr/bin/env perl

use strict;
use warnings;
use Text::CSV;

my $csv = Text::CSV -> new; 
open ( my $input, '<', 'flarg.csv' ) or die $!; 

while ( my $row = $csv -> getline ( $input ) ) {
   if ( $. == 1 ) {
        # do first row stuff; 
        print "Header: ", join ",", @$row,"\n";
   }
   else {
       print join "\n", @$row;
   }
}

或者更简单 - 使用核心的Text::ParseWords

#!/usr/bin/env perl

use strict;
use warnings;
use Text::ParseWords;

while ( my $line = <DATA> ) {
    my @fields = parse_line(',', 1, $line);
    print join "\n", @fields;
} 
__DATA__
"RAM","31st street, Bengaluru, India",,,,"7865431234",,"VALID"