删除文件(如果存在)

时间:2011-12-14 09:34:51

标签: haskell

在Haskell中执行此操作的正确方法是什么?

if exists "foo.txt" then delete "foo.txt"
doSomethingElse

到目前为止,我有:

import System.Directory
main = do
        filename <- getFileNameSomehow
        fileExists <- doesFileExist filename
        if fileExists 
             then removeFile filename
             ???
        doSomethingElse

2 个答案:

答案 0 :(得分:46)

你最好删除文件,如果它不存在则简单地恢复:

import Prelude hiding (catch)
import System.Directory
import Control.Exception
import System.IO.Error hiding (catch)

removeIfExists :: FilePath -> IO ()
removeIfExists fileName = removeFile fileName `catch` handleExists
  where handleExists e
          | isDoesNotExistError e = return ()
          | otherwise = throwIO e

这可以避免某人在您的代码之间删除文件的竞争条件,检查它是否存在并将其删除。在你的情况下可能没关系,但无论如何这都是好的做法。

注意import Prelude hiding (catch)行 - 这是因为Prelude包含来自异常处理的旧函数,现在不推荐使用Control.Exception,它也有一个名为catch的函数;导入行只是简单地隐藏Prelude的catch,而支持Control.Exception

但是,这仍然存在更基本的基本问题:如何在IO中编写条件?

嗯,在这种情况下,只需执行

就足够了
when fileExists $ removeFile filename

(使用Control.Monad.when)。但是,通常在Haskell中查看类型是有用的。

条件的两个分支必须具有相同的类型。所以要填写

if fileExists
    then removeFile filename
    else ???

我们应该看一下removeFile filename的类型;无论???是什么,它都必须具有相同的类型。

System.Directory.removeFile的类型为FilePath -> IO (),因此removeFile filename的类型为IO ()。所以我们想要的是一个IO动作,结果类型为(),什么都不做。

嗯,return的目的是构建一个没有效果的动作,只返回一个常量值,return ()具有正确的类型:IO ()(或更一般地说,(Monad m) => m ())。因此???return ()(您可以看到我在上面的改进代码段中使用过,removeFile失败时无法执行任何操作,因为该文件不存在)。

(顺便说一句,您现在应该可以在when的帮助下实施return ();这非常简单:))

如果你发现一开始很难进入Haskell方式,那就不要担心 - 它会自然而然地产生,当它发生时,它会非常有益。 :)

答案 1 :(得分:11)

注意: ehird的答案对于竞争条件提出了一个非常好的观点。在阅读我的答案时应该牢记这一点,忽略了这个问题。请注意命令式伪代码在问题中提出也遇到了同样的问题。)

文件名的定义是什么?是在程序中给出,还是由用户提供?在命令式伪代码中,它是程序中的常量字符串。我假设您希望用户通过将其作为程序的第一个命令行参数传递来提供它。

然后我建议这样的事情:

import Control.Monad    
import System.Directory
import System.Environment

doSomethingElse :: IO ()

main = do
  args <- getArgs
  fileExists <- doesFileExist (head args)
  when fileExists (removeFile (head args))
  doSomethingElse

(如您所见,我添加了doSomethingElse的类型签名以避免混淆。)

我为System.Environment函数导入getArgs。如果有问题的文件只是由一个常量字符串给出(例如在你的命令式伪代码中),只需删除所有的args东西,并在head args {。}}的任何地方填写常量字符串。

导入

Control.Monad以获取when功能。请注意,这个有用的函数不是关键字(如if),而是普通函数。我们来看看它的类型:

when :: Monad m => Bool -> m () -> m ()

在您的情况下,mIO,因此您可以将when视为采用Bool和IO操作的函数,并仅在BoolTrue。当然,您可以使用if来解决您的问题,但在您的情况下,when会更清楚地解读。至少我是这么认为的。

附录:如果你像我一开始那样,感觉when是一些神奇而艰难的机器,那么尝试自己定义这个功能是非常有益的。我向你保证,这很简单...