【问题标题】:Parse CSV with empty fields, escaped quotes and commas with awk使用 awk 解析带有空字段、转义引号和逗号的 CSV
【发布时间】:2017-11-02 16:21:21
【问题描述】:

我一直在愉快地使用 gawk 和 FPAT。这是我用于示例的脚本:

#!/usr/bin/gawk -f

BEGIN {
    FPAT="([^,]*)|(\"[^\"]+\")"
}

{
    for (i=1; i<=NF; i++) {
        printf "Record #%s, field #%s: %s\n", NR, i, $i
    }
}

简单,无引号

效果很好。

$ echo 'a,b,c,d' | ./test.awk 
Record #1, field #1: a
Record #1, field #2: b
Record #1, field #3: c
Record #1, field #4: d

带引号

效果很好。

$ echo '"a","b",c,d' | ./test.awk 
Record #1, field #1: "a"
Record #1, field #2: "b"
Record #1, field #3: c
Record #1, field #4: d

带有空列和引号

效果很好。

$ echo '"a","b",,d' | ./test.awk 
Record #1, field #1: "a"
Record #1, field #2: "b"
Record #1, field #3: 
Record #1, field #4: d

带有转义引号、空列和引号

效果很好。

$ echo '"""a"": aaa","b",,d' | ./test.awk 
Record #1, field #1: """a"": aaa"
Record #1, field #2: "b"
Record #1, field #3: 
Record #1, field #4: d

包含转义引号并以逗号结尾的列

失败。

$ echo '"""a"": aaa,","b",,d' | ./test.awk 
Record #1, field #1: """a"": aaa
Record #1, field #2: ","
Record #1, field #3: b"
Record #1, field #4: 
Record #1, field #5: d

预期输出:

$ echo '"""a"": aaa,","b",,d' | ./test_that_would_be_working.awk 
Record #1, field #1: """a"": aaa,"
Record #1, field #2: "b"
Record #1, field #4: 
Record #1, field #5: d

是否有 FPAT 的正则表达式可以使这项工作,或者这只是 awk 不支持?

该模式将是",后跟除单个" 之外的任何内容。正则表达式类搜索一次只处理一个字符,因此它不能匹配""

我认为可能有一个环视选项,但我还不够好,无法让它发挥作用。

【问题讨论】:

  • @RomanPerekhrest,有四个字段。使用| 作为字段分隔符:a||"b" b,|c
  • @BenoitDuffez,你能接受另一个可行的替代解决方案吗?简单的单线
  • @BenoitDuffez,不幸的是,问题被标记为重复,我无法添加答案。如果您仍然想得到它 - 使用python 标签发布问题,而不提及awk。让我知道
  • @BenoitDuffez,您更喜欢哪种输入:单个字符串还是文件?
  • 请修正第二个例子,它显然是错误的,并解释“somewhat parses”是什么意思。为每种情况添加所需的确切输出。现在它是模糊的,即第二个例子说字段 """b"" b" 需要进一步解析(为什么?)第三个例子说它可以。

标签: regex csv awk


【解决方案1】:

因为 awk 的 FPAT 不知道环视,所以您需要在模式中明确说明。这个可以:

FPAT="[^,\"]*|\"([^\"]|\"\")*\""

解释:

[^,\"]*             # match 0 or more times any character except , and "
|                   # OR
\"                  # match '"'
  ([^\"]            #   followed by 0 or more anything but '"'
   |                #   OR
   \"\"             #   '""'
  )*        
\"                  # ending with '"'

现在测试它:

$ cat tst.awk
BEGIN {
    FPAT="[^,\"]*|\"([^\"]|\"\")*\""
}
{ 
   for (i=1; i<=NF; i++){ printf "Record #%s, field #%s: %s\n", NR, i, $i }
}


$ echo '"""a"": aaa,","b",,d' | awk -f tst.awk
Record #1, field #1: """a"": aaa,"
Record #1, field #2: "b"
Record #1, field #3:
Record #1, field #4: d

【讨论】:

  • 简直太棒了。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-11
  • 2013-03-09
  • 1970-01-01
  • 2013-01-16
  • 2013-01-10
  • 1970-01-01
相关资源
最近更新 更多