如何在Ada中完全快速地读取二进制文件?

时间:2012-12-19 16:40:55

标签: file io binaryfiles ada

我想读取几MB的二进制文件的内容并将其存储到缓冲区中。这是我的函数原型(如果需要我可以更改它):

procedure GET_BIN_CONTENT_FROM_PATH(PATH    : in UNBOUNDED_STRING;
                                    CONTENT : out UNBOUNDED_STRING);

到目前为止,我已经尝试了两种方法,都使用Direct_IO包。在第一种方法中,我正逐字逐句地读取文件;它工作,但它非常缓慢。为了加快这个过程,我尝试用MB读取MB文件:

procedure GET_BIN_CONTENT_FROM_PATH (PATH    : in UNBOUNDED_STRING;
                                     CONTENT : out UNBOUNDED_STRING) is

   BIN_SIZE_LIMIT : constant NATURAL := 1000000;
   subtype FILE_STRING is STRING (1 .. BIN_SIZE_LIMIT);
   package FILE_STRING_IO is new ADA.DIRECT_IO (FILE_STRING);
   FILE : FILE_STRING_IO.FILE_TYPE;
   BUFFER : FILE_STRING;

begin
   FILE_STRING_IO.OPEN (FILE, MODE => FILE_STRING_IO.IN_FILE,
                        NAME => TO_STRING (C_BASE_DIR & PATH));
   while not FILE_STRING_IO.END_OF_FILE (FILE) loop
      FILE_STRING_IO.READ (FILE, ITEM => BUFFER);
      APPEND (CONTENT, BUFFER);
   end loop;
   FILE_STRING_IO.CLOSE (FILE);
end GET_BIN_CONTENT_FROM_PATH;

不幸的是,如果文件中剩余的内存少于1MB,似乎不会发生READ操作。结果,大文件(> 1MB)被截断,而小文件根本不被读取。在处理图像时尤其明显。

所以,我的问题是:快速和完整地读取二进制文件的正确方法是什么?

提前致谢。

4 个答案:

答案 0 :(得分:6)

使Bin_Size等于Ada.Directories.Size(my_file),并一次阅读。

如果它对于堆栈分配太大(你会得到Storage_Error)用New分配它,并使用重命名技巧

my_image : bin_array renames my_image_ptr.all;

所以没有其他事情需要知道...... 但如果它只有几MB,那就没有必要了。

答案 1 :(得分:4)

有许多“正确”的方法,但这里有一个你可能喜欢的方法。特别是在读取大文件时,读取整个文件的有效方法是使用mmap映射内存。

根据您的许可需求,您可以向第三方GPLd解决方案开放。 AdaCore提供GNATColl集合,为mmap提供了一个很好的界面。您可以映射整个文件并复制内容。

declare
   File : Mapped_File;
   Str  : Str_Access;
begin
   File := Open_Read ("/tmp/file_on_disk");
   Read (File);  --  read the whole file
   Str := Data (File);
   for S in 1 .. Last (File) loop
       Put (Str (S));
   end loop;
   Close (File);
end;

如果您的系统不支持mmap调用,则该库会回退到read(2)实施。

答案 2 :(得分:3)

Ada.Streams.Stream_IO.Read读入Stream_Element_Array并告诉您最后读取的元素;如果未填充数组(因为您已到达文件末尾),Last将小于Item'Last

纯粹主义者会注意到Ada.Streams.Stream_Element'Size可能与Character'Size不同,但对于任何正常的处理器芯片都是如此,因此您可以在{{1}的已使用部分之间进行未经检查的转换在添加到Stream_Element_Array之前,{}和String的大小相同。

答案 3 :(得分:0)

正如其他人提到的,Ada.Streams.Stream_IO.Read 是要走的路。这是我放在我的系统上的一个例子。假设您有足够的内存可用于动态分配,这可以读取大于堆栈大小的文件。

我没有深入研究 Stream.IO.Read 代码的内部结构,但我怀疑 Stream_IO 包正在使用 4k 内存块(从堆分配)来缓冲读取操作。

with Ada.Directories;  use Ada.Directories;
with Ada.Direct_IO;
with Ada.Unchecked_Deallocation;
with Ada.Streams.Stream_IO;

procedure Read_Input_File is

   type Byte is mod 2 ** 8;
   type Byte_Array is array (File_Size range <>) of Byte;
   type Byte_Array_Access is access Byte_Array;
      
   procedure Delete is new Ada.Unchecked_Deallocation 
                           (Byte_Array, Byte_Array_Access);
 
   function Read_Binary_File (Filename : String)
      return Byte_Array_Access
   is
      package SIO renames Ada.Streams.Stream_IO;
 
      Binary_File_Size : File_Size := Ada.Directories.Size (Filename);
      Binary_File_Data : Byte_Array_Access; 
      S                : SIO.Stream_Access;   
      File             : SIO.File_Type;
 
   begin
      -- Allocate memory from the heap 
      Binary_File_Data := new Byte_Array (1 .. Binary_File_Size);
 
      SIO.Open (File, SIO.In_File, Filename);
      S := SIO.Stream (File);
 
      -- Read entire file into the buffer
      Byte_Array'Read (S, Binary_File_Data.all);
 
      SIO.Close (File);
 
      return Binary_File_Data;
   end;

   File_Data : Byte_Array_Access;

begin

   File_Data := Read_Binary_File ("File_Name.bin");

   -- Do something with data

   Delete (File_Data);

end Read_Input_File;