您可以使用awk 命令仅在" 引用的部分内进行搜索/替换。
第一步是将,替换为_
cat demo.txt | awk 'BEGIN{FS=OFS="\""} {for(i=2;i<NF;i+=2)gsub(",","_",$i)} 1'
给了
3, "hh_1_foo", foo
"5___5", "1_2_3d___something ", foo2
test, "col3", foo3
然后用更常用的tr 命令替换, 为;。
tr ',' ';'
最后一步以“反向”方式再次使用 awk 将临时的_ 占位符替换为初始的, 字符。
把我们所有的东西放在一起:
cat demo.txt |
awk 'BEGIN{FS=OFS="\""} {for(i=2;i<NF;i+=2)gsub(",","_",$i)} 1' |
tr ',' ';' |
awk 'BEGIN{FS=OFS="\""} {for(i=2;i<NF;i+=2)gsub("_",",",$i)} 1'
这给了
3; "hh,1,foo"; foo
"5,,,5"; "1,2,3d,,,something "; foo2
test; "col3"; foo3
正如预期的那样。
更新:最快的解决方案?
我使用我得到的 3 个答案将它们放在一个 206Mb 的 csv 文件上(运行了几次来处理缓存效果......),这是我得到的典型结果:
1/ 我最初的回答:
time cat avec_vapeur.csv | awk 'BEGIN{FS=OFS="\""} {for(i=2;i<NF;i+=2)gsub(",","_",$i)} 1' | tr ',' ';' | awk 'BEGIN{FS=OFS="\""} {for(i=2;i<NF;i+=2)gsub("_",",",$i)} 1' > /dev/null
real 0m2.488s
user 0m5.025s
sys 0m0.242s
2/ 基于 awk 的替代解决方案:ravindersingh13
time cat avec_vapeur.csv | awk -F"\"" '{for(i=1;i<=NF;i+=2){gsub(/,/,";",$i)}} 1' OFS="\"" > /dev/null
real 0m4.705s
user 0m4.631s
sys 0m0.111s
3/ 基于 sed 的解决方案:sjsam
time cat avec_vapeur.csv | sed -E 's/,([[:space:]]*")/;\1/g;s/("[[:space:]]*),/\1;/g' > /dev/null
real 0m0.174s
user 0m0.118s
sys 0m0.130s
-> 明显的赢家是基于 sed 的解决方案!
我得到的最后一个答案:inian
time cat avec_vapeur.csv | awk -v OFS=';' 'BEGIN{FPAT = "([^,]+)|([[:space:]]*\"[^\"]+\")"}{$1=$1}1' > /dev/null
real 0m37.507s
user 0m37.463s
sys 0m0.122s
这也是我测试过的最慢的(这里不做判断,只是为了好玩才做这些测试!)
更新:我最初误读了 =inian=,抱歉。如果我理解你,我补充说
LC_ALL=C
加快速度。
现在我明白了:
real 0m20.268s
user 0m20.008s
sys 0m0.087s
这比 sed 解决方案更快,但没有那么快。
现在比赛结束了,我没有替补席了(我也得努力一点)
获胜者的遗言,perl 解决方案:sjsam
time cat avec_vapeur.csv | perl -ane 's/,(\s*"[^"]*"\s*),/;$1;/g;print' > /dev/null
real 0m0.134s
user 0m0.096s
sys 0m0.104s
它甚至比 sed 快一点(至少在我的测试中)!