正确处理readFile和writeFile异常的方法

时间:2016-06-28 15:12:33

标签: haskell file-io error-handling

我正在Haskell中编写一个应用程序,并希望在readFilewriteFile失败时向用户显示有意义的错误消息。我目前正在使用IOError抓取Control.Exception.tryJust并将其转换为人类可读的文字。

但是,我无法弄清楚应该捕获哪些错误以及如何从中提取信息。例如,假设“/ bin”是一个目录而“/ bin / ls”是一个文件,readFile "/bin"readFile "/bin/ls/asdf"都给出“不合适的类型”,但(在我看来)它们是不同的错误。在第一个的情况下,我可以通过处理目录中的每个文件来恢复,而第二个更像是“不存在”类型的错误。

与前面的例子相比,似乎没有一种可移植的方式来捕获“不适当类型”错误。查看GHC.IO.ExceptionInappropriateType仅标记为GHC,因此我无法在ioeGetErrorType上进行模式匹配。我可以在ioeGetErrorString上进行模式匹配,但我不确定这些字符串在不同平台,编译器,语言环境等之间是否总是相同。

总之,我的问题是:

  1. 我应该抓住readFile / writeFile
  2. 的哪些例外情况
  3. 一旦出现异常,我该如何从中提取信息?
  4. 是否有一种可移植的方式来捕获仅限GHC的例外,例如InappropriateType
  5. 更新

    根据@ ErikR的回答,我正在查看GHC.IO.Exception.IOException的字段,其中包含以下Haskell程序:

    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
                                                ]
    

    使用GHC 7.10.3的Debian Sid GNU / Linux上的输出是:

    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 个答案:

答案 0 :(得分:3)

  
      
  1. 我应该为readFile / writeFile捕获哪些异常?
  2.   

在OS X下,如果您使用openFile后跟hGetContents而不是readFile,那么您提到的案例会有不同的例外情况。

openFile "/bin/ls/asdf" ...会抛出一个"没有这样的文件或目录"异常而openFile "/bin" ...将抛出"不合适的类型"。

在Linux下,两个打开的电话都会抛出一个"不合适的类型"例外。但是,您可以通过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

示例ghci会话:

*Main> bar "/bin"
errno: Nothing
description: is a directory
*Main> bar "/bin/ls/asd"
errno: Just 20
description: Not a directory
  
      
  1. 一旦出现异常,我该如何从中提取信息?
  2.   

每个例外都有自己的结构。可以找到IOException的定义here

要将字段访问者纳入范围,您需要导入GHC.IO.Exception

  
      
  1. 是否有一种可移植的方式来捕获仅限GHC的异常,例如InappropriateType?
  2.   

正如@dfeuer所说,出于所有实际目的,GHC是目前唯一的Haskell实现。

<强>更新

运行程序的结果。我没有包含最后一个结果,因为我没有只读文件系统来测试它,但我确定错误是相同的。

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