使用awk或perl从CSV中提取特定列(解析)

前端之家收集整理的这篇文章主要介绍了使用awk或perl从CSV中提取特定列(解析)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
背景 – 我想从csv文件提取特定列. csv文件以逗号分隔,使用双引号作为text-qualifier(可选,但是当字段包含特殊字符时,限定符将在那里 – 参见示例),并使用反斜杠作为转义字符.某些字段也可能为空白.

示例输入和所需输出 – 例如,我只希望列1,3和4位于输出文件中. csv文件中列的最终提取应与原始文件的格式匹配.不应删除任何转义字符或添加额外的引号等.

输入

"John \"Super\" Doe",25,"123 ABC Street",123-456-7890,"M",A
"Jane,Mary","",132 CBS Street,333-111-5332,"F",B
"Smith \"Jr.\",Jane",35,555-876-1233,"Lee,Jack",22,123 Sesame St,D

期望的输出

"John \"Super\" Doe",123-456-7890
"Jane,333-111-5332
"Smith \"Jr.\",555-876-1233
"Lee,""

初步脚本(awk) – 以下是我发现的大部分工作的初步脚本,但在我注意到的一个特定实例中不起作用,可能还有更多我尚未看到或想到的

#!/usr/xpg4/bin/awk -f

BEGIN{  OFS = FS = ","  }

/"/{
    for(i=1;i<=NF;i++){
        if($i ~ /^"[^"]+$/){
            for(x=i+1;x<=NF;x++){
                $i=$i","$x
                if($i ~ /"+$/){
                    z = x - (i + 1) + 1
                    for(y=i+1;y<=NF;y++)
                        $y = $(y + z)
                    break
                }
            }
            NF = NF - z
            i=x
        }
    }
print $1,$3,$4
}

以上似乎运行良好,直到遇到包含转义双引号和逗号的字段.在这种情况下,解析将关闭,输出将不正确.

问题/评论 – 我已经读过awk不是解析csv文件的最佳选择,建议使用perl.但是,我根本不懂perl.我找到了一些perl脚本的例子,但它们没有提供我想要的所需输出,我不知道如何根据我的需要轻松编辑脚本.

至于awk,我对它很熟悉并偶尔使用它的基本功能,但我不知道很多高级功能,比如上面脚本中使用的一些命令.只使用awk可以实现我想要的输出吗?如果是这样,是否可以编辑上面的脚本来解决我遇到的问题?有人可以逐行解释脚本究竟在做什么吗?

任何帮助将不胜感激,谢谢!

解决方法

我不会重新发明 wheel.
use Text::CSV_XS;

my $csv = Text::CSV_XS->new({
   binary      => 1,escape_char => '\\',eol         => "\n",});

my $fh_in  = \*STDIN;
my $fh_out = \*STDOUT;

while (my $row = $csv->getline($fh_in)) {
   $csv->print($fh_out,[ @{$row}[0,2,3] ])
      or die("".$csv->error_diag());
}

$csv->eof()
   or die("".$csv->error_diag());

输出

"John \"Super\" Doe","132 CBS Street","123 Sesame St",

它在没有任何地址的地址周围添加了引号,但由于某些地址已经有引号,你显然可以处理它.

重新发明轮子:

my $field = qr/"(?:[^"\\]|\\.)*"|[^"\\,]*/s;
while (<>) {
   my @fields = /^($field),$field,($field),/
      or die;
   print(join(',',@fields),"\n");
}

输出

"John \"Super\" Doe",""

猜你在找的Linux相关文章