仅删除双引号内的逗号

Avi*_*Raj 11 sed text-processing csv

在文本文件中,我想删除,(逗号)和"(引号)(仅当双引号包含以逗号分隔的数字时)。

56,72,"12,34,54",x,y,"foo,a,b,bar"
Run Code Online (Sandbox Code Playgroud)

预期输出

56,72,123454,x,y,"foo,a,b,bar"
Run Code Online (Sandbox Code Playgroud)

注意:我显示上面的行只是作为一个例子。我的文本文件包含多行如上,双引号中用逗号分隔的数字应该有所不同。那是,

56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"
Run Code Online (Sandbox Code Playgroud)

预期输出:

56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"
Run Code Online (Sandbox Code Playgroud)

n双引号内有许多数字,以逗号分隔。并且还保留包含字符的双引号。

我喜欢sed文本处理工具。如果您sed为此发布任何解决方案,我很高兴。

ric*_*ici 10

如果 perl 没问题,这里有一个简短的(可能很快,如果不一定简单:))的方法:

perl -pe 's:"(\d[\d,]+)":$1=~y/,//dr:eg' file
Run Code Online (Sandbox Code Playgroud)

运算符的e标志s:::(这只是另一种写法s///)导致替换被视为每次都计算的表达式。该表达式$1从正则表达式(已经缺少引号)中获取捕获,并通过删除 ( ) 所有逗号y///来翻译 ( ,也可以写为tr///) 它/d。为了使值成为已翻译的字符串,而不是翻译计数,必须使用r标志 to y

对于那些不知怎么觉得被 perl 玷污的人,这里是 python 等价物。Python 确实不是一个 shell 单行工具,但有时它可以被哄骗合作。以下内容可以写成一行(与for循环不同,循环不能),但水平滚动使其(甚至更多)不可读:

python -c '
import re;
import sys;
r=re.compile("\"(\d+(,\d+)*)\"");
all(not sys.stdout.write(r.sub(lambda m:m.group(1).replace(",",""),l))
    for l in sys.stdin)
' < file
Run Code Online (Sandbox Code Playgroud)


ter*_*don 8

这(改编自此处)应该可以满足您的需求,尽管@rici 的 Perl 更简单:

$ sed -r ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;ta; s/""/","/g; 
          s/"([0-9]*)",?/\1,/g ' file
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454,
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"
Run Code Online (Sandbox Code Playgroud)

解释

  • :a: 定义一个名为 的标签a
  • s/(("[0-9,]*",?)*"[0-9,]*),/\1/ : 这个需要拆开
    • 首先,使用这个构造 : (foo(bar)), \1will befoobar\2will be bar
    • "[0-9,]*",?: 匹配 0 个或多个0-9or ,,然后是 0 或 1 ,
    • ("[0-9,]*",?)* : 匹配 0 个或多个以上。
    • "[0-9,]*:匹配0或更多的0-9还是,一个来后右"
  • ta;:如果替换成功,请返回标签a并再次运行。
  • s/""/","/g;: 后期处理。替换""","
  • s/"([0-9]*)",?/\1,/g : 删除数字周围的所有引号。

用另一个例子可能更容易理解:

$ echo '"1,2,3,4"' | sed -nr ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;p;ta;'
"1,2,34"
"1,234"
"1234"
"1234"
Run Code Online (Sandbox Code Playgroud)

因此,虽然您可以找到一个紧跟在引号之后且后跟逗号和另一个数字的数字,但请将这两个数字连接在一起并重复该过程,直到不再可能为止。

在这一点上,我认为引用info sed描述高级功能的部分中出现的引用很有用,例如上面使用的标签(感谢您找到@Braiam):

在大多数情况下,使用这些命令表明您可能最好使用诸如“awk”或 Perl 之类的语言进行编程。


gle*_*man 6

对于 CSV 数据,我会使用一种带有真正 CSV 解析器的语言。以 Ruby 为例:

ruby -rcsv -pe '
  row = CSV::parse_line($_).map {|e| e.delete!(",") if e =~ /^[\d,]+$/; e} 
  $_  = CSV::generate_line(row)
' <<END
56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"
END
Run Code Online (Sandbox Code Playgroud)
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"
Run Code Online (Sandbox Code Playgroud)