【问题标题】:Replace column value in csv file with (g)awk with delimiter containing strings用 (g)awk 替换 csv 文件中的列值,并使用包含字符串的分隔符
【发布时间】:2014-11-20 00:20:42
【问题描述】:

我使用的是 gawk 4.0.1,我知道如何替换 CSV 文件中的列值,例如:

> ROW='1,2,3,4,5,6'
> echo $ROW | gawk -F, -vOFS=, '$2="X"'
1,X,3,4,5,6

但是,我正在处理一个文件,其中包含包含分隔符的字符串。读一列没问题,但是当替换一个值时,会插入一个额外的分隔符:

> ROW='1,"2,3",4,5,6'
> echo $ROW | gawk -vOFS=, -vFPAT='[^,]*|"[^"]*"' '{print $2}'
"2,3"
> echo $ROW | gawk -vOFS=, -vFPAT='[^,]*|"[^"]*"' '$2="X"'
1,X,,4,5,6

这是我所期待的:

> echo $ROW | gawk -vOFS=, -vFPAT='[^,]*|"[^"]*"' '$2="X"'
1,X,4,5,6

值 '"2,3"' 替换为 'X,'。我该如何解决这个问题?

编辑:我没有包括我也有空字段。因此,更好的行示例是:

ROW='1,,"2,3",4,5,6'

编辑 2: 来自Dawg's 的回答我认为这在纯 awk 中是不可能的。虽然我同意 python 的解决方案更好,但 awk 的唯一解决方案是包含一些预处理和后处理来处理空字段。

#/bin/bash
ROW='1,,"2,3",4,"",5'
for col in {1..6}; do 
    echo $ROW |\ 
        sed 's:,,:, ,:' |\ 
        gawk -v c=$col -v OFS=, -v FPAT='([^,]+)|("[^\"]*")' '$c="X"' |\
        sed 's:, ,:,,:g'
done

输出:

X,,"2,3",4,"",5
1,X,"2,3",4,"",5
1,,X,4,"",5
1,,"2,3",X,"",5
1,,"2,3",4,X,5
1,,"2,3",4,"",X

【问题讨论】:

  • 这很奇怪,因为echo $ROW | gawk -vOFS=, -vFPAT='[^,]*|"[^"]*"' '{print $1,"X",$3}' 做到了。我也尝试过 $0=$0 重新解释字符串,但没有运气。
  • @gospes 为什么要依赖 awk?您想从其他工具中获得答案吗?
  • 试试+s 和括号([^,]+)|("[^"]+")
  • 根据the fine manual,您应该在替代运算符| 的任一侧使用分组。那就是(总是来自以前的来源),FPAT = "([^,]+)|(\"[^\"]+\")" --- hth
  • @Jidder:这也不起作用,或者至少,如果我有空字段。

标签: csv awk gawk


【解决方案1】:
$ echo $ROW | awk -vOFS=, -vFPAT="([^,]+)|(\"[^\"]+\")" '$2="X"'
1,X,4,5,6

我使用了 GNU Awk 手册 4.7 Defining Fields By Content 中的模式

与相同模式的*比较:

$ echo $ROW | awk -vOFS=, -vFPAT="([^,]*)|(\"[^\"]*\")" '$2="X"'
1,X,,4,5,6

所以答案是——(对于这个有限的例子)——使用-vFPAT="([^,]+)|(\"[^\"]+\")",但这不适用于像1,"2,3",4,,"","should be 6th field"这样的空字段

这是两种空字段(,,"")的结果:

$ echo $ROW2 | awk -vOFS=, -vFPAT="([^,]+)|(\"[^\"]+\")" '$2="X"'
1,X,4,"","should be 6th field"
      ^^                    - missing the ',,' field
            ^^^             - now the 5th field  -- BUG!

按照惯例,ROW2 应被视为具有 6 个字段,其中空白字段 ,,"" 每个都计为 1 个字段。如果您不将空白字段计为字段,您将失去对空白后哪个字段的计数。使用 awk 正则表达式添加到 CSV 的复杂性列表中。

知道 CSV 是 surprisingly complicated 并处理许多可能性是 not trivial with awkregex 单独。

CSV 的另一个解决方案是使用 Perl 或 Python 以及它们可以使用的更复杂和标准化的 CSV 库。对于 Python,它是 Python 标准发行版的一部分。

这是一个与RFC 4180完全兼容的Python解决方案

$ echo $ROW | python -c '
> import csv, fileinput
> for line in csv.reader(fileinput.input()):
> print ",".join(e if i!=1 else "X" for i, e in enumerate(line))'
1,X,4,5,6

这样可以轻松处理更复杂的 CSV。

这是 5 个字段的 4 条记录 CSV,带引号的字段中带有 CRLF,带引号的字段中带有转义引号,以及两种空白字段(,,"")。

1,"2,3",4,5,6
"11,12",13,14,15,16
21,"22,
23",24,25,"26
27"
31,,"33\"not 32\"","",35

使用相同的脚本(使用repr 查看完整的字段值,但在正常情况下您可能会使用str)根据 RFC 4180 正确处理所有这些情况:

$ cat /tmp/3.csv | python -c '
import csv, fileinput
for line in csv.reader(fileinput.input()):
   print ",".join(repr(e) if i!=1 else "X" for i, e in enumerate(line))'
'1',X,'4','5','6'
'11,12',X,'14','15','16'
'21',X,'24','25','26\n27'
'31',X,'33\\not 32\\""','','35'

这对 awk 来说很困难,因为\n 定义了每条记录,我们没有正确处理空字段,也没有正确处理转义引号:

$ cat /tmp/3.csv | awk -vOFS=, -vFPAT='[^,]+|"[^"]*"' '$2="X"'
1,X,4,5,6
"11,12",X,14,15,16
21,X
23",X,25,"26
27",X
31,X,"",35

现在您需要将 RS 重新定义为一个正则表达式,该表达式在 CR 周围查找引号并使用 awk 读取多行...添加对转义引号的支持...执行更复杂的正则表达式来拆分字段...复杂... . 祝你好运!

【讨论】:

    【解决方案2】:

    输出用于

    $ ROW='1,"2,3",4,5,6' 
    $ echo $ROW | gawk -vOFS=, -vFPAT='[^,]+|"[^"].*"' '$2="X"'
    1,X,4,5,6
    

    这两个命令都可以正常工作。在第二个命令中,* 在此处粘贴时丢失。

    perl:

    $var='1,"2,3",4,5,6';
    $var=~s/\".*\"/X/g;
    print $var;
    

    【讨论】:

    • 您能否详细说明并提供一个示例,好吗?你是说你在“X”中添加了一个逗号?
    • 是的。用 X 添加逗号,同时替换整个双引号将变为 $2。
    • 您能否更新您的答案并表明它确实适用于提问者提供的数据。否则,这是评论,不是真正的答案,可能会被删除。
    • @karthickSundaram 这怎么能通过添加一个来摆脱多余的逗号?
    • 回声 $ROW | gawk -vOFS=, -vFPAT='[^,]+|"[^"].*"' '$2="X"' * 在此处粘贴时丢失。
    【解决方案3】:
    1. $ 回声 $ROW | gawk -vOFS=, -vFPAT='[^,]+|"[^"]."' '$2="X"'

    . 应该在 [^"]

    之后
    1. 回显 $ROW | gawk -vOFS=, -vFPAT='[^,]+|"[^"].*"' '$2="X"'

    对于 ROW='1,"2,3",4,5,6',这 2 个答案产生 1,x,4,5,6 的输出

    【讨论】:

      猜你喜欢
      • 2021-06-25
      • 2017-03-13
      • 2017-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多