正则表达式以匹配CSV分隔符

时间:2011-06-21 21:48:47

标签: regex

我正在尝试创建一个PCRE,它只匹配CSV文件中一行中用作分隔符的逗号。假设一行的格式为:

1,"abcd",2,"de,fg",3,"hijk"

我希望匹配除“e”和“f”之间的逗号之外的所有逗号。或者,如果这是更容易或更明智的解决方案,那么仅匹配那个是可接受的。我有一种感觉,我需要使用负前瞻断言来处理这个问题,但我发现它有点难以弄明白。

6 个答案:

答案 0 :(得分:9)

有关详细信息,请参阅我的post that solves this problem

^(?:(?:"((?:""|[^"])+)"|([^,]*))(?:$|,))+$将匹配整行,然后您可以使用match.Groups [1] .Captures来获取数据(不带引号)。另外,我让“我的名字是”“在引号”“”是一个有效的字符串。

答案 1 :(得分:6)

CSV解析是一个难题,并且已经很好地解决了。无论你使用什么语言都无疑有一个完整的解决方案来处理它,而你不必走自己写的自己的正则表达式。

您使用的是哪种语言?

答案 2 :(得分:5)

正如你已经被告知的那样,正则表达式真的不合适;处理一般情况很棘手(如果在字段中允许换行则加倍,如果你可能不得不处理格式错误的CSV数据,那么就会很复杂。

  • 我建议工具CSVFIX尽可能做你需要的工作。

要查看CSV有多糟糕,请考虑这些数据(包含5个清洁字段,其中两个为空):

"""",,"",a,"a,b"

请注意,第一个字段只包含一个双引号。将两个双引号压缩成一个是非常艰难的;在使用正则表达式捕获两者之后,你可能必须使用第二遍。并考虑这个不正确的数据:

"",,"",a",b c",

问题在于以a开头的字段包含双引号;怎么解释呢?停在逗号?然后,以b开头的字段同样是格式错误的。停在下一个报价?所以该字段为a",b c"(或者应该删除引号)?等等......呸!

这个Perl非常接近正确处理上面的数据行和可怕的正则表达式:

use strict;
use warnings;

my @list = ( q{"""",,"",a,"a,b"}, q{"",,"",a",b c",} );

foreach my $string (@list)
{
    print "Pattern: <<$string>>\n";
    while ($string =~ m/ (?: " ( (?:""|[^"])* ) "  |  ( [^,"] [^,]* )  |  ( .? ) )
                         (?: $ | , ) /gx)
    {
        print "Found QF: <<$1>>\n" if defined $1;
        print "Found PF: <<$2>>\n" if defined $2;
        print "Found EF: <<$3>>\n" if defined $3;
    }
}

请注意,如上所述,您必须确定实际使用的三个捕获中的哪一个。通过两个阶段处理,您可以处理一个捕获,然后删除封闭的双引号和嵌套的双引号双引号。这个正则表达式假定如果该字段不以双引号开头,那么双引号在该字段中没有特殊含义。玩得开心响应变化!

输出:

Pattern:  <<"""",,"",a,"a,b">>
Found QF: <<"">>
Found EF: <<>>
Found QF: <<>>
Found PF: <<a>>
Found QF: <<a,b>>
Found EF: <<>>
Pattern:  <<"",,"",a",b c",>>
Found QF: <<>>
Found EF: <<>>
Found QF: <<>>
Found PF: <<a">>
Found PF: <<b c">>
Found EF: <<>>

我们可以讨论第一个模式结束时的空场(EF)是否正确;它可能不是,这就是为什么我说'非常接近'。 OTOH,第二种模式结束时的EF是正确的。 此外,从字段""""中提取两个双引号并不是您想要的最终结果;你必须对该字段进行后处理,以消除每对相邻的双引号之一。

答案 3 :(得分:0)

在不考虑努力的情况下,我会做[0-9]+|"[^"]*"之类的事情,以匹配除逗号分隔符之外的所有。那会诀窍吗?

如果没有上下文,就无法提供更具体的解决方案。

答案 4 :(得分:0)

Andy说得对:正确解析CSV比你想象的要难得多,并且有各种丑陋的边缘情况。我怀疑在数学上不可能用正则表达式正确地解析CSV,特别是sed理解的那些。

使用使用the Text::CSV module from CPAN(或首选脚本语言中的等效语句)的Perl脚本而不是sed。这样的事情应该这样做:

use Text::CSV;
use feature 'say';

my $csv = Text::CSV->new ( { binary => 1, eol => $/ } )
    or die "Cannot use CSV: ".Text::CSV->error_diag ();
my $rows = $csv->getline_all(STDIN);
for my $row (@$rows) {
    say join("\t", @$row);
}

这假设您的数据中没有嵌入任何制表符,当然也许最好还是使用Real Scripting Language进行后续阶段,这样您就可以利用正确的列表了? / p>

答案 5 :(得分:0)

我知道这是旧的,但这个RegEx适合我:

/(\"[^\"]+\")|[^,]+/g

它可以用于任何语言。我在JavaScript中测试过它,所以 g 只是一个全局修饰符。它甚至可以使用混乱的线条(额外的引号),但不处理空白。

只是分享,也​​许这会对某人有帮助。