2012-12-19 35 views
3

我想讀取幾MB的二進制文件的內容並將其存儲到緩衝區中。這是我的函數原型(如果需要的話我可以改變它):如何在Ada中完整快速地讀取二進制文件?

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其餘在文件中不會發生讀取操作。結果,大文件(> 1MB)被截斷,而小文件根本不被讀取。處理圖像時尤其明顯。

所以,我的問題是:什麼是正確的方法快速讀取二進制文件和完全?

在此先感謝。

回答

6

使Bin_Size等於Ada.Directories.Size(my_file),並一口氣讀取它。

如果它太大的堆棧分配(你會得到的Storage_Error)用新的分配它,並用重命名招

my_image : bin_array renames my_image_ptr.all; 

,使沒有別的需要知道...
但如果它是唯一幾MB,這是不必要的。

+0

爲一個變量分配幾MB值的堆棧數據可能會很危險。在某些系統上,這可能會耗盡你的堆棧,特別是如果函數是遞歸調用的,或者你已經使用了大部分堆棧。 – Anthony

+0

在PC上,「堆棧太大」限制在幾百MB內。 (顯然不存在遞歸!)。所以在許多情況下,這是最簡單的選擇。小系統和嵌入式軟件,你必須設計系統的約束(在嵌入式系統中,可能會禁止動態堆分配)。 –

3

Ada.Streams.Stream_IO.Read讀入Stream_Element_Array並告訴您讀取的最後一個元素;如果數組未填滿(因爲您已達到文件末尾),則Last將小於Item'Last

一個純粹主義者會注意到,Ada.Streams.Stream_Element'Size可能不一樣Character'Size,但對於任何正常的處理器芯片就可以了,所以你可以做Stream_Element_Array的使用的部分和相同尺寸的附加前String之間未檢查的轉換到您的Content

+1

這是我以前用於此確切要求的方法。爲了提高效率,您的目標應該是在單個I/O中執行整個讀取。 –

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)實現。

+0

我喜歡使用內存映射的想法。我認爲這可能是實現這一目標的更優化的方式。出於某些原因,我不想依賴第三方,但我會查看GNATCool代碼,以查看是否可以直接使用mmap。謝謝。 – tvuillemin

+0

如果你不想使用GANTColl,你可以嘗試自己實現它。這將涉及'編譯器將mmap調用導入到您的代碼庫中。 – Anthony