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

时间:2015-05-27 14:26:29

标签: delphi delphi-xe5 dynamic-arrays tfilestream

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

1 个答案:

答案 0 :(得分: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;

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

之类的内容

关于你的尝试。

http://wiki.freepascal.org/File_Handling_In_Pascal

  1. 如上所述,SizeOf与文件无关,即File变量类型的内存大小。如果您想坚持使用旧的TurboPascal API,则必须使用FileSize函数立即设置大小。对于小文件,它可以正常工作,对于大文件,“将所有内容一次性读取到内存,然后进程”是错误的。

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

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

  4. 来完成 滥用BlockRead(myFile, byteArray, sizeof(myFile), count)时,
  5. 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 - 结果是实际读取的字节数。因此,您不必使用文件内容填充数组,而是“在一次尝试中读取了多少字节?”代替。您最好使用tf.ReadBuffer程序。

  7. 然而,如果你想通过部分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;
    

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

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

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