2010-03-26 29 views
15

Vẫn còn khá mới đối với Haskell ..Trong Haskell, tôi muốn đọc một tập tin và sau đó ghi vào nó. Tôi có cần chú thích nghiêm ngặt không?

Tôi muốn đọc nội dung của tệp, làm điều gì đó liên quan đến IO (sử dụng putStrLn bây giờ) và sau đó viết nội dung mới vào cùng một tệp.

tôi đến với:

doit :: String -> IO() 
doit file = do 
    contents <- withFile tagfile ReadMode $ \h -> hGetContents h 
    putStrLn contents 
    withFile tagfile WriteMode $ \h -> hPutStrLn h "new content" 

Tuy nhiên điều này không làm việc do sự lười biếng. Nội dung tập tin không được in. Tôi tìm thấy this post giải thích rõ điều đó.

Các giải pháp đề xuất có bao gồm putStrLn trong withFile:

doit :: String -> IO() 
doit file = do 
    withFile tagfile ReadMode $ \h -> do 
     contents <- hGetContents h 
     putStrLn contents 
    withFile tagfile WriteMode $ \h -> hPutStrLn h "new content" 

này hoạt động, nhưng nó không phải là những gì tôi muốn làm. Các hoạt động trong tôi cuối cùng sẽ thay thế putStrLn có thể dài, tôi không muốn giữ cho tập tin mở toàn bộ thời gian. Nói chung tôi chỉ muốn có thể lấy nội dung tập tin ra và sau đó đóng nó trước khi làm việc với nội dung đó.

Các giải pháp tôi đã đưa ra như sau:

doit :: String -> IO() 
doit file = do 
    c <- newIORef "" 
    withFile tagfile ReadMode $ \h -> do 
     a <- hGetContents h 
     writeIORef c $! a 
    d <- readIORef c 
    putStrLn d 
    withFile tagfile WriteMode $ \h -> hPutStrLn h "Test" 

Tuy nhiên, tôi thấy dài này và một chút obfuscated. Tôi không nghĩ rằng tôi cần một IORef chỉ để có được một giá trị, nhưng tôi cần "đặt" để đưa nội dung tập tin. Ngoài ra, nó vẫn không hoạt động nếu không có chú thích nghiêm ngặt $! cho writeIORef. Tôi đoán IORef s không nghiêm ngặt về bản chất?

Có ai có thể giới thiệu một cách tốt hơn, ngắn hơn để thực hiện việc này trong khi vẫn giữ ngữ nghĩa mong muốn của mình không?

Cảm ơn!

+3

Nếu bạn đăng các khai báo 'import' cần thiết để biên dịch mã của bạn, những người khác có thể giúp gỡ lỗi nó ... –

+2

Tối ưu hóa sớm, gốc tà ác, v.v. Tại sao bạn ngại giữ bộ mô tả tệp miễn là bạn cần nó? – jrockway

+1

