需要读取任何扩展名的文件,一次一个字节 XE5 到动态数组中

Need to read a file, of any extension one byte at a time XE5 into dynamic array

我已经尝试将文件读入 TFileStream 但那是我卡住的地方,文件被插入到 TFileStream 但我无法读取文件的字节,我有一段时间没有编程了,请帮助我。

我也试过把它读入普通文件

 var
   myFile    : File;
   byteArray : array of byte;
   oneByte   : byte;
   i, count  : Integer;

 begin
   // Try to open the Test.byt file for writing to
   AssignFile(myFile, 'C:\Users\theunie\Desktop\Toets\Test2.txt');

   // Reopen the file for reading only
   FileMode := fmOpenRead;
   Reset(myFile, 1);   // Now we define one record as 1 byte

   // Display the file contents
   // Start with a read of the first 6 bytes. 'count' is set to the
   // actual number read
   ShowMessage('Reading first set of bytes :');
   setlength(ByteArray,sizeof(myfile));
   BlockRead(myFile, byteArray, sizeof(myFile), count);

   // Display the byte values read
   for i := 0 to count do
     ShowMessage(IntToStr(byteArray[i]));

   // Now read one byte at a time to the end of the file
   ShowMessage('Reading remaining bytes :');
   while not Eof(myFile) do
   begin
     BlockRead(myFile, oneByte, 1);   // Read and display one byte at a time
     ShowMessage(IntToStr(oneByte));
   end;
   Freeandnil(byteArray);
   // Close the file for the last time
   CloseFile(myFile);
 end;

还有这个

  procedure TForm1.Button1Click(Sender: TObject);
var
tf  : TFileStream;  //My Filestream
ar  : array of byte;//The dynamic array I want to read it into
k   : integer;//count
s   : string;//I want to display this at the end
begin
k   := 0;
tf  := TFileStream.Create('C:\Users\Theunie\Desktop\Test2.txt',fmOpenReadwrite);
try
  inc(k);
  SetLength(ar,k);
  ar[k-1] := tf.Read(ar[k-1],tf.size);
finally
s   := inttostr(ar[0]) +';';
for k := 1 to length(ar) do
begin
  s := s + ';' + IntToStr(ar[k]);
end;
FreeAndNil(ar);
end;
RichEdit1.Lines.Add(s);
end;

文件很大吗?它可以一次放入 RAM 中吗?

您基本上有两个简单的选项可以从文件中创建 DynArray,但仅建议将它们用于中小型文件。

1: http://www.freepascal.org/docs-html/rtl/classes/tbytesstream.html

 var BS: TBytesStream; b: byte; L, i: integer;
 begin
   BS := TBytesStream.Create;
   try
     BS.LoadFromFile('c:\boot.ini');
     L := High(BS.Bytes);
     for i := 0 to L do begin
       b := BS.Bytes[i];
       ShowMessage( IntToStr( b ) );
     end;
   finally
     BS.Destroy;
   end;
 end;

2: 使用 IOUtils 类

等等

 var BS: TBytes; b: byte; L, i: integer;
 begin
   BS := TFile.ReadAllBytes('c:\boot.ini');
   L := High(BS);
   for i := 0 to L do begin
     b := BS[i];
     ShowMessage( IntToStr( b ) );
   end;
 end;

相反,要将数组的内容保存到文件中,您可以使用 How to convert TBytes to Binary File? (using MemoryStream)


关于您的尝试。

http://wiki.freepascal.org/File_Handling_In_Pascal

  1. 如前所述,SizeOf与文件无关,即File变量类型的内存大小。如果你想坚持使用旧的 TurboPascal API,那么你必须立即使用 FileSize 函数来设置大小。对于小文件,它可以正常工作,对于大文件,这种方法 "read it all to memory at once, then process" 是错误的。

  2. inc(k); SetLength(ar,k); - 在 +1 循环中 - 这是一个非常糟糕的主意,这意味着堆碎片和复制、重新复制和重新复制 gorwing 数据缓冲时间然后再次。那是 Length*Length/2 缩放,也可能严重破坏堆内存结构(google 大约 heap fragmentation)。
    如果可以 - 您需要检查之前的 FileSize 并将数组设置为

  3. FreeAndNil(byteArray); - 完全错误。数组不是对象。您不能对它们使用 Destroy/Free/FreeAndNil。那么如何清理 dynarray 呢? 好吧,你可能什么都不做,因为 dynarrays 是自动引用计数类型之一,如字符串和接口等。只要你的过程退出,Delphi 就会自动从局部变量中释放内存。 但是,如果你想在过程中间清理一个 dynarray,你可以通过 SetLength( MyDynArray, 0 ) 或快捷方式 MyDynArray := nil

  4. 来完成
  5. BlockRead(myFile, byteArray, sizeof(myFile), count) 误用 SizeOf 是错误的。但它还有另一个缺点:ByteArray 变量基本上是一个指针,所以它只有 4(四!)个字节(在 Win64 代码中是 8 个字节),所以你只需覆盖所有调用堆栈。你真的应该更好地使用现代类型安全 API 来代替。但是如果你想坚持使用旧的不安全低级 API 那么你必须非常清楚不同类型变量的低级实现。基本上你不想将文件内容读入缓冲区的指针,而是读入指向的缓冲区,所以它应该像 BlockRead(myFile, byteArray[0], MyFileAndArraySize, count)。然后,如果只读取文件的一部分 - count < MyFileAndArraySize - 你会 BlockRead(myFile, byteArray[count], MyFileAndArraySize - count, count1),然后 BlockRead(myFile, byteArray[count+count1], MyFileAndArraySize - count - count1, count2) 等等。 即使您了解低级类型中的字节 运行 也很乏味...

  6. ar[k-1] := tf.Read(ar[k-1],tf.size); - 这简直太可怜了。检查 http://www.freepascal.org/docs-html/rtl/classes/tstream.read.html - 结果是实际读取了多少字节。因此,不是用文件内容填充数组,而是用 "how many bytes were read in one attempt?" 代替。那么你最好使用 tf.ReadBuffer 过程。

然而,如果你想通过部分 tf.Read 进行,它应该类似于

k := 0;
SetLength(ar, tf.Size);
while k < tf.Size do begin
  k := k + tf.Read( ar[k], tfSize - k);
end;

但是,在现代 Delphi

中,您可以使用更简单的工具来处理小文件

您的代码中还有一个问题在

s   := inttostr(ar[0]) +';';
for k := 1 to length(ar) do
begin
  s := s + ';' + IntToStr(ar[k]);
end;

也就是所谓的"one-off error".

虽然由于历史原因,字符串的索引从 1 到 Length(s),并且通常没有第 0 个元素,但 dynarrays 不是。

动态数组的索引从 0 = Low(ArrayVarName)High(ArrayVarName) = Length(ArrayVarName) - 1。所以你的循环试图读取数组末尾以外的内存,数组本身之外。

另一个错误是您以两个分号开头,例如“10;;20;30;40.....”

当你累了或注意力不集中时很典型。所以你最好完全避免索引数组。 下面是将动态数组从 Delphi XE2

转换为字符串的工作代码
procedure TForm1.Button1Click(Sender: TObject);
var DynamicArray: TBytes;
    SB: TStringBuilder; iSL: IJclStringList;
    s1,s2: string; b: byte;
begin
   DynamicArray := TBytes.Create( 10, 20, 30, 40, 50 );

   SB := TStringBuilder.Create;
   try
     for b in DynamicArray do begin
       if SB.Length > 0 then
          SB.Append( ';' );
       SB.Append( b );
     end;

     s1 := SB.ToString;
   finally
     SB.Destroy;  // you must do it in Delphi for Windows
   end;

   iSL := JclStringList();
   for b in DynamicArray do
     iSL.Add( IntToStr( b ) );
   s2 := iSL.Join( ';' );
   iSL := nil;  // you may skip it, Delphi would do on exit from procedure

   ShowMessage( 'Dynamic array of bytes to string'
                + ^M^J'   with Delphi RTL: ' + s1
                + ^M^J'   with J.E.D.I. Code Library: ' + s2);

end;

关于动态数组的更多信息: