代码之家  ›  专栏  ›  技术社区  ›  Alister

在Delphi中读取带有多行记录的csv文件

  •  1
  • Alister  · 技术社区  · 14 年前

    通常我只使用tstringlist.commatext,但是当一个给定的字段有多行时,这不起作用。基本上我需要一个符合 rfc4180 . 我不想自己实现RFC。

    3 回复  |  直到 14 年前
        1
  •  1
  •   dlamotte    14 年前

    您真的需要完整的RFC支持吗?我无法计算用Perl或类似语言编写“csv解析器”的次数。在逗号上拆分并完成。唯一的问题是当你需要尊重引用的时候。如果你这样做了,写一个“QuoteSplit”例程,查找引号并确保它们是平衡的。除非这个csv处理器是某些应用程序的主要部分,否则我不确定它是否真的会有问题。

    另一方面,我真的不认为完全实现RFC是如此复杂。这是一个相对较短的RFC相比,像…http,smtp,imap,…

    在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;
    }
    
        2
  •  0
  •   111    14 年前

    是否尝试使用分隔符:=';'和分隔符文本:=而不是逗号文本?

    顺便说一句,RFC完全没有意义…请求对csv发表评论是荒谬的…

        3
  •  0
  •   Brian Frost    14 年前

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