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

标签 perl parsing csv awk

背景 - 我想从 csv 文件中提取特定的列。 csv 文件以逗号分隔,使用双引号作为文本限定符(可选,但当字段包含特殊字符时,限定符将在那里 - 请参见示例),并使用反斜杠作为转义字符。某些字段也可能为空。

示例输入和所需输出 - 例如,我只希望第 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,"F",
"Lee, Jack",22,123 Sesame St,"","M",D

期望输出
"John \"Super\" Doe","123 ABC Street",123-456-7890
"Jane, Mary",132 CBS Street,333-111-5332
"Smith \"Jr.\", Jane",,555-876-1233
"Lee, Jack",123 Sesame St,""

初步脚本 (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","123 ABC Street",123-456-7890
"Jane, Mary","132 CBS Street",333-111-5332
"Smith \"Jr.\", Jane",,555-876-1233
"Lee, Jack","123 Sesame St",

它在没有引号的地址周围添加引号,但由于某些地址周围已经有引号,因此您显然可以处理。

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

输出:
"John \"Super\" Doe","123 ABC Street",123-456-7890
"Jane, Mary",132 CBS Street,333-111-5332
"Smith \"Jr.\", Jane",,555-876-1233
"Lee, Jack",123 Sesame St,""

关于perl - 使用 awk 或 perl 从 CSV 中提取特定列(解析),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9287770/

相关文章:

python - 在python中解析具有多个根元素的xml文件

excel - 跳过 "reading"之前的 6 行进入电源查询

regex - 这个 perl 正则表达式匹配什么?

linux - Perl Net::FTP 得到错误错误的远程文件名 'ex.pl

parsing - HTML Agility Pack xPath问题

xml - 从维基百科页面获取 xml

python - 日期分类

python pandas - 应用于 csv 的函数不持久

arrays - 这是 CPAN Uniq 模块中的错误吗?

perl - 需要帮助理解/d 的 perl tr 命令