【问题标题】:Access violation while using "record" type in delphi7在delphi7中使用“记录”类型时访问冲突
【发布时间】:2017-01-06 07:26:11
【问题描述】:

我需要做的是从 excel 文件中读取表格详细信息,并希望从中创建 .pas 文件。

出于读写目的,我在 delphi 7 中使用了 record 类型。

这是我到目前为止尝试过的代码:

unit fImportFile;
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    tSourceFile: TEdit;
    dlgSourceFile: TOpenDialog;
    btnImport: TButton;
    procedure tSourceFileClick(Sender: TObject);
    procedure btnImportClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TTableDetails = record
    fTableName: String;
  end;

  TFieldDetails = record
    fFieldName: String;
    fType: String;
    fShortAlias: String;
    fLongAlias: String;
    fDomainName: String;
    fFieldAttributes:TStringList;
    fComments: String;
  end;

var
  Form1: TForm1;

implementation

uses ComObj, uFileGeneration;

{$R *.dfm}

procedure TForm1.tSourceFileClick(Sender: TObject);
begin
  with dlgSourceFile do
  begin
    FileName := tSourceFile.Text;
    if ExtractFilePath(FileName) <> '' then
      InitialDir := ExtractFilePath(FileName);
    if Execute then
      if FileName <> '' then
        tSourceFile.Text := FileName;
  end;

end;

procedure TForm1.btnImportClick(Sender: TObject);
const
  cEndOfTables = '';
  cTable = 'Table';
  cTableCell = 2;
  cTableNameCell = 3; // TableNameField
  cFieldName = 'Field Name';
var
  Excel: OleVariant;
  iRow: Integer;
  aTableDetails: TTableDetails;
  aFieldDetails: Array of TFieldDetails;
  fieldCount: Integer;
  iTableName, FldWithChar, FldWithoutChar: String;
  FldList, WordList: TStringList;
  FieldName: String;
begin
  FldList := nil;
  WordList := nil;

  try
    Excel := CreateOleObject('Excel.Application');
    Excel.Visible := False;
    Excel.Workbooks.Open(tSourceFile.Text);
    iRow := 1;

    while (Excel.ActiveSheet.Cells[iRow,cTableCell].Value <> '') do //To exit loop when the excel record will read blank TableName
    begin
      if (Excel.ActiveSheet.Cells[iRow,cTableCell].Value = cTable) then
      begin
        iTableName := Excel.ActiveSheet.Cells[iRow,cTableNameCell].Value;
        if (iTableName = '') then
        begin
          ShowMessage('Table Name cannot be blank.');
          exit;
        end;
        aTableDetails.fTableName := iTableName;
        Inc(iRow);

        fieldCount := 0;
        FldList := TStringList.Create;
        WordList := TStringList.Create;
        ShowMessage('1 -- iRow --> ' + IntToStr(iRow));

        while (Excel.ActiveSheet.Cells[iRow,cTableCell].Value <> '') AND
          (Excel.ActiveSheet.Cells[iRow,cTableCell].Value <> cTable) do //Will create record until another table will found
        begin
          ShowMessage('2 -- Excel.ActiveSheet.Cells[iRow,cTableNameCell].Value = ' + Excel.ActiveSheet.Cells[iRow,cTableNameCell].Value);
          FieldName := Excel.ActiveSheet.Cells[iRow,cTableNameCell].Value;
          aFieldDetails[fieldCount].fFieldName := FieldName; //ERROR LINE

          ShowMessage('3 -- iRow --> ' + IntToStr(iRow));
          FldWithChar := aFieldDetails[fieldCount].fFieldName;
          FldWithoutChar := NameWithoutAnyChar(FldWithChar, WordList);
          FldList.Add(FldWithChar + '=' + FldWithoutChar);
          WordList.Clear;
          ShowMessage('4 -- iRow --> ' + IntToStr(iRow));

          if (aFieldDetails[fieldCount].fFieldName = '') then
          begin
            ShowMessage('Field Name cannot be blank. TableName: '+iTableName);
            exit;
          end;
          aFieldDetails[fieldCount].fType := Excel.ActiveSheet.Cells[iRow,3].Value;
          aFieldDetails[fieldCount].fShortAlias := Excel.ActiveSheet.Cells[iRow,4].Value;
          aFieldDetails[fieldCount].fLongAlias := Excel.ActiveSheet.Cells[iRow,5].Value;
          aFieldDetails[fieldCount].fDomainName := Excel.ActiveSheet.Cells[iRow,6].Value;
          aFieldDetails[fieldCount].fFieldAttributes.CommaText := Excel.ActiveSheet.Cells[iRow,7].Value;
          aFieldDetails[fieldCount].fComments := Excel.ActiveSheet.Cells[iRow,8].Value;
          Inc(fieldCount);
          Inc(iRow);
        end;
        //Once new table row will be fouund it will call below method to create a dataview file for current table
        ShowMessage('5 -- iRow --> ' + IntToStr(iRow));
        GenerateDataviewFiles(aTableDetails, aFieldDetails, FldList, fieldCount-1);
        ShowMessage('6 -- iRow --> ' + IntToStr(iRow));
      end; //End of If condition
    end; //End of outer most while loop
  finally
    FreeAndNil(WordList);
    FreeAndNil(FldList);
    Excel.Workbooks.Close;
    Excel.Quit;
    Excel := Unassigned;
  end;
