我想读取几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)被截断,而小文件根本不被读取。在处理图像时尤其明显。
所以,我的问题是:快速和完整地读取二进制文件的正确方法是什么?
提前致谢。
答案 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;