用逗号分隔的行进行sed模式否定

用逗号分隔的行进行sed模式否定,sed,Sed,我有一个充满行的文本文件,看起来像: Female,"$0 to $25,000",Arlington Heights,0,60462,ZD111326,9/18/13 0:21,Disk Drive 我正在尝试将所有逗号,更改为管道|,引号中的逗号除外。 尝试使用sed(我是新手)。。。而且它不起作用。使用: sed '/".*"/!s/\,/|/g' textfile.csv 有什么想法吗?使用awk可以: kent$ cat f foo,foo,"x,y,z",foo,"a,b,c"

我有一个充满行的文本文件,看起来像:

Female,"$0 to $25,000",Arlington Heights,0,60462,ZD111326,9/18/13 0:21,Disk Drive
我正在尝试将所有逗号
更改为管道
|
,引号中的逗号除外。 尝试使用sed(我是新手)。。。而且它不起作用。使用:

sed '/".*"/!s/\,/|/g' textfile.csv

有什么想法吗?

使用awk可以:

kent$  cat f
foo,foo,"x,y,z",foo,"a,b,c",foo,"yes,no"
Female,"$0 to $25,000",Arlington Heights,0,60462,ZD111326,9/18/13 0:21,Disk Drive

kent$  awk -F'"' -v OFS='"' '{for(i=1;i<=NF;i++)if(i%2)gsub(",","|",$i)}7' f
foo|foo|"x,y,z"|foo|"a,b,c"|foo|"yes,no"
Female|"$0 to $25,000"|Arlington Heights|0|60462|ZD111326|9/18/13 0:21|Disk Drive
kent$f类
foo,foo,“x,y,z”,foo,“a,b,c”,foo,“是,不是”
女性,“0至25000美元”,阿灵顿高地,060462,ZD111326,9/18/13 0:21,磁盘驱动器

肯特$ AWK-F′- v OFS='' { for(i=1;i作为测试实例),考虑此文件:

Female,"$0 to $25,000",Arlington Heights,0,60462,ZD111326,9/18/13 0:21,Disk Drive
foo,foo,"x,y,z",foo,"a,b,c",foo,"yes,no"
"x,y,z",foo,"a,b,c",foo,"yes,no",foo
下面是一个
sed
命令,用于用管道符号替换不带引号的逗号:

$ sed -r ':a; s/^([^"]*("[^"]*"[^"]*)*),/\1|/g; t a' file 
Female|"$0 to $25,000"|Arlington Heights|0|60462|ZD111326|9/18/13 0:21|Disk Drive
foo|foo|"x,y,z"|foo|"a,b,c"|foo|"yes,no"
"x,y,z"|foo|"a,b,c"|foo|"yes,no"|foo
解释 这将查找双引号对后出现的逗号,并用管道符号替换它们

  • :a

    这定义了一个标签
    a

  • s/^([^”]*(“[^”]*“[^”]*)*),/\1 |/g

    如果行中的逗号前面有0、2、4或任意偶数个引号,则用管道符号替换该逗号

    • ^

      这与行的开头相匹配

    • (`

      这将启动主分组(
      \1

    • [^”]*

      这将查找零个或多个非引号字符

    • (“[^”]*“[^”]*)*

      在括号外的
      *
      表示我们在括号内寻找零个或多个模式。括号内的模式由引号、任意数量的非引号、引号和非引号上的任意数字组成

      换句话说,此分组仅匹配引号对。由于括号外的
      *
      ,它可以匹配任意偶数个引号

    • 这将关闭主分组

    • 这要求分组后面必须跟一个逗号

  • ta

    如果先前的
    s
    命令成功进行了替换,则
    test
    命令将告诉
    sed
    跳回标签
    a
    ,然后重试

    如果没有替换,那么我们就完了


我建议使用带有适当CSV解析器的语言。例如:

ruby -rcsv -ne 'puts CSV.generate_line(CSV.parse_line($_), :col_sep=>"|")' file

在这里,我会使用
gnu awks
FPAT
。它定义一个字段的外观,告诉您分隔符是什么。然后您可以将输出分隔符设置为
|

awk '{$1=$1}1' OFS=\| FPAT="([^,]+)|(\"[^\"]+\")" file
Female|"$0 to $25,000"|Arlington Heights|0|60462|ZD111326|9/18/13 0:21|Disk Drive

如果您的
awk
不支持
FPAT
,可以使用:

awk -F, '{for (i=1;i<NF;i++) {c+=gsub(/\"/,"&",$i);printf "%s"(c%2?FS:"|"),$i}print $NF}' file
Female|"$0 to $25,000"|Arlington Heights|0|60462|ZD111326|9/18/13 0:21|Disk Drive
awk-F'{for(i=1;i

这将匹配引号中的文本并为逗号放置一个占位符,然后将所有其他逗号切换为管道并将占位符重新放置为逗号。您可以将##HOLD##文本更改为您想要的任何文本。

修订:我认为否定模式会导致跳过整行。这可能吗?您是正确的,sed正在运行所以当它找到你的正则表达式时,它会匹配这一行,当你否定它时,它会删除整行。还有,为什么是“7”在for循环结束时?@Sam用于获取打印输出。它可以是任何数字,如
1
8
。然后它将执行默认操作,
print
做得好,我从来没有想过嵌套保留空间,我想sed就是用于此的..=)
awk -F, '{for (i=1;i<NF;i++) {c+=gsub(/\"/,"&",$i);printf "%s"(c%2?FS:"|"),$i}print $NF}' file
Female|"$0 to $25,000"|Arlington Heights|0|60462|ZD111326|9/18/13 0:21|Disk Drive
sed 's/"\(.*\),\(.*\)"/"\1##HOLD##\2"/g;s/,/|/g;s/##HOLD##/,/g'