如何在csv文件的特定列中用AWK替换多个字符?

时间:2017-10-15 16:37:08

标签: csv awk

我有一个包含数千行的csv文件。

我需要替换特定列中的一些字符

&acirc ; ---> a
&amp ; ---> &
&eacute ; ---> é

我尝试使用此命令,但它无法正常工作

awk 'BEGIN{FS=OFS=";"} {for (i=3;i<=NF;i++) gsub("/\&amp\;/","\&",$3); gsub("/\&middot\;/", " ",$3); gsub("/\&acirc\;/", "a",$3); gsub("/\&eacute\;/", "e",$3); gsub(/\#/, " ",$3)}' file.csv

示例输入:

32602;1;"Wet &amp; Dry 5029";2663,2662

预期产出:

32602;1;"Wet & Dry 5029";2663,2662

1 个答案:

答案 0 :(得分:1)

那么,您想要使用awk解析CSV文件并仅修改列的子集吗?

首先,解析CSV字段并不像在分隔符(,上或在您的情况下;)中拆分那么简单,因为在引用值时必须避免拆分。 awk的{​​{1}}配方在excellent answer by @EdMorton中给出,如果您使用GNU awk,最优雅的方法是使用FPAT

awk -v FPAT='[^;]*|"[^"]+"' -v OFS=';' '...'

(对于其他awk和一些特殊情况,请参阅引用的答案。)

现在回到你的程序。 gsub ERE参数的正确语法是/pattern/"pattern",但不是两者(例如"/pattern/")。

这意味着您必须按以下方式更换:

gsub("/\&amp\;/","\&",$3)     -->  gsub(/&amp;/, "\\&", $3)
gsub("/\&middot\;/", " ",$3)  -->  gsub(/&middot;/, " ", $3)
gsub("/\&acirc\;/", "a",$3)   -->  gsub(/&acirc;/, "a", $3)
gsub("/\&eacute\;/", "e",$3)  -->  gsub(/&eacute;/, "e", $3)

另请注意,在ERE正则表达式部分中,&;不必转义,但在替换字符串&中也是如此(\还需要转义)。

此外,要仅修改列$3,您不需要for循环。但是,如果您确实要修改以$3开头并以最后$NF结尾的列范围,则需要在每个$i中使用gsub请致电,而不是$3

已修复,您的awk程序如下:

awk -v FPAT='[^;]*|"[^"]+"' -v OFS=';' '{
    for (i=3; i<=NF; i++) {
        gsub(/&amp;/, "\\&", $i)
        gsub(/&middot;/, " ", $i)
        gsub(/&acirc;/, "a", $i)
        gsub(/&eacute;/, "e", $i)
        gsub(/#/, " ", $i)
    }
    print
 }' file.csv

(最后的print确保打印每一行。)

应用于您的示例(并转换为单行):

$ echo '32602;1;"Wet &amp; Dry 5029";2663,2662' | awk -v FPAT='[^;]*|"[^"]+"' -v OFS=';' '{for (i=3;i<=NF;i++) {gsub(/&amp;/,"\\&",$i); gsub(/&middot;/," ",$i); gsub(/&acirc;/,"a",$i); gsub(/&eacute;/,"e",$i); gsub(/#/," ",$i)}; print}'
32602;1;"Wet & Dry 5029";2663,2662

在评论中进行了额外的故障排除后,您的问题的解决方案似乎不是替换某些特定列中的HTML实体,而是在完整文件中替换它们,因为您的CSV文件似乎格式不正确,因此后续处理器无法解析它(可能是由于未加引号的;)。

您可以使用简单的sed命令替换您指定的所有HTML实体,如:

sed -e 's/&amp;/\&/g' -e 's/&middot;/ /g' -e 's/&acirc;/a/g' -e 's/&eacute;/e/g' -e 's/#/ /g' file