end;
end.

我已在 //ERROR LINE 中评论了访问冲突的错误。

我在这里所做的是,创建 TFieldDetails 数组,并希望在另一个文件中循环遍历它。

请帮我解决这个问题,因为我是 delphi 的新手。

【问题讨论】:

    标签: delphi delphi-7


    【解决方案1】:

    您忘记初始化动态数组aFieldDetails。因此,当您尝试使用索引fieldCount 写入它时,它的长度仍然为零。

    如果你知道字段的总数,你应该通过调用预分配整个数组

    SetLength(aFieldDetails, TheTotalFieldCount);
    

    如果您事先不知道总数,您可以使用列表或其他动态数据结构,因为每次添加项目时重新分配在性能方面非常昂贵。

    aFieldDetailList := TList.Create;
    try
      while (Excel.ActiveSheet.Cells[iRow,cTableCell].Value <> '') AND
            (Excel.ActiveSheet.Cells[iRow,cTableCell].Value <> cTable) do //Will create record until another table will found
      begin
        New(aFieldDetail); // create a new instance of aFieldDetail; aFieldDetail is of pointer type "^TFieldDetails"
    
        // do your loop work
      end;
      // do your after-loop work
    finally
      // free all allocated memory
      for aFieldDetail in aFieldDetailList do
        Dispose(aFieldDetail);
      FreeAndNil(aFieldDetailList);
    end;
    

    【讨论】:

      【解决方案2】:

      问题是您试图访问超出范围的aFieldDetails 索引。您应该在此之前设置动态数组的长度。像这样:

      ...
      SetLength(aFieldDetails, fieldCount+1);
      aFieldDetails[fieldCount].fFieldName := FieldName; //ERROR LINE
      ...
      

      但是,除非您知道从一开始就有多少个总字段,否则它的性能很差。这是因为每次调用SetLength 时,都会分配另一块内存并将整个数组复制到其中。

      我建议您使用TList,即使您不知道要向列表中添加多少项目,它也会尝试保持性能良好。

      【讨论】:

      • 是的,它奏效了。谢谢。但是这里有一个蹩脚的问题,因为我们在 while 循环中使用SetLength(aFieldDetails, fieldCount+1);,它不会花费执行时间吗?因为我们每次分配字段详细信息时都会设置长度。
      • @user3085495:是的,它会降低性能(有时会导致内存碎片)。另一种方法是将数组预分配给比您需要的更高的数字,跟踪您实际使用的元素数量,然后在末尾SetLength 记录使用的元素数量。您可以在第二个代码块in this answer 中看到这样做的示例;看向底部,您会看到 SetLength(Result, 26)。 (这是一个小例子,因为分配 26 个元素不足以影响性能,但这是一个例子。)
      猜你喜欢
      • 1970-01-01
      • 2011-07-04
      • 1970-01-01
      • 1970-01-01
      • 2010-11-19
      • 2016-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多