在Delphi中使用多行记录读取一个CSV文件
通常我只是使用TStringList.CommaText,但是当给定字段有多行时,这不会工作。基本上我需要一个符合rfc4180的csv处理器。我宁愿不必自己实施RFC。在Delphi中使用多行记录读取一个CSV文件
你真的需要完整的RFC支持吗?我无法计算我在perl或类似软件中编写“csv解析器”的次数。在逗号分割并完成。唯一的问题是,当你需要尊重报价。如果你这样做,写一个“quotesplit”例程,查找报价并确保它们是平衡的。除非这个csv处理器是某些应用程序的肉食和土豆,否则我不确定这真的会成为一个问题。
另一方面,我真的不认为完全实施rfc就是那么复杂。这是比较喜欢的东西...... HTTP,SMTP,IMAP,相对较短的RFC ......
在Perl中,一个体面的quotesplit()
我写的是:
sub quotesplit {
my ($regex, $s, $maxsplits) = @_;
my @split;
my $quotes = "\"'";
die("usage: quotesplit(qr/.../,'string...'), // instead of qr//?\n")
if scalar(@_) < 2;
my $lastpos;
while (1) {
my $pos = pos($s);
while ($s =~ m/($regex|(?<!\\)[$quotes])/g) {
if ($1 =~ m/[$quotes]/) {
$s =~ m/[^$quotes]*/g;
$s =~ m/(?<!\\)[$quotes]/g;
}
else {
push @split, substr($s,$pos,pos($s) - $pos - length($1));
last;
}
}
if (defined(pos($s)) and $lastpos > pos($s)) {
errorf('quotesplit() issue: lastpos %s > pos %s',
$lastpos, pos($s)
);
exit;
}
if ((defined($maxsplits) && scalar(@split) == ($maxsplits - 1))) {
push @split, substr($s,pos($s));
last;
}
elsif (not defined(pos($s))) {
push @split, substr($s,$lastpos);
last;
}
$lastpos = pos($s);
}
return @split;
}
您的“quotesplit”建议就是我所使用的(我刚刚阅读您的帖子时已完成测试)。基本上我确保每行有偶数的引号,如果不是,我将下一行作为同一记录的一部分进行处理。 – Alister 2010-05-14 01:55:13
@alister我的解决方案不需要“偶数的引号”,但它绝对可以被增强。如果你可以阅读Perl,它可能是有用的,但也许只是这个想法会有所帮助。祝你好运。 – dlamotte 2010-05-14 02:47:37
即使引号解决方案工作完美 - 虽然我确实需要首先快速解析转义双引号,这是“\”而不是正确的“”(双引号),并可能导致解析问题。在RFC中,它将表明它确保每个记录有双引号,但由于CSV的不同实现数量,我怀疑这可能有点假定 – Alister 2010-05-14 03:27:12
这里是我的CSV解析器(未也许到RFC,但它工作正常)。继续在提供的字符串上调用它,每次它给你下一个CSV字段。我不相信它有多行问题。
function CSVFieldToStr(
var AStr : string;
ADelimChar : char = Comma) : string;
{ Returns the next CSV field str from AStr, deleting it from AStr,
with delimiter }
var
bHasQuotes : boolean;
function HandleQuotes(const AStr : string) : string;
begin
Result := Trim(AStr);
If bHasQuotes then
begin
Result := StripQuotes(Result);
ReplaceAllSubStrs('""', '"', Result);
end;
end;
var
bInQuote : boolean;
I : integer;
C : char;
begin
bInQuote := False;
bHasQuotes := False;
For I := 1 to Length(AStr) do
begin
C := AStr[I];
If C = '"' then
begin
bHasQuotes := True;
bInQuote := not bInQuote;
end
else
If not bInQuote then
If C = ADelimChar then
begin
Result := HandleQuotes(Copy(AStr, 1, I-1));
AStr := Trim(Copy(AStr, I+1, MaxStrLEn));
Exit;
end;
end;
Result := HandleQuotes(AStr);
AStr := '';
end;
CSV是一个噩梦,因为有这么多的变种(其中大部分不符合RFC)。 XML是您的选择吗? – 2010-05-14 06:03:26