4

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

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

到目前为止,我已经尝试了两种方法,都使用 Direct_IO 包。在第一种方法中,我是逐字符读取文件;它起作用了,但是速度非常慢。为了加快进程,我尝试按 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

4 回答 4

6

让 Bin_Size 等于Ada.Directories.Size(my_file),一口气读完。

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

my_image : bin_array renames my_image_ptr.all;

这样就不需要知道其他任何东西了……
但是,如果它只有几 MB,那就没有必要了。

于 2012-12-19T17:00:31.027 回答
4

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

纯粹主义者会注意到Ada.Streams.Stream_Element'Size可能与.Character'SizeStream_Element_ArrayStringContent

于 2012-12-19T18:15:59.910 回答
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)实现。

于 2012-12-19T23:11:28.627 回答
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;
于 2021-05-21T22:09:15.850 回答