处理 CSV 文件中字符串值中的逗号

处理 CSV 文件中字符串值中的逗号

我有一个以逗号分隔的文件,其中包含数字列和字符串列。字符串列被引用,并且引号之间可以有逗号。如何识别带有 的列FS =","

样本记录

"prabhat,kumar",19,2000,"bangalore,India"

AWK它应该是

$1 = "prabhat,kumar"
$2 = 19
$3 = "2000"
$4 = "bangalore,india"

设置FS=","正在创造问题。

输入是:

"prabhat,kumar",19,2000,"bangalore,India","ABC,DEF","GHI",123,"KLM","NOP,QRS"
"prabhat,kumar",19,2000,"bangalore,India","ABC,DEF","GHI",123,"KLM","NOP,QRS"

输出应该是:

"prabhat,kumar"|19|2000|"bangalore,India"|"ABC,DEF"|"GHI"|123|"KLM"|"NOP,QRS"
"prabhat,kumar"|19|2000|"bangalore,India"|"ABC,DEF"|"GHI"|123|"KLM"|"NOP,QRS"

我正在尝试的代码:

awk -F"," '{for(i=1;i<=NF;i++){if(i%NF==0){ORS="\n"} {if($i ~ /^\"/ || $i ~ /\"$/) {a=a OFS $i;j++;{if(j%2==0){sub(/^\,/,X,a); print a;j=0;a=""}}} else {print $i}}} {ORS="|"}}' ORS="|" OFS=, p.txt

答案1

首先,您应该使用合适的 CSV 解析器。例如,在 Perl 中,您可以使用Text::CSV

  1. 安装cpanm(如果你使用 Perl,稍后你会感谢我的)

    $ sudo apt-get install cpanminus
    

    如果您使用的不是基于 Debian 的系统,您应该能够使用您的发行版的包管理器来安装它。

  2. 安装Text::CSV模块

    $ sudo cpanm Text::CSV
    
  3. 解析你的文件

    $ perl -MText::CSV -le '
        $csv = Text::CSV->new({binary=>1}); 
        while ($row = $csv->getline(STDIN)){
        print "1:$row->[0], 2:$row->[1], 3:$row->[2], 4:$row->[3]"}' < file.csv 
    1:prabhat,kumar, 2:19, 3:2000, 4:bangalore,India
    

    正如您在上面看到的,第一个字段是$row->[0],第二个字段$row->[1]等等。


这是正确的方法。一个更简单但肮脏的技巧是将任何引用的逗号替换为另一个字符。然后,正常使用awk,最后再次将它们切换回逗号。我在这里使用###,但你可以使用任何你确信永远不会出现在你的领域之一的东西。

$ sed -r 's/("[^",]+),([^",]+")/\1###\2/g' file.csv | 
    awk -F, '{print $1,$3}' | sed 's/###/,/g'
"prabhat,kumar" 2000

答案2

如果你有 GNU awk

$ awk -vFPAT='[^,]*|"[^"]*"' '{ gsub("^\"|\"$","",$1); gsub("^\"|\"$","",$4); print $1 $4} '
prabhat,kumarbangalore,India

输出格式有点难看,因为我只打印$1并且$4彼此相邻 - 我相信您可以根据自己的口味更改它。

如果您需要保留字段周围的双引号,请删除这两个gsub();函数。

解释:

通常,awk通过(字段分隔符)变量的内容分隔记录中的字段 FS,该变量默认为任何空白(制表符、空格和换行符)。分隔符告诉awk记录的结束位置。在csv文件中,记录以逗号结尾(传递给awkas -vFS=,),但是当然,在与您类似的示例中,这太简单了并且会损坏。

或者,FPAT(字段模式)定义 中的记录awk。您无需指定awk记录的结束位置,而是创建包含整个记录的定义。在你的例子的复杂中csv,这是[^,]*|"[^"]*"

细分如下:-

  • [^,]尽可能多地出现非逗号 ( ) 的字符( *)。两个逗号之间的所有内容都是一个字段。
  • 或者 (|
  • 一个单双引号 ( ") 后跟无双引号 ( [^"]) 的次数尽可能多 ( *) 后跟一个单双引号 ( ")。双引号内的所有内容(包括逗号)均算作一个字段。

答案3

Ruby 对于 CSV 解析很方便:

ruby -rcsv -ne 'puts CSV.generate_line(CSV.parse_line($_), :col_sep=>"|")' file
prabhat,kumar|19|2000|bangalore,India|ABC,DEF|GHI|123|KLM|NOP,QRS
prabhat,kumar|19|2000|bangalore,India|ABC,DEF|GHI|123|KLM|NOP,QRS

请注意,输出中没有引号。这是因为没有任何字段包含字段分隔符。如果需要引号,可以强制引用所有字段(甚至整数):

ruby -rcsv -ne 'puts CSV.generate_line(CSV.parse_line($_), :col_sep=>"|",:force_quotes=>true)' file
"prabhat,kumar"|"19"|"2000"|"bangalore,India"|"ABC,DEF"|"GHI"|"123"|"KLM"|"NOP,QRS"
"prabhat,kumar"|"19"|"2000"|"bangalore,India"|"ABC,DEF"|"GHI"|"123"|"KLM"|"NOP,QRS"

答案4

这对我有用:

$ echo '"prabhat,kumar",19,2000,"bangalore,India"' | 
  awk -F, '{print $1,$2,$3,$4,$5,$6}'| 
    awk -F\" '{print $2,$3,$4}'|awk -F\  '{print $1","$2,$3,$4,$5","$6}'`

相关内容