我正在尝试创建一个PCRE,它只匹配CSV文件中一行中用作分隔符的逗号。假设一行的格式为:
1,"abcd",2,"de,fg",3,"hijk"
我希望匹配除“e”和“f”之间的逗号之外的所有逗号。或者,如果这是更容易或更明智的解决方案,那么仅匹配那个是可接受的。我有一种感觉,我需要使用负前瞻断言来处理这个问题,但我发现它有点难以弄明白。
答案 0 :(得分:9)
有关详细信息,请参阅我的post that solves this problem。
^(?:(?:"((?:""|[^"])+)"|([^,]*))(?:$|,))+$
将匹配整行,然后您可以使用match.Groups [1] .Captures来获取数据(不带引号)。另外,我让“我的名字是”“在引号”“”是一个有效的字符串。
答案 1 :(得分:6)
CSV解析是一个难题,并且已经很好地解决了。无论你使用什么语言都无疑有一个完整的解决方案来处理它,而你不必走自己写的自己的正则表达式。
您使用的是哪种语言?
答案 2 :(得分:5)
正如你已经被告知的那样,正则表达式真的不合适;处理一般情况很棘手(如果在字段中允许换行则加倍,如果你可能不得不处理格式错误的CSV数据,那么就会很复杂。
要查看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 只是一个全局修饰符。它甚至可以使用混乱的线条(额外的引号),但不处理空白。
只是分享,也许这会对某人有帮助。