用(g)awk替换csv文件中的列值,并使用包含字符串的分隔符

时间:2014-09-25 09:26:02

标签: csv awk gawk

我使用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

3 个答案:

答案 0 :(得分:2)

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

我使用了GNU Awk Manual 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。

以下是4个记录的5个字段CSV,其中引用字段为CRLF,引用字段中包含转义引号,以及两种空白字段(,,和{{1 }})。

""

使用相同的脚本(使用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)根据RFC 4180正确处理所有这些情况:

str

使用awk很困难,因为$ 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' 定义了每条记录,我们没有正确处理空字段,也没有正确处理转义引号:

\n

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

答案 1 :(得分:0)

输出是

$ 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;

答案 2 :(得分:0)

  1. $ echo $ ROW | gawk -vOFS =, - vFPAT ='[^,] + |“[^”] “''$ 2 =”X“'
  2. 应该在[^“]之后

    1. echo $ ROW | gawk -vOFS =, - vFPAT ='[^,] + |“[^”]。*“''$ 2 =”X“'
    2. 这2个答案产生1,x,4,5,6的输出为ROW ='1,“2,3”,4,5,6'