如何替换该字符的第N个和第K个实例之间的所有字符实例?

时间:2019-06-13 10:00:17

标签: bash csv awk sed

我正在处理大量CSV文件,并且在其中一列中,字段本身包含逗号。不幸的是,此列没有用引号引起来,因此将CSV文件加载到外部应用程序中会引起问题。

我的CSV文件如下:

col1, col2, col3, co,,,l4, col5, col6
col1, col2, col3, co,,,,,l4, col5, col6
col1, col2, col3, co,,l4, col5, col6

我需要删除此特定列中的所有逗号,但是我不确定如何去做。不幸的是,用正确地用引号引起来的有问题的列重写文件不是一种选择。

这些有问题的逗号总是在 倒数第二个逗号之间出现,但是我没有足够的bash专业知识来编写删除它们的脚本。

输入文件:

col1, col2, col3, co,,,l4, col5, col6
col1, col2, col3, co,,,,,l4, col5, col6
col1, col2, col3, co,,l4, col5, col6

预期输出:

col1, col2, col3, col4, col5, col6
col1, col2, col3, col4, col5, col6
col1, col2, col3, col4, col5, col6

2 个答案:

答案 0 :(得分:0)

我会提出以下建议:

awk '{ match($0,/^[^,]*,[^,]*,[^,],/); p1=RLENGTH+1
       match($0,/,[^,]*,[^,]*$/);    ; p2=RSTART
       s=substr($0,p1,p2-p1); gsub(/,/,"",s)
       print substr($0,1,p1-1) s substr($0,p2)
     }' file.csv

awk 'BEGIN{FS=OFS=","}
     { s=""; for(i=4;i<NF-1;++i) s=s $i }
     { print $1,$2,$3,s,$(NF-1),$NF }' file.csv

这些解决方案假定col1,col2,col3,col5和col6中没有出现,

如果其他列中有逗号,但根据CSV标准正确引用了这些列,则可以使用基于What's the most robust way to efficiently parse CSV using awk?

的类似方法
awk -v FPAT='[^,]*|"[^"]+"' 'BEGIN{OFS=","}
     { s=""; for(i=4;i<NF-1;++i) s=s $i }
     { print $1,$2,$3,s,$(NF-1),$NF }' file.csv

更一般地,回答标题问题:

  

如何替换该字符的第N个和第K个最后一个实例之间的所有字符实例?

假设c是字符:

awk 'BEGIN{FS=OFS="c"; n=3; k=}
     { s=""; for(i=1; i <= n   ;++i) s = $i OFS 
             for(   ; i <= NF-k;++i) s=s $i 
             for(   ; i <= NF  ;++i) s = OFS $i }
     { print s }' file.csv

答案 1 :(得分:0)

如果您真的只想删除该字段中的逗号,则使用GNU awk将第三个参数匹配():

awk 'match($0,/(([^,]*,){3})(.*)((,[^,]*){2})/,a){gsub(/,/,"",a[3]); $0=a[1] a[3] a[4]} 1' file
col1, col2, col3, col4, col5, col6
col1, col2, col3, col4, col5, col6
col1, col2, col3, col4, col5, col6

,否则我将麻烦的字段用双引号引起来,然后将其像其他CSV一样对待(例如,参见What's the most robust way to efficiently parse CSV using awk?):

$ awk 'match($0,/(([^,]*,){3})(.*)((,[^,]*){2})/,a){$0=a[1] "\"" a[3] "\"" a[4]} 1' file
col1, col2, col3," co,,,l4", col5, col6
col1, col2, col3," co,,,,,l4", col5, col6
col1, col2, col3," co,,l4", col5, col6

$ awk '
    BEGIN { FPAT="[^,]*|\"[^\"]+\"" }
    match($0,/(([^,]*,){3})(.*)((,[^,]*){2})/,a) { $0=a[1] "\"" a[3] "\"" a[4] }
    { for (i=1; i<=NF; i++) print NR, NF, i, $i }
' file
1 6 1 col1
1 6 2  col2
1 6 3  col3
1 6 4 " co,,,l4"
1 6 5  col5
1 6 6  col6
2 6 1 col1
2 6 2  col2
2 6 3  col3
2 6 4 " co,,,,,l4"
2 6 5  col5
2 6 6  col6
3 6 1 col1
3 6 2  col2
3 6 3  col3
3 6 4 " co,,l4"
3 6 5  col5
3 6 6  col6

或只是用sed引用部分:

$ sed -E 's/(([^,]*,){3})(.*)((,[^,]*){2})/\1"\3"\4/' file
col1, col2, col3," co,,,l4", col5, col6
col1, col2, col3," co,,,,,l4", col5, col6
col1, col2, col3," co,,l4", col5, col6

以上要求-E使用GNU或BSD / OSX。对于任何POSIX sed,它都是:

$ sed 's/\(\([^,]*,\)\{3\}\)\(.*\)\(\(,[^,]*\)\{2\}\)/\1"\3"\4/' file
col1, col2, col3," co,,,l4", col5, col6
col1, col2, col3," co,,,,,l4", col5, col6
col1, col2, col3," co,,l4", col5, col6