将双引号外的每个逗号替换为管道

将双引号外的每个逗号替换为管道

使用 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

相关内容