纠正读取文件和写入文件异常的方法

问题描述:

我在Haskell中编写应用程序,并且如果readFilewriteFile失败,想向用户显示有意义的错误消息。我目前正在使用Control.Exception.tryJust来捕捉IOError,并将它们转换为人类可读的文本。纠正读取文件和写入文件异常的方法

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

与前面的例子相比,似乎没有捕获“不恰当类型”错误的便携方式。看着GHC.IO.ExceptionInappropriateType被标记为GHC-only,所以我不能只在ioeGetErrorType模式匹配。我能模式匹配上ioeGetErrorString,但我不知道如果这些字符串总是在不同的平台,编译器,语言环境是相同的,等

总之,我的问题是:

  1. 哪些异常应该我赶上readFile/writeFile
  2. 一旦我有一个例外,我应该如何去从中提取信息?
  3. 是否有一种便携的方式来捕获GHC唯一的例外,如InappropriateType

更新:

基于@ 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 
              ] 

在Debian希德GNU/Linux的输出与GHC 7.10.3是:

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

我对Haskell基础设施当前状态的“便携”异常并不乐观。我们一直坚持单编译模式多年。希望尽快改变。 – dfeuer

  1. 我应该捕捉readFile/writeFile的哪些异常?

在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. 曾经有一个例外,我应该如何去从中提取信息?
开始=>

每个异常都有自己的结构。可以发现IOException的定义here

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

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

正如@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 
+0

对于GNU/Linux,我仍然在GHC 7.10.3上使用System.IO.openFile获得“不合适的类型”。 – Matthew

+0

我想确切的例外是操作系统依赖 - 然后我在OS X + GHC 7.10.2下测试它。更新了答案。 – ErikR

+0

但是 - 你会得到一个稍微不同的信息 - “不是目录”与“是目录” - 所以这是区分这两者的一种方法。 – ErikR