IORef không có hiệu quả bạn cảm nhận. Một giá trị trong một IORef có thể chỉ là lười biếng như là một trong những bạn vừa trở về từ một khối. Mã của bạn tương đương với mã không hoạt động: tệp doit = ​​do { d <- withFile tagfile ReadMode $ \ h -> do { a <- hGetContents h; trả lại $! a }; ... IORef chỉ là một vòng vô nghĩa để nhảy qua. Nhưng dù sao, cú đấm là thường xuyên seq là không đủ để buộc một chuỗi toàn bộ. Bạn cần một seq sâu. – luqui

Trả lời

21

Lý do chương trình đầu tiên của bạn không hoạt động là withFile đóng tệp sau khi thực hiện thao tác IO được truyền cho nó. Trong trường hợp của bạn, hành động IO là hGetContents, không không đọc tệp ngay lập tức, nhưng chỉ khi nội dung của nó được yêu cầu. Vào thời điểm bạn cố gắng in nội dung của tệp, withFile đã đóng tệp, do đó đọc không thành công (âm thầm).

Bạn có thể sửa vấn đề này bằng cách không reinventing the wheel và chỉ đơn giản là sử dụng readFilewriteFile:

doit file = do 
    contents <- readFile file 
    putStrLn contents 
    writeFile file "new content" 

Nhưng giả sử bạn muốn mới nội dung phụ thuộc vào nội dung cũ. Sau đó, bạn có thể không, nói chung, chỉ cần làm

doit file = do 
    contents <- readFile file 
    writeFile file $ process contents 

writeFile thể ảnh hưởng đến những gì readFile lợi nhuận (hãy nhớ, nó đã không thực sự đọc các tập tin nào). Hoặc, tùy thuộc vào hệ điều hành của bạn, bạn có thể không mở được cùng một tệp để đọc và ghi trên hai tay cầm riêng biệt.Cách giải quyết đơn giản nhưng xấu xí là

doit file = do 
    contents <- readFile file 
    length contents `seq` (writeFile file $ process contents) 

mà sẽ buộc readFile để đọc toàn bộ tập tin và đóng nó trước khi hành động writeFile có thể bắt đầu.

+0

Sau khi đọc bài đăng bạn liên kết đến, tôi đoán bạn đã biết một số điều này. Nhưng vì có nhiều hơn một vấn đề lười biếng ở đây, tôi nghĩ tốt nhất là nên triệt để. –

+0

Thay vì sử dụng 'nội dung độ dài 'seq' ...'. Tôi nghĩ bạn có thể sử dụng phần mở rộng BangPatterns và viết lại dòng trước là '! Contents <- readFile file'. – Alasdair

+1

Điều đó tương đương với 'nội dung 'seq' ...', không đủ: nó sẽ chỉ đánh giá hàm tạo nội dung cấp cao nhất (nghĩa là nó trống) có nghĩa là chỉ đoạn đầu tiên của tệp sẽ được đọc . –

0

Thật xấu xí nhưng bạn có thể buộc nội dung được đọc bằng cách yêu cầu length đầu vào và seq 'nhập mã đó vào câu lệnh tiếp theo trong khối công việc của bạn. Nhưng thực sự giải pháp là sử dụng phiên bản nghiêm ngặt của hGetContents. Tôi không chắc nó được gọi là gì.

9

Tôi nghĩ rằng cách dễ nhất để giải quyết vấn đề này được useing IO nghiêm ngặt:

import qualified System.IO.Strict as S 
main = do 
    file <- S.readFile "filename" 
    writeFile "filename" file 
1

Bạn có thể lặp lại trong các tập tin xử lý, làm ghi lười biếng với một bản gốc (đến cuối tập tin) và lười biếng đọc với nhau . Vì vậy, không có chú thích nghiêm ngặt liên quan đến trường hợp phụ thêm vào tệp.

import System.IO 
import GHC.IO.Handle 

main :: IO() 
main = do 
    h <- openFile "filename" ReadWriteMode 
    h2 <- hDuplicate h 

    hSeek h2 AbsoluteSeek 0 
    originalFileContents <- hGetContents h2 
    putStrLn originalFileContents 

    hSeek h SeekFromEnd 0 
    hPutStrLn h $ concatMap ("{new_contents}" ++) (lines originalFileContents) 

    hClose h2 
    hClose h 

Chức năng hDuplicate được cung cấp bởi mô-đun GHC.IO.Handle.

Trả về bản sao của tay cầm ban đầu, với bộ đệm riêng. Tuy nhiên, hai Handles sẽ chia sẻ một con trỏ tập tin. Bộ đệm ban đầu của xử lý được xả, bao gồm loại bỏ bất kỳ dữ liệu đầu vào nào, trước khi xử lý được nhân đôi.

Với hSeek bạn có thể đặt vị trí của tay cầm trước khi đọc hoặc viết.

Nhưng tôi không chắc chắn mức độ tin cậy sẽ sử dụng "AbsoluteSeek 0" thay vì "SeekFromEnd 0" để viết, tức là ghi đè nội dung. Nói chung tôi sẽ đề nghị để viết vào một tập tin tạm thời đầu tiên, ví dụ bằng cách sử dụng openTempFile (từ System.IO), và sau đó thay thế ban đầu.

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