在文本文件中,我想删除,
(逗号)和"
(引号)(仅当双引号包含以逗号分隔的数字时)。
56,72,"12,34,54",x,y,"foo,a,b,bar"
预期产出
56,72,123454,x,y,"foo,a,b,bar"
笔记:我仅将上面的行作为示例。我的文本文件包含许多像上面这样的行,双引号内以逗号分隔的数字应该有所不同。那是,
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"
预期输出:
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"
双引号内有许多n
数字,用逗号分隔。并且保留包含字符的双引号。
我喜欢sed
文本处理工具。如果您sed
为此 发布任何解决方案,我很高兴。
答案1
如果 perl 没问题,这里有一个简短的(可能是快速的,如果不一定简单:))方法:
perl -pe 's:"(\d[\d,]+)":$1=~y/,//dr:eg' file
e
运算符的标志(s:::
这只是另一种编写方式s///
)导致替换被视为每次都会计算的表达式。该表达式$1
从正则表达式(已经缺少引号)中获取捕获并通过删除 ( ) 所有逗号来翻译 ( y///
,也可以写为) 它。为了获取翻译字符串的值(而不是翻译的计数),必须使用标志to 。tr///
/d
r
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
答案2
这个(改编自这里)应该做你需要的事情,尽管 @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"
解释
:a
:定义一个名为 的标签a
。s/(("[0-9,]*",?)*"[0-9,]*),/\1/
: 这个需要分解一下- 首先,使用这个结构:
(foo(bar))
,\1
will befoobar
和\2
will bebar
。 "[0-9,]*",?
:匹配 0 个或多个0-9
或,
,后跟 0 或 1,
。("[0-9,]*",?)*
:匹配以上 0 个或多个。"[0-9,]*
:匹配 0 个或多个紧随 a 之后的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"
因此,虽然您可以找到紧接在引号后面且后跟逗号和另一个数字的数字,但请将这两个数字连接在一起并重复该过程,直到不再可能为止。
在这一点上,我认为提及info sed
描述高级功能的部分中出现的引用是有用的,例如上面使用的标签(感谢查找 if @Braiam):
在大多数情况下,使用这些命令表明您最好使用“awk”或 Perl 等语言进行编程。
答案3
对于 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
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"
答案4
使用乐(以前称为 Perl_6)
~$ raku -pe 's:g/ \" ~ \" (\d+) ** 2..* % "," /{$0.join}/;' file
输入示例:
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"
示例输出:
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"
Raku 是 Perl 系列中的一种编程语言,具有许多强大的正则表达式功能。请参阅下面的 URL,了解此答案的总体概述:
https://unix.stackexchange.com/a/722570/227738
在上面的代码中,识别了数字并删除了嵌入的逗号。正则表达式利用了以下事实:嵌套结构可以用 Raku 的新 ~ 波形符(嵌套)符号表示,这\" ~ \" [\d+]
意味着“一个或多个数字被“双引号”包围。
此外,重复结构%
可以用 Raku 的新修改重复结构量词来表示。符号 [\d+] ** 2..* % "," 表示“用,
逗号分隔的一个或多个数字,此模式重复** 2..*
两次或多次。[如果碰巧有尾随分隔符(例如逗号),在语法中使用 a%%
代替]。%
这只是一个开始。带有备用分隔符、嵌入换行符、嵌入逗号、可能为空白字段等的 CSV 文件确实需要由真正的 CSV 解析器(如 RakuText::CSV
模块)来处理。有关详细信息,请参阅下面的链接。
https://docs.raku.org/language/regexes
https://raku.land/github:Tux/Text::CSV
https://raku.org