2012-10-01 23 views
5

Làm cách nào để đọc nhiều tệp dưới dạng ByteString đơn lẻ với bộ nhớ không đổi? Tôi có thể đọc n tệp một cách lười biếng như một thao tác IO đơn trong Haskell không?

readFiles :: [FilePath] -> IO ByteString 

Tôi hiện đang có việc thực hiện sau nhưng từ những gì tôi đã thấy từ hồ sơ cũng như sự hiểu biết của tôi, tôi sẽ kết thúc với n-1 của các tập tin trong bộ nhớ.

readFiles = foldl1 joinIOStrings . map ByteString.readFile 
    where joinIOStrings ml mr = do 
           l <- ml 
           r <- mr 
           return $ l `ByteString.append` r 

Tôi hiểu rằng các lỗ hổng ở đây là tôi đang áp dụng các hành động IO sau đó xuống dòng tự động họ vì vậy những gì tôi nghĩ tôi cần là một cách để thay thế foldl1 joinIOStrings mà không cần áp dụng chúng.

Trả lời

7

Làm cách nào để đọc nhiều tệp dưới dạng ByteString đơn lẻ với bộ nhớ không đổi?

Nếu bạn muốn sử dụng bộ nhớ liên tục, bạn cần Data.ByteString.Lazy. Không thể đọc một cách nghiêm ngặt ByteString và sẽ yêu cầu bộ nhớ O(sum of filesizes).

Đối với một số không quá lớn của các tập tin, bạn chỉ cần đọc tất cả (D.B.L.readFile đọc uể oải) và concatenating kết quả là tốt,

import qualified Data.ByteString.Lazy as L 

readFiles :: [FilePath] -> IO L.ByteString 
readFiles = fmap L.concat . mapM L.readFile 

Các mapM L.readFile sẽ mở các tập tin, nhưng chỉ đọc nội dung của mỗi khi nó được yêu cầu.

Nếu số lượng tệp lớn, do đó giới hạn của các tệp xử lý mở được hệ điều hành cho phép cho một quá trình có thể bị cạn kiệt, bạn cần điều gì đó phức tạp hơn. Bạn có thể nấu lên phiên bản lười biếng của riêng bạn mapM,

import System.IO.Unsafe (unsafeInterleaveIO) 

mapM_lazy :: [IO a] -> IO [a] 
mapM_lazy [] = return [] 
mapM_lazy (x:xs) = do 
       r <- x 
       rs <- unsafeInterleaveIO (mapM_lazy xs) 
       return (r:rs) 

để mỗi file sẽ chỉ được mở ra khi nội dung của nó là cần thiết, khi đã đọc trước đây tập tin có thể đã được đóng lại. Có một khả năng nhỏ mà vẫn chạy vào giới hạn tài nguyên, vì thời gian đóng các chốt không được đảm bảo.

Hoặc bạn có thể sử dụng yêu thích iteratee, enumerator, conduit hoặc bất kỳ gói nào giải quyết vấn đề một cách có hệ thống. Mỗi người trong số họ có những lợi thế và bất lợi riêng của mình đối với những người khác và, nếu mã hóa chính xác, loại bỏ khả năng vô tình nhấn giới hạn tài nguyên.

+0

Cảm ơn, tôi nên đề cập rằng tôi đã sử dụng Data.ByteString.Lazy. Điều này làm việc tốt với việc sử dụng bộ nhớ chỉ tăng một chút với mỗi tay cầm mở. Ngoài ra cảm ơn bạn đã chỉ ra các gói bổ sung, tôi mới bắt đầu học Haskell và chưa bắt gặp chúng. –

1

Tôi giả sử rằng bạn đang sử dụng các chuỗi byte lười (từ Data.ByteString.Lazy). Có nhiều cách có thể khác để làm điều này, nhưng một lựa chọn là chỉ cần sử dụng concat :: [ByteString] -> ByteString:

import Control.Monad 
import Data.ByteString.Lazy (ByteString) 
import qualified Data.ByteString.Lazy as ByteString 

readFiles :: [FilePath] -> IO ByteString 
readFiles = fmap ByteString.concat . mapM ByteString.readFile 

(Lưu ý: Tôi không có thời gian để kiểm tra mã, nhưng đọc tài liệu nói rằng điều này sẽ làm việc)

Các vấn đề liên quan