使用Perl解析包含嵌套逗号的CSV文件

3
我正在解析一份包含逗号的CSV文件,显然,使用split()方法会由于这个原因有一些限制。
我应该注意的一件事是,带有逗号的值用括号、双引号或两者都包围着...
例如:
(Date, Notional), "Date, Notional", "(Date, Notional)"
此外,我正在尝试在不使用任何模块的情况下完成它,原因我现在不想深入讨论...
有人能帮我解决这个问题吗?
3个回答

3
这应该能满足您的需求。它的工作方式与Text::CSV_PP中的代码非常相似,但不允许在字段内使用转义字符,因为您说您没有这样的字符。
use strict;
use warnings;
use 5.010;

my $re = qr/(?| "\( ( [^()""]* ) \)" |  \( ( [^()]* ) \) |  " ( [^"]* ) " |  ( [^,]* ) ) , \s* /x;

my $line = '(Date, Notional 1), "Date, Notional 2", "(Date, Notional 3)"';

my @fields = "$line," =~ /$re/g;

say "<$_>" for @fields;

输出

<Date, Notional 1>
<Date, Notional 2>
<Date, Notional 3>

更新

这里提供适用于旧版本Perl(10版之前)的版本,它没有正则表达式分支重置结构。它产生与上述相同的输出。

use strict;
use warnings;
use 5.010;

my $re = qr/(?: "\( ( [^()""]* ) \)" |  \( ( [^()]* ) \) |  " ( [^"]* ) " |  ( [^,]* ) ) , \s* /x;

my $line = '(Date, Notional 1), "Date, Notional 2", "(Date, Notional 3)"';

my @fields = grep defined, "$line," =~ /$re/g;

say "<$_>" for @fields;

3
我知道你已经有了一个使用Borodin的答案的工作解决方案,但是为了记录,使用split也有一个简单的解决方案(请参见在线演示底部的结果)。这种情况听起来非常类似于正则表达式匹配模式除非...
#!/usr/bin/perl
$regex = '(?:\([^\)]*\)|"[^"]*")(*SKIP)(*F)|\s*,\s*';
$subject = '(Date, Notional), "Date, Notional", "(Date, Notional)"';
@splits = split($regex, $subject);
print "\n*** Splits ***\n";
foreach(@splits) { print "$_\n"; } 

工作原理

竖线 | 的左侧匹配完整的 (括号)(引号),然后故意失败。右侧匹配逗号,我们知道它们是正确的逗号,因为它们没有被左侧表达式匹配。

可能的改进

如果需要,可以将括号匹配部分递归,以匹配 (嵌套的括号)

参考资料

如何在情况s1、s2、s3...中匹配(或替换)模式


0

我知道这是相当老的问题,但为了完整性,我想添加来自 Jeffrey Friedl 的伟大书籍《精通正则表达式》(第271页)的解决方案:

sub parse_csv {
    my $text = shift; # record containing comma-separated values
    my @fields = ( );
    my $field;
 
    chomp($text);

    while ($text =~ m{\G(?:^|,)(?:"((?>[^"]*)(?:""[^"]*)*)"|([^",]*))}gx) {
        if (defined $2) {
            $field = $2;
        } else {
            $field = $1;
            $field =~ s/""/"/g;
        }
#        print "[$field]";
        push @fields, $field;
    }
    return @fields;
}

尝试对测试行进行操作:

    my $line = q(Ten Thousand,10000, 2710 ,,"10,000",,"It's ""10 Grand"", baby",10K);
    my @fields = parse_csv($line);
    my $i;

    for ($i = 0; $i < @fields; $i++) {
         print "$fields[$i],";
    }
    print "\n";

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接