Pascal 中的 FileIO 问题

Trouble with FileIO in Pascal

我在为我的编程入门 class 中的最终项目创建工作保存系统时遇到了问题。我正在尝试将狗的记录保存在数组中。每只狗都有名字、主人姓名、品种、性别(1 和 2 与是和否相关)、绝育/绝育状态(同样 1 和 2 与是和否相关),以及一系列体重和 BCS 分数存储在记录中,所有这些都绘制在整个狗数组的单独索引中。我设置的系统完美地导入了一只狗,但是一旦我添加另一只狗被带到终端,我就会收到有关访问冲突的错误。我已经手动执行了很多次,但似乎找不到问题所在。我将在下面附上代码。谢谢!!!

type
DogInformation = record
    OwnerName: String;
    name: String;
    breed: String;
    gender: Integer;
    weight: array of Integer;
    spay_neut: Integer;
    bcs: array of Integer;

    end;

DogArray = array of DogInformation;

procedure SaveAllData(var dog: DogArray);
var
i, o, z: Integer;
StoredData: Text;
len: Integer;
begin   
    FileCreate('/Users/Nat/Desktop/data.txt');
    AssignFile(StoredData, '/Users/Nat/Desktop/data.txt');
    Reset(StoredData);
    Rewrite(StoredData);    
                for i := 0 to (High(dog) - 1) do
                    begin
                        WriteLn(StoredData, '!');
                        WriteLn(StoredData, dog[i].name);
                        WriteLn(StoredData, '@');
                        WriteLn(StoredData, dog[i].OwnerName);
                        WriteLn(StoredData, '#');
                        WriteLn(StoredData, dog[i].breed);
                        WriteLn(StoredData, '$');
                        WriteLn(StoredData, dog[i].gender);
                        WriteLn(StoredData, '%');
                        WriteLn(StoredData, dog[i].spay_neut);
                        for o := 0 to High(dog[i].weight) do
                            begin
                                WriteLn(StoredData, '^');
                                WriteLn(StoredData, dog[i].weight[o]);      
                            end;
                        for z := 0 to High(dog[i].bcs) do
                            begin
                                WriteLn(StoredData, '&');
                                WriteLn(StoredData, dog[i].bcs[z]);
                            end;
                        WriteLn(StoredData, '?');
                    end;
    len := (Length(dog) - 1);
    WriteLn(StoredData, '!');
    WriteLn(StoredData, dog[len].name);
    WriteLn(StoredData, '@');
    WriteLn(StoredData, dog[len].OwnerName);
    WriteLn(StoredData, '#');
    WriteLn(StoredData, dog[len].breed);
    WriteLn(StoredData, '$');
    WriteLn(StoredData, dog[len].gender);
    WriteLn(StoredData, '%');
    WriteLn(StoredData, dog[len].spay_neut);
    for o := 0 to High(dog[len].weight) do
        begin
            WriteLn(StoredData, '^');
            WriteLn(StoredData, dog[len].weight[o]);        
        end;
    for z := 0 to High(dog[len].bcs) do
        begin
            WriteLn(StoredData, '&');
            WriteLn(StoredData, dog[len].bcs[z]);
        end;
    WriteLn(StoredData, '~');

    Close(StoredData);
end;


procedure LoadAllData(var dog: DogArray);
var
ident: String;
i, o, z: Integer;
StoredData: Text;
skip: String;
begin
    AssignFile(StoredData, '/Users/Nat/Desktop/data.txt');
    Reset(StoredData);
    i := 0;
    o := 0;
    z := 0;

    SetLength(dog,1);
    SetLength(dog[i].weight,0);
    SetLength(dog[i].bcs,0);

    repeat
        begin
            ReadLn(StoredData, ident);          
            if ident = '!' then
                begin
                    WriteLn('Importing Dogs Name.......');
                    ReadLn(StoredData, dog[i].name);
                    WriteLn(dog[i].name);
                end;    

            if ident = '?' then
                begin
                    SetLength(dog, (Length(dog))+1);
                    i := i + 1;
                end;

            if ident = '@' then
                begin
                    WriteLn('Importing Owner Name......');
                    ReadLn(StoredData, dog[i].OwnerName);
                    WriteLn(dog[i].OwnerName);
                end;

            if ident = '#' then
                begin
                    WriteLn('Importing Breed...........');
                    ReadLn(StoredData, dog[i].breed);
                    WriteLn(dog[i].breed);
                end;

            if ident = '$' then
                begin
                    WriteLn('Importing Gender..........');
                    ReadLn(StoredData, dog[i].gender);
                    WriteLn(dog[i].gender);
                end;

            if ident = '%' then
                begin
                    WriteLn('Importing Spay/Neut.......');
                    ReadLn(StoredData, dog[i].spay_neut);
                    WriteLn(dog[i].spay_neut);
                end;

            if ident = '^' then
                begin
                    WriteLn('Importing Weights.........');
                    SetLength(dog[i].weight, (Length(dog[i].weight))+1);
                    ReadLn(StoredData, (dog[i].weight[o]));
                    WriteLn(dog[i].weight[o], ' ');
                    o := o + 1;
                end;

            if ident = '&' then
                begin
                    WriteLn('Importing BCS.............');
                    SetLength(dog[i].bcs, (Length(dog[i].bcs))+1);
                    ReadLn(StoredData, (dog[i].bcs[z]));
                    WriteLn(dog[i].bcs[z], ' ');
                    z := z + 1;
                end;
        end;
    until ident = '~';
    WriteLn('');
    WriteLn('Import Complete...........');
    Close(StoredData);
