使用 sed,我想将双引号之外的每个逗号替换为管道。
因此 .csv 文件中的这一行:
John,Tonny,"345.3435,23",56th Street
将转换为:
John|Tonny|"345.3435,23"|56th Street
你能帮我解决这个问题的正则表达式吗?
答案1
使用csvkit
:
$ csvformat -D '|' file.csv
John|Tonny|345.3435,23|56th Street
csvkit 中的工具知道如何处理复杂的 CVS 文件,这里我们使用正确的csvformat
方式替换分隔逗号|
。输出字段将根据需要被引用。
例子:
$ cat file.csv
John,Tonny,"345.3435,23",56th Street
The | factory,Ltd.,"0,0",meep meep
$ csvformat -D '|' file.csv
John|Tonny|345.3435,23|56th Street
"The | factory"|Ltd.|0,0|meep meep
答案2
如果您sed
支持该-E
选项(-r
在某些实现中):
sed -Ee :1 -e 's/^(([^",]|"[^"]*")*),/\1|/;t1' < file
这
:label
s/pattern/replacement/
t label
是一个很常见的sed
习语。只要成功,它就会在循环中不断执行相同的替换。
在这里,我们将由 0 个或多个带引号的字符串或字符(在 中捕获)"
以外的字符组成的行的前导部分替换为该捕获和 a ,因此在您的示例中这意味着:,
\1
,
\1
|
John,Tonny,"345.3435,23",56th Street
->John|Tonny,"345.3435,23",56th Street
John|Tonny,"345.3435,23",56th Street
->John|Tonny|"345.3435,23",56th Street
John|Tonny|"345.3435,23",56th Street
->John|Tonny|"345.3435,23"|56th Street
- 我们在这里停止,因为模式不再匹配。
使用perl
,您可以通过将标志替换g
为:
perl -pe 's{("[^"]*"|[^",]+)|,}{$1 // "|"}ge'
在这里,假设输入中的引号是平衡的,该模式将匹配所有输入,并将其分解为:
- 带引号的字符串
,
除or之外的字符序列"
- 一个逗号
并且只有当匹配的字符串是逗号时(当$1
替换部分中没有定义时),才将其替换为|
.
答案3
用perl
perl -MText::CSV -lne '
BEGIN { $p = Text::CSV->new() }
print join "|", $p->fields() if $p->parse($_)
' file.csv
John|Tonny|345.3435,23|56th Street
答案4
使用 Python 和csv
模块:
import csv,sys
with open(sys.argv[1]) as csvfile:
csvr = csv.reader(csvfile)
for line in csvr:
dup = map( lambda x: '"' + x + '"' if ',' in x else x, line )
print('|'.join(dup))
工作原理如下:
$ python3 csvfile.py input.csv
John|Tonny|"345.3435,23"|56th Street
John|Doe|"123.456,25"|26th Street
Jane|Doe|"987.654,52"|15th Street