我试图在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位。
有人可以建议更改以获得正确的字段位置吗?
答案 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"