【发布时间】:2016-04-30 04:20:38
【问题描述】:
我是 SED 的新手,有一个简单的问题。我以前用它来替换和删除字符,但这有点不同。我需要消除引号中的逗号,然后消除 csv 文件中的引号。所以这个:
"5,196,386","99,017",493,21
最终应该是这样的:
5196386,99017,493,21
【问题讨论】:
标签: replace sed conditional
我是 SED 的新手,有一个简单的问题。我以前用它来替换和删除字符,但这有点不同。我需要消除引号中的逗号,然后消除 csv 文件中的引号。所以这个:
"5,196,386","99,017",493,21
最终应该是这样的:
5196386,99017,493,21
【问题讨论】:
标签: replace sed conditional
gnu awk 单行:
awk -v FPAT='([^,]+|"[^"]+")' -v OFS="," '{for(i=1;i<=NF;i++)gsub(/[",]/,"",$i)}7'
用你的例子:
kent$ awk -v FPAT='([^,]+|"[^"]+")' -v OFS="," '{for(i=1;i<=NF;i++)gsub(/[",]/,"",$i)}7' <<< '"5,196,386","99,017",493,21'
5196386,99017,493,21
【讨论】:
awk 作为第一反应(或 perl)。
FPAT 是GNU awk 的一个特性,在awk 的其他版本中不一定存在;它不是由 POSIX 指定的。
您需要通过多个s/// 操作来做到这一点。当引号之间只有逗号和数字时,第一个将消除引号之间的逗号;第二个将消除引号(现在它们之间只有数字):
sed -e 's/"\([0-9][0-9]*\),\([0-9,][0-9,]*\)"/"\1\2"/g' \
-e 's/"\([0-9][0-9]*\),\([0-9,][0-9,]*\)"/"\1\2"/g' \
-e 's/"\([0-9][0-9]*\)"/\1/g'
您必须尽可能多地重复第一个操作,因为引号之间可以出现的逗号的最大数量。如果您的价值达到数十亿,您将需要它的第三份副本。
【讨论】:
[0-9][0-9]*简化成[0-9]+甚至\d+...?
sed 的哪个子类。我正在使用可以与任何符合 POSIX 的 sed 一起使用的符号;您正在提议与 sed 的其他变体一起使用的扩展。
+ 是 not part of the "lowest common denominator" of BRE - 感谢您澄清这一点。你这样写确实让我感到惊讶,这就是我问这个问题的原因。现在我明白了。
我会使用带有适当 CSV 解析器的语言。例如:
echo '"5,196,386","99,017",493,21' |
ruby -rcsv -ne 'CSV.parse($_) do |row|
puts CSV.generate_line(row.map {|e| e.delete(",")})
end'
5196386,99017,493,21
【讨论】:
这应该适用于几乎所有awk
echo '"5,196,386","99,017",493,21' | awk 'BEGIN {FS=OFS=""} {for (i=1;i<=NF;i++) {if ($i=="\"") {f=!f;$i=""}; if (f && $i==",") $i=""}}1'
5196386,99017,493,21
它是如何工作的:
awk '
BEGIN { # Begin block
FS=OFS=""} # Set input and output Field separator to "" (nothing) makes loop work on every characters
{for (i=1;i<=NF;i++) { # Looping trough line, one and one character at the time
if ($i=="\"") { # If a double quote is found do:
f=!f # Swap the flag "f" (If "f" is true, you are inside a double quote string
$i=""} # Delete the double quote
if (f && $i==",") # If "f" is true and we find a comma "," (inside a double quote string):
$i=""} # Delete the comma
}
1 # Print the line.
' file
【讨论】:
这可能对你有用(GNU sed):
sed -r ':a;s/"[0-9,]+"/\n&\n/;T;h;s/[,"]//g;G;s/.*\n(.*)\n.*\n(.*)\n.*\n/\2\1/;ta' file
这会将\n 标记围绕双引号字段,复制整行,删除双引号和逗号,然后将行重新组合在一起并重复,直到不再需要更改为止。
另一种方法:
sed -r 's/^/\n/;ta;:a;s/\n+$//;t;s/\n\n"/\n/;ta;s/\n"/\n\n/;ta;s/\n\n,/\n\n/;ta;s/(\n+)(.)/\2\1/;ta' file
使用\n 作为标记逐个字符地传递字符串。两个 \n 的标记表示下一个字符在带引号的字段中。
【讨论】:
awk '{gsub(/"5,196,386","99,017"/,"5196386,99017")}1' file
5196386,99017,493,21
【讨论】: