1

我已经尝试将文件读入 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;
4

1 回答 1

2

文件大吗?它可以一次全部放入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;

相反,要将数组的内容保存到文件中,您可以使用如何将 TBytes 转换为二进制文件?(使用内存流)


关于你的尝试。

http://wiki.freepascal.org/File_Handling_In_Pascal

  1. 正如上面已经提到的,SizeOf与文件无关,即File变量类型的内存大小。如果你想坚持使用旧的 TurboPascal API,那么你必须使用FileSize函数来一次设置大小。对于小文件,它可以正常工作,对于大文件,“一次将其全部读入内存,然后处理”的方法是错误的。

  2. inc(k); SetLength(ar,k);- 在 +1 循环中 - 这是一个非常糟糕的主意,这意味着堆碎片和复制和重新复制和重新复制一次又一次地重新复制数据缓冲区。这就是Length*Length/2扩展,也可能严重破坏堆内存结构(google about heap fragmentation)。
    如果可以 - 您需要FileSize在之前签入并将数组设置为

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

  4. 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)等等。即使您了解字节在低级类型中是如何运行的,也很乏味......

  5. ar[k-1] := tf.Read(ar[k-1],tf.size);——那真是太可怜了。检查http://www.freepascal.org/docs-html/rtl/classes/tstream.read.html - 结果是实际读取了多少字节。因此,不是用文件内容填充数组,而是用“一次尝试读取了多少字节?”来感受它。反而。那你最好使用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;

这就是所谓的“一次性错误”。

虽然出于历史原因的字符串从 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;

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

于 2015-05-27T14:48:04.033 回答