2010-05-14 227 views
1

通常我只是使用TStringList.CommaText,但是當給定字段有多行時,這不會工作。基本上我需要一個符合rfc4180的csv處理器。我寧願不必自己實施RFC。在Delphi中使用多行記錄讀取一個CSV文件

+0

CSV是一個噩夢,因爲有這麼多的變種(其中大部分不符合RFC)。 XML是您的選擇嗎? – 2010-05-14 06:03:26

回答

1

你真的需要完整的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; 
} 
+0

您的「quotesplit」建議就是我所使用的(我剛剛閱讀您的帖子時已完成測試)。基本上我確保每行有偶數的引號,如果不是,我將下一行作爲同一記錄的一部分進行處理。 – Alister 2010-05-14 01:55:13

+0

@alister我的解決方案不需要「偶數的引號」,但它絕對可以被增強。如果你可以閱讀Perl,它可能是有用的,但也許只是這個想法會有所幫助。祝你好運。 – dlamotte 2010-05-14 02:47:37

+0

即使引號解決方案工作完美 - 雖然我確實需要首先快速解析轉義雙引號,這是「\」而不是正確的「」(雙引號),並可能導致解析問題。在RFC中,它將表明它確保每個記錄有雙引號,但由於CSV的不同實現數量,我懷疑這可能有點假定 – Alister 2010-05-14 03:27:12

0

沒有你試圖用分隔符: =';'和DelimiterText:=改爲CommaText?

順便說一句,即RFC已經沒有任何意義......這是荒謬的請求有關CSV評論...

+0

問題是每條記錄有多行 – Alister 2010-05-14 01:42:52

+1

閱讀Wiki上的條目:http://en.wikipedia.org/wiki/Request_for_Comments儘管RFC有不同的用途今天它已經不適合了,而且這當然遠非荒謬或無意義 - 這需要一個規範的格式描述*某處*。 – mghie 2010-05-14 06:30:19

0

這裏是我的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;