end;

任何信息或建议都会有所帮助!

谢谢,纳特

编辑 - 这是第二只狗在终端中的输出。除了 dogs Weight 和 BCS 之外的所有内容都出来了,然后当我尝试通过添加新狗来编辑 dog 数组时,程序以访问冲突为由退出。

Data.txt Output Terminal Output

您需要在循环的每次迭代中仔细重新初始化加载过程中的变量:

  i := 0;
  repeat
    o := 0;
    z := 0;
    SetLength(dog, i+1);
    SetLength(dog[i].weight, 0);
    SetLength(dog[i].bcs, 0);

    ...

    inc(i);
  until...

否则 oz 变量递增到高于 weight 和 bcz 数组上边界的值。

我决定也写一个答案。我已经给出了一个答案,说明了您问题的确切解决方案。但是我觉得我需要解释一下,您使用的方法从一开始就不是最佳的……这不是使用 Pascal 方言的方式。

让我们开始吧。

首先。 Pascal 开发人员通常在用户定义类型的名称前使用 T:我重命名了记录声明。这是声明。

type
  TDogInformation = record
    OwnerName: String;
    // other fields...
  end;

下一步。我声明了以下变量:

var
  fl: file;
  RecSize: word;      // This variable will be used for fast data access
  DI: array of TDogInformation;
  i: word;

我以这种方式填充数据,您可以使用任何其他方式。

  Setlength(DI, 2);
  with DI[0] do
  begin
    OwnerName := 'First owner';
    name := 'First dog';
    breed := 'Mixed';
    gender := 0;
    Setlength(weight, 2);
    weight[0] := 56;
    weight[1] := 60;
    spay_neut := 1;
    Setlength(bcs, 1);
    bcs[0] := 15;
  end;
  with DI[1] do
  begin
    OwnerName := 'Second owner';
    name := 'Second dog';
    breed := 'Mixed mixed';
    gender := 1;
    Setlength(weight, 1);
    weight[0] := 64;
    spay_neut := 1;
    Setlength(bcs, 12);
    bcs[0] := 16;
    bcs[1] := 18;
  end;

现在我们进行数据的快速写入。整个过程只需要几行代码。

  AssignFile(fl, 'b:\asd.txt');
  ReWrite(fl);
  for i := 0 to High(DI) do
  begin
    RecSize := SizeOf(DI[i]);   //<--We calculate and keep the buffer size 
    BlockWrite(fl, RecSize, 2); //<--and write it in the file to simlify reading
    BlockWrite(fl, DI[i], RecSize);//<--Now we write the record at once.
  end;
  CloseFile(fl);

下面是快速阅读。

  AssignFile(fl, 'b:\asd.txt');
  SetLength(DI, 0);//line is compulsory. We reinitialize DI
  Reset(fl);
  i := 0;
  repeat
    Setlength(DI, i+1);
    BlockRead(fl, RecSize, SizeOf(RecSize) { =2 as it is a word });
    //^ You will get the record size by reading the word variable first
    BlockRead(fl, DI[i], RecSize); //Now read the record using its fetched size
    WriteLn(DI[i].OwnerName);//output the records' data
    Inc(i);
  until Eof(fl);
  CloseFile(fl);
  ReadLn;

就是这样。您确实需要解析文本数据。最后。有一些技术允许一次写入整个集合(例如,使用 TStream/TMemoryStream/TFileStream)而无需任何解析 and/or setting/fetching 单个记录的长度。