2016-06-28 18 views
5

Tôi đang viết một ứng dụng trong Haskell và muốn hiển thị thông báo lỗi có ý nghĩa cho người dùng nếu readFile hoặc writeFile không thành công. Tôi hiện đang tìm kiếm IOError s với Control.Exception.tryJust và chuyển đổi chúng thành văn bản có thể đọc được.Cách đúng để xử lý ngoại lệ readFile và writeFile

Tuy nhiên, tôi đang gặp khó khăn trong việc tìm ra lỗi nào tôi nên nắm bắt và cách trích xuất thông tin từ chúng. Ví dụ, giả sử "/ bin" là một thư mục và "/ bin/ls" là một tệp, readFile "/bin"readFile "/bin/ls/asdf" cả hai đều cung cấp "loại không phù hợp" nhưng (theo ý kiến ​​của tôi) chúng là các lỗi khác nhau. Trong trường hợp đầu tiên, tôi có thể phục hồi bằng cách xử lý từng tệp trong thư mục, trong khi thứ hai giống như loại lỗi "không tồn tại".

Liên quan đến ví dụ trước, có vẻ như đó không phải là cách di chuyển các lỗi "loại không phù hợp". Nhìn vào GHC.IO.Exception, InappropriateType được đánh dấu chỉ GHC nên tôi không thể chỉ khớp mẫu trên ioeGetErrorType. Tôi có thể phù hợp với mô hình trên ioeGetErrorString nhưng tôi không chắc chắn nếu những chuỗi luôn luôn giống nhau trên nhiều nền tảng khác nhau, trình biên dịch, miền địa phương vv

Nói tóm lại, câu hỏi của tôi là:

  1. Những trường hợp ngoại lệ nên tôi bị bắt cho readFile/writeFile?
  2. Khi tôi có ngoại lệ, tôi nên làm thế nào để trích xuất thông tin từ nó?
  3. Có cách nào cầm tay để bắt các ngoại lệ chỉ áp dụng cho GHC chẳng hạn như InappropriateType?

Cập nhật:

Dựa trên câu trả lời @ ErikR của tôi là nhìn vào các lĩnh vực GHC.IO.Exception.IOException với chương trình Haskell sau:

import Control.Exception (try) 
import GHC.IO.Exception (IOException(..)) 
import qualified Data.ByteString as B 


main :: IO() 
main = do 
    try (readFile "/nonexistent") >>= printException 
    try (writeFile "/dev/full" " ") >>= printException 
    try (readFile "/root") >>= printException 
    try (readFile "/bin") >>= printException 
    try (writeFile "/bin" "") >>= printException 
    try (readFile "/bin/ls/asdf") >>= printException 
    try (writeFile "/bin/ls/asdf" "") >>= printException 
    try (B.readFile "/dev/null") >>= printException 

    -- I have /media/backups mounted as read-only. Substitute your own read-only 
    -- filesystem for this one 
    try (writeFile "/media/backups/asdf" "") >>= printException 

printException :: Either IOError a -> IO() 
printException (Right _) = putStrLn "No exception caught" 
printException (Left e) = putStrLn $ concat [ "ioe_filename = " 
              , show $ ioe_filename e 
              , ", ioe_description = " 
              , show $ ioe_description e 
              , ", ioe_errno = " 
              , show $ ioe_errno e 
              ] 

Sản lượng trên Debian Sid GNU/Linux với GHC 7.10.3 là:

ioe_filename = Just "/nonexistent", ioe_description = "No such file or directory", ioe_errno = Just 2 
ioe_filename = Just "/dev/full", ioe_description = "No space left on device", ioe_errno = Just 28 
ioe_filename = Just "/root", ioe_description = "Permission denied", ioe_errno = Just 13 
ioe_filename = Just "/bin", ioe_description = "is a directory", ioe_errno = Nothing 
ioe_filename = Just "/bin", ioe_description = "Is a directory", ioe_errno = Just 21 
ioe_filename = Just "/bin/ls/asdf", ioe_description = "Not a directory", ioe_errno = Just 20 
ioe_filename = Just "/bin/ls/asdf", ioe_description = "Not a directory", ioe_errno = Just 20 
ioe_filename = Just "/dev/null", ioe_description = "not a regular file", ioe_errno = Nothing 
ioe_filename = Just "/media/backups/asdf", ioe_description = "Read-only file system", ioe_errno = Just 30 
+1

