当我在ghci上输入以下内容时,会引发异常:
Prelude> import Control.Exception
Prelude Control.Exception> readFile "test.txt" `catch` (const $ return "exception caught!" :: SomeException -> IO String)
"*** Exception: test.txt: hGetContents: invalid argument (invalid byte sequence)
我不明白为什么没有抓住异常。我在Windows 7上使用stack ghci
命令运行上面的命令。
(“test.txt”文件包含一些以UTF8编码的随机日文字母,但我预计异常应该被捕获)
有人可以解释原因吗?
答案 0 :(得分:5)
由于readFile
返回一个惰性String
,因此在使用之前它不会评估文件内容,在这种情况下,当ghci打印它时。您可以通过强制评估其内容来捕获它。
import Control.Exception
import Control.DeepSeq
(readFile "test.txt" >>= evaluate . force) `catch` (const $ return "exception caught!" :: SomeException -> IO String)
答案 1 :(得分:4)
此问题是由惰性IO和Windows的默认语言环境编码引起的。
与您的假设相反,该文件实际上是在 readFile "test.txt"
抓住(const $ return "exception caught!" :: SomeException -> IO String)
之后读取的。
通过惰性IO,当实际评估结果值时,文件内容只读。
您遇到的例外情况仅在阅读内容时引起(如下所述)。
强制评估文件使catch
函数捕获:
> (print . length =<< readFile "test.txt") `catch` (const $ putStrLn "Exception caught" :: SomeException -> IO ())
Exception caught
在此示例中,应用length
函数时实际读取文件,然后由catch
函数捕获。
此外,引发异常的原因是test.txt
的某些(可能是第一个)字符与句柄的默认字符编码不兼容,
这是日本Windows中的CP932。
尝试在无效的日文字符前插入一些ASCII字符,
然后你会发现打印时真的抛出了异常(实际评估了test.txt
的内容):
$ cat .\test.txt
abc介
$ stack exec ghci
> import Control.Exception
> readFile "test.txt" `catch` (const $ return "exception caught!" :: SomeException -> IO String)
"abc\33673*** Exception: test.txt: hGetContents: invalid argument (invalid byte sequence)
如果您能阅读日语,请参阅由我撰写的https://haskell.jp/blog/posts/2017/windows-gotchas.html的第一部分! :)