You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将CSV加载到StringGrid时遇内存不足及空白列问题求助

问题分析与解决方案

核心问题诊断

1. 内存不足的原因

  • TStringList一次性加载整个大文件到内存,占用大量资源
  • 提前将StringGrid.RowCount设为文件总行数(包含空行),造成不必要的内存预分配
  • 频繁的Copy/Delete字符串操作生成大量临时对象,加剧内存消耗

2. 空白列的根源

  • WordCount函数逻辑错误,导致列数统计偏大
  • 行末尾无逗号时,循环提前结束,未填充的列残留空白

3. 空行处理问题

  • 直接用LFile.Count设置行总数,空行占用Grid行资源,还可能引发后续解析异常

修正后的完整代码

优化后的LoadCSVtoGrid过程

procedure x.LoadCSVtoGrid(ACSVFile: String; AStringGrid: TStringGrid);
var
  LRowIndex, LColIndex, LGridRow: Integer;
  LStrLine: string;
  LStream: TStreamReader;
  LMaxCols: Integer;
  LIsEmptyLine: Boolean;
begin
  AStringGrid.RowCount := 0;
  AStringGrid.ColCount := 0;
  if not FileExists(ACSVFile) then Exit;

  // 第一步:遍历文件统计最大列数,同时过滤空行
  LMaxCols := 0;
  LStream := TStreamReader.Create(ACSVFile);
  try
    while not LStream.EndOfStream do
    begin
      LStrLine := Trim(LStream.ReadLine);
      LIsEmptyLine := (LStrLine = '');
      if not LIsEmptyLine then
        LMaxCols := Max(LMaxCols, WordCount(LStrLine, [',', '"'], '"'));
    end;
  finally
    LStream.Free;
  end;

  if LMaxCols = 0 then Exit; // 全是空行的情况

  AStringGrid.ColCount := LMaxCols;
  AStringGrid.RowCount := 0; // 初始化为0,按需动态添加行

  // 第二步:逐行读取并解析到Grid
  LStream := TStreamReader.Create(ACSVFile);
  try
    LGridRow := 0;
    while not LStream.EndOfStream do
    begin
      LStrLine := LStream.ReadLine;
      LIsEmptyLine := (Trim(LStrLine) = '');
      if LIsEmptyLine then Continue; // 跳过空行

      // 动态添加行
      AStringGrid.RowCount := AStringGrid.RowCount + 1;
      LColIndex := 0;

      while LStrLine <> '' do
      begin
        if LColIndex >= AStringGrid.ColCount then Break; // 防止列越界

        if Pos('"', LStrLine) = 1 then
        begin
          Delete(LStrLine, 1, 1);
          // 处理转义引号("")
          if Pos('""', LStrLine) > 0 then
            LStrLine := StringReplace(LStrLine, '""', '"', [rfReplaceAll]);
          // 提取引号内内容并移除结束引号
          AStringGrid.Cells[LColIndex, LGridRow] := Copy(LStrLine, 1, Pos('"', LStrLine) - 1);
          Delete(LStrLine, 1, Pos('"', LStrLine));
          // 跳过引号后的逗号(如果存在)
          if LStrLine <> '' and (LStrLine[1] = ',') then
            Delete(LStrLine, 1, 1);
        end
        else
        begin
          // 处理非引号包裹的内容
          if Pos(',', LStrLine) > 0 then
          begin
            AStringGrid.Cells[LColIndex, LGridRow] := Copy(LStrLine, 1, Pos(',', LStrLine) - 1);
            Delete(LStrLine, 1, Pos(',', LStrLine));
          end
          else
          begin
            // 行末尾的最后一个字段
            AStringGrid.Cells[LColIndex, LGridRow] := LStrLine;
            LStrLine := '';
          end;
        end;
        Inc(LColIndex);
      end;

      // 清空当前行剩余列,消除空白列
      while LColIndex < AStringGrid.ColCount do
      begin
        AStringGrid.Cells[LColIndex, LGridRow] := '';
        Inc(LColIndex);
      end;

      Inc(LGridRow);
    end;
  finally
    LStream.Free;
  end;
end;

修正后的WordCount函数

function x.WordCount(const S: string; const WordDelims: TSysCharSet; const QuoteChar: Char): Integer;
var
  LInWord: Boolean;
  LQuoteOpen: Boolean;
  i: Integer;
begin
  Result := 0;
  LInWord := False;
  LQuoteOpen := False;
  for i := 1 to Length(S) do
  begin
    if S[i] = QuoteChar then
    begin
      LQuoteOpen := not LQuoteOpen;
      // 跳过转义的引号("")
      if (i < Length(S)) and (S[i+1] = QuoteChar) then
        Inc(i);
      Continue;
    end;

    if (S[i] in WordDelims) and not LQuoteOpen then
    begin
      if LInWord then
      begin
        Inc(Result);
        LInWord := False;
      end;
    end
    else
    begin
      if not LInWord then
      begin
        LInWord := True;
        // 初始化第一个字段的计数
        if Result = 0 then
          Result := 1;
      end;
    end;
  end;
  // 统计最后一个字段
  if LInWord and not LQuoteOpen then
  begin
    if Result = 0 then
      Result := 1
    else if not (S[Length(S)] in WordDelims) then
      Inc(Result);
  end;
end;

关键优化说明

  1. 内存优化

    • 用TStreamReader逐行读取文件,避免一次性加载大文件到内存
    • 动态添加Grid行,减少不必要的内存预分配
    • 优化字符串操作逻辑,减少临时对象生成
  2. 空白列修复

    • 修正WordCount的列数统计逻辑,准确计算字段数量
    • 解析完每行后清空剩余列,消除残留空白
    • 处理行末尾无逗号的场景,避免循环提前结束
  3. 空行处理

    • 遍历和解析阶段均跳过空行,不占用Grid资源
  4. CSV兼容性提升

    • 支持标准CSV的转义引号("")解析
    • 添加列越界保护,避免访问无效Grid列

内容的提问来源于stack exchange,提问作者Eddy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 06:35:20