Tôi không lạc quan về ngoại lệ "di động" với trạng thái hiện tại của cơ sở hạ tầng Haskell. Chúng tôi đã bị mắc kẹt trong một chế độ biên dịch cho một số năm. Đây là hy vọng rằng những thay đổi sớm. – dfeuer

Trả lời

3
  1. Tôi nên bắt những ngoại lệ nào cho readFile/writeFile?

Dưới OS X, nếu bạn sử dụng openFile Tiếp theo hGetContents thay vì readFile sau đó bạn sẽ nhận được ngoại lệ khác nhau cho các trường hợp bạn đề cập đến.

openFile "/bin/ls/asdf" ... sẽ ném ngoại lệ "không có tệp hoặc thư mục" ngoại trừ openFile "/bin" ... sẽ ném "loại không phù hợp".

Trong Linux, cả hai cuộc gọi mở đều sẽ loại trừ ngoại lệ "loại không phù hợp".Tuy nhiên, bạn có thể phân biệt giữa hai thông qua các lĩnh vực ioe_errnoioe_description:

import System.IO 
import GHC.IO.Exception 
import Control.Exception 

foo path = do 
    h <- openFile path ReadMode 
    hClose h 

show_ioe :: IOException -> IO() 
show_ioe e = do 
    putStrLn $ "errno: " ++ show (ioe_errno e) 
    putStrLn $ "description: " ++ ioe_description e 

bar path = foo path `catch` show_ioe 

mẫu phiên ghci:

*Main> bar "/bin" 
errno: Nothing 
description: is a directory 
*Main> bar "/bin/ls/asd" 
errno: Just 20 
description: Not a directory 
  1. Khi tôi có một ngoại lệ, làm thế nào tôi nên đi về giải nén thông tin từ nó?

Mỗi ngoại lệ đều có cấu trúc riêng. Định nghĩa của một IOException có thể được tìm thấy here.

Để đưa trình truy cập trường vào phạm vi bạn cần nhập GHC.IO.Exception.

  1. Có cách nào cầm tay để bắt ngoại lệ chỉ GHC như InappropriateType không?

Như @dfeuer, cho tất cả mục đích thực tế, GHC là triển khai Haskell duy nhất tại thời điểm này.

Cập nhật

Kết quả chạy chương trình của bạn. Tôi không bao gồm kết quả cuối cùng bởi vì tôi không có một hệ thống tập tin chỉ đọc xung quanh để kiểm tra nó trên, nhưng tôi chắc chắn rằng lỗi sẽ giống nhau.

ioe_filename = Just "/nonexistent", ioe_description = "No such file or directory", ioe_errno = Just 2 
ioe_filename = Just "/dev/full", ioe_description = "Permission denied", ioe_errno = Just 13 
ioe_filename = Just "/root", ioe_description = "is a directory", ioe_errno = Nothing 
ioe_filename = Just "/bin", ioe_description = "is a directory", ioe_errno = Nothing 
ioe_filename = Just "/bin", ioe_description = "Is a directory", ioe_errno = Just 21 
ioe_filename = Just "/bin/ls/asdf", ioe_description = "Not a directory", ioe_errno = Just 20 
ioe_filename = Just "/bin/ls/asdf", ioe_description = "Not a directory", ioe_errno = Just 20 
ioe_filename = Just "/dev/null", ioe_description = "not a regular file", ioe_errno = Nothing 
+0

Tôi vẫn nhận được "loại không phù hợp" từ cả hai bằng cách sử dụng System.IO.openFile trên GHC 7.10.3 cho GNU/Linux. – Matthew

+0

Tôi đoán ngoại lệ chính xác là hệ điều hành phụ thuộc sau đó - Tôi đã thử nghiệm nó dưới OS X + GHC 7.10.2. Đã cập nhật câu trả lời. – ErikR

+0

Tuy nhiên - bạn nhận được một thông báo hơi khác nhau - "không phải là một thư mục" so với "là một thư mục" - vì vậy đó là một cách để phân biệt giữa hai. – ErikR