使用awk / sed进行CSV解析,并在每列中包含可能的','字符

时间:2014-02-10 11:22:29

标签: bash sed awk

我的CSV文件包含以下格式的文本:

"abcd, xyz", abcd, 012
"xyz, 123, abcd", 123, "abcd, pqr"

每列可能都有逗号分隔的文本。在这种情况下,列内的文本被包含在一对“”字符中。

我正在寻求使用sed或awk解析这样一个文件的帮助。

非常感谢你。

2 个答案:

答案 0 :(得分:1)

最简单的事情是将引号内的逗号或字段之间的逗号转换为其他字符,例如:这会将字段之间的每个“,”转换为制表符char:

$ awk 'BEGIN{FS=OFS="\""} {for (i=1;i<=NF;i+=2) gsub(/[[:space:]]*,[[:space:]]*/,"\t",$i)} 1' file
"abcd, xyz"     abcd    012
"xyz, 123, abcd"        123     "abcd, pqr"

然后当然你需要找到一些在输入中不会出现的字符,这样你就可以选择一个控件字符或SUBSEP或其他东西。

或者,这会将每个“a”转换为“aA”,并将每个分隔符转换为“aB”,这样您就知道您的分隔符不会出现在您的输入中:

$ awk 'BEGIN{FS=OFS="\""} {gsub(/a/,"aA"); for (i=1;i<=NF;i+=2) gsub(/[[:space:]]*,[[:space:]]*/,"aB",$i)} 1' file
"aAbcd, xyz"aBaAbcdaB012
"xyz, 123, aAbcd"aB123aB"aAbcd, pqr"

你可以这样做:

$ awk 'BEGIN{FS=OFS="\""} {gsub(/a/,"aA"); for (i=1;i<=NF;i+=2) gsub(/[[:space:]]*,[[:space:]]*/,"aB",$i)} 1' file |
awk -F'aB' '{gsub(/aA/,"a"); print $0; for (i=1;i<=NF;i++) print "\tField " i " = <" $i ">"}'
"abcd, xyz"aBabcdaB012
        Field 1 = <"abcd, xyz">
        Field 2 = <abcd>
        Field 3 = <012>
"xyz, 123, abcd"aB123aB"abcd, pqr"
        Field 1 = <"xyz, 123, abcd">
        Field 2 = <123>
        Field 3 = <"abcd, pqr">

如果您想在一个命令中完成所有操作:

$ awk '
function decomma() {
    FS = OFS = "\""
    $0 = $0
    gsub(/a/,"aA")
    for (i=1;i<=NF;i+=2)
        gsub(/[[:space:]]*,[[:space:]]*/,"aB",$i)
    gsub(/aA/,"a")
    FS = "aB"
    $0 = $0
}

{
    print $0
    decomma()
    for (i=1;i<=NF;i++)
       print "\tField " i " = <" $i ">"
}
' file
"abcd, xyz", abcd, 012
        Field 1 = <"abcd, xyz">
        Field 2 = <abcd>
        Field 3 = <012>
"xyz, 123, abcd", 123, "abcd, pqr"
        Field 1 = <"xyz, 123, abcd">
        Field 2 = <123>
        Field 3 = <"abcd, pqr">

答案 1 :(得分:0)

另一种CSV格式如下(唯一引用的字段是包含逗号的字段):

  field1, "field2,with,commas"  ,  field3  ,  "field4,foo"   

我们在这里有引用和不引用字段的混合,不能直接通过FS的任何值解析(至少我知道)。但是,我们仍然可以在循环中使用match()来获取字段(并且有点作弊):

c=0
$0=$0","                                   # yes, cheating
while($0) {
  match($0,/ *"[^"]*" *,|[^,]*,/)
  f=substr($0,RSTART,RLENGTH)             # save what matched in f
  gsub(/^ *"?|"? *,$/,"",f)               # remove extra stuff
  print "Field " ++c " is " f
  $0=substr($0,RLENGTH+1)                 # "consume" what matched
}

上述数据引自:http://web.archive.org/web/20120531065332/http://backreference.org/2010/04/17/csv-parsing-with-awk/