【问题标题】:Grid out of range error when importing CSV file导入 CSV 文件时出现网格超出范围错误
【发布时间】:2014-07-12 22:54:55
【问题描述】:

我正在关注this tutorial 将 CSV 文件导入 Delphi。我起草了下面提供的代码。程序编译没有问题,但是当我尝试执行函数来读取文件时,我得到 grid out of range 错误消息。

unit geoimp;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.Buttons, Vcl.StdCtrls,
  Vcl.Grids, Vcl.DBGrids, Data.DB, Datasnap.DBClient;


 const
  shfolder = 'ShFolder.dll';

type
  TMainForm = class(TForm)
    MainPageControl: TPageControl;
    ImportTab: TTabSheet;
    MapPreviewTab: TTabSheet;
    GeoMatchingTab: TTabSheet;
    ImportLbl: TLabel;
    SlctImportDta: TSpeedButton;
    MainOpenDialog: TOpenDialog;
    MainListBox: TListBox;
    SG1: TStringGrid;
    procedure SlctImportDtaClick(Sender: TObject);

  private
    { Private declarations }
    procedure ParseRecord(sRecord: string; Row: integer);
     procedure ReadCSVFile;

  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}





procedure TMainForm.ParseRecord(sRecord: string; Row: integer);
var
  Col, PosComma: integer;
  sField: string;
begin
  sRecord := StringReplace(sRecord, '"', '',
                           [rfReplaceAll]    ); // 1.
  Col := 0;    // first column of stringgrid
  repeat
    PosComma := Pos(',', sRecord);              // 2.
    if PosComma > 0 then
      sField := Copy(sRecord, 1, PosComma - 1)  // 3.a
    else
      sField := sRecord;                        // 3.b
    SG1.Cells[Col, Row] := sField;              // 4.
    if PosComma > 0 then begin                  // 5.
      Delete(sRecord, 1, PosComma);
      Col := Col + 1;                           // next column
    end;
  until PosComma = 0;                           // 6.
end;

procedure TMainForm.ReadCSVFile;
var
  FileName1, sRecord: string;
  Row: integer;
begin
  FileName1 := MainOpenDialog.FileName;
  MainListBox.Items.LoadFromFile(FileName1);
  SG1.RowCount := MainListBox.Items.Count;

  for Row := 0 to MainListBox.Items.Count - 1 do begin
    sRecord := MainListBox.Items[Row];
    ParseRecord(sRecord, Row);
  end;

  // 5. Select first "data" cell
  SG1.Row := 1;
  SG1.Col := 0;
  SG1.SetFocus;

end;


procedure TMainForm.SlctImportDtaClick(Sender: TObject);
begin

  // Create the open dialog object - assign to our open dialog variable
  MainOpenDialog := TOpenDialog.Create(self);

  // Set up the starting directory to be the current one
  MainOpenDialog.InitialDir := GetCurrentDir;

  // Only allow existing files to be selected
  MainOpenDialog.Options := [ofFileMustExist];

  // Allow only .dpr and .pas files to be selected
  MainOpenDialog.Filter :=
    'CSV Files|*.csv';

  // Select pascal files as the starting filter type
  MainOpenDialog.FilterIndex := 2;

  // Display the open file dialog
  if MainOpenDialog.Execute
  then ReadCSVFile
  else ShowMessage('Open file was cancelled');

  // Free up the dialog
  MainOpenDialog.Free;
end;

end.

【问题讨论】:

  • 你把 StringGrid(行和列)做了多大? CSV 文件中有多少行和多少列数据?
  • @David Schwartz 给了你一个很好的答案。更一般地说,在编写代码时养成预测问题的习惯是一个好主意,例如“我的 Col 值是否会访问我的网格中的有效列,如果不能访问,我该怎么办。”你知道他们说什么:“期待意外。”
  • 通过使用StringReplace(..., [rfReplaceAll]) 来处理Char(和其他东西)来判断本教程是有缺陷的。你看到TStrings.CommaText了吗? (只记得StrictDelimiter

标签: class delphi csv import tstringgrid


【解决方案1】:

在没有看到数据的情况下,您通过删除输入字符串中的引号做出了一些危险的假设。在 CSV 文件的带引号的字符串中嵌入逗号是完全有效的——事实上,这就是它们允许带引号的字符串的原因。您只需要在一条记录中嵌入一个逗号,如果您没有定义足够多的列,它就会爆炸。

您没有展示如何设置网格的 ColCount。默认情况下,它设置为 5。

FixedRows/FixedColumns 值也需要包含在 RowCount/ColCount 中。

您可以在第 6 行上方插入:

if (col >= (SG1.ColCount+SG1.FixedColumns)) then
  SG1.ColCount := SG1.ColCount + 1;

这将增加网格中的列数,并允许您查看嵌入在 CSV 数据中带引号的字符串中的任何错误逗号的结果。

在ReadCSVFile的第3行设置SG1.RowCount,如果SG1.FixedRows > 0则为短。

这些都是您遇到异常的可能原因。

【讨论】:

    猜你喜欢
    • 2020-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多