递归搜索目录中所有与Haskell中的名称条件匹配的文件

dia*_*rov 6 file-extension haskell filepath

我在Haskell相对缺乏经验,我想改进,所以对于我的学习项目,我有以下要求:

  • 我想从指定的顶级目录开始搜索,不一定是绝对路径.
  • 我想找到给定扩展名的所有文件.md.
  • 我想不要搜索隐藏的目录toplevel/.excluded.
  • 我希望能够忽略像gedit产生的隐藏文件.filename.md.swp.
  • 我希望最终得到一个完整的文件列表作为我的功能的结果.

我搜遍了所有的SO.这是我到目前为止所拥有的:

import qualified System.FilePath.Find as SFF
import qualified Filesystem.Path.CurrentOS as FP

srcFolderName = "src"
outFolderName = "output"
resFolderName = "res"

ffNotHidden :: SFF.FindClause Bool
ffNotHidden = SFF.fileName SFF./~? ".?*"

ffIsMD :: SFF.FindClause Bool
ffIsMD = SFF.extension SFF.==? ".md" SFF.&&? SFF.fileName SFF./~? ".?*"

findMarkdownSources :: FilePath -> IO [FilePath]
findMarkdownSources filePath = do
    paths <- SFF.find ffNotHidden ffIsMD filePath
    return paths
Run Code Online (Sandbox Code Playgroud)

这不起作用.在"findMarkdownSources"中进行printf风格的调试,我可以验证filePath是否正确,例如"/home/user/testdata"(打印包含",如果告诉你的话).列表paths总是空的.我绝对肯定目录中有markdown文件我已指定(find/path/to/dir -name"*.md"找到它们).

因此,我有一些具体的问题.

  1. 有没有理由(过滤器不正确),例如,为什么这段代码不起作用?
  2. 在haskell中有很多方法可以做到这一点.似乎至少有六个包(fileman,system.directory,system.filepath.find)专用于此.这里有一些问题可以回答:

    1. 在Haskell中流式传输目录的递归下降
    2. Haskell中有一些目录漫游器吗?
    3. 使用filemanip避免递归到特定文件夹

    每个人都有三种独特的方式来实现我想要达到的目标,所以,我们差不多有10种方法可以做到......

  3. 有没有具体的方法我应该这样做?如果是这样的话?如果它有帮助,一旦我有我的文件列表,我将走完整个事情,打开并解析每个文件.

如果它有帮助,我对基本的haskell相当舒服,但是如果我们开始变得过于沉重的monad和applicative functors你们需要放慢速度(我没有足够使用haskell来保持这种状态).不过,我发现hackage上的haskell文档难以理解.

dan*_*iaz 5

所以,我们差不多有10种方法可以做到这一点......

这是另一种方法,使用目录,文件路径额外包中的函数,但不是太多的monad魔法:

import Control.Monad (foldM)
import System.Directory (doesDirectoryExist, listDirectory) -- from "directory"
import System.FilePath ((</>), FilePath) -- from "filepath"
import Control.Monad.Extra (partitionM) -- from the "extra" package

traverseDir :: (FilePath -> Bool) -> (b -> FilePath -> IO b) -> b -> FilePath -> IO b
traverseDir validDir transition =
    let go state dirPath =
            do names <- listDirectory dirPath
               let paths = map (dirPath </>) names
               (dirPaths, filePaths) <- partitionM doesDirectoryExist paths
               state' <- foldM transition state filePaths -- process current dir
               foldM go state' (filter validDir dirPaths) -- process subdirs
     in go
Run Code Online (Sandbox Code Playgroud)

这个想法是用户传递一个FilePath -> Bool函数来过滤不需要的目录; 还有一个初始状态b和一个转换函数b -> FilePath -> IO b,它处理文件名,更新b状态并可能有一些副作用.请注意,状态的类型由调用者选择,调用者可能会在那里放置有用的东西.

如果我们只想在生成文件名时打印文件名,我们可以这样做:

traverseDir (\_ -> True) (\() path -> print path) () "/tmp/somedir"
Run Code Online (Sandbox Code Playgroud)

我们正在使用()虚拟状态,因为我们在这里并不需要它.

如果我们想将文件累积到列表中,我们可以这样做:

traverseDir (\_ -> True) (\fs f -> pure (f : fs)) [] "/tmp/somedir" 
Run Code Online (Sandbox Code Playgroud)

如果我们想过滤一些文件怎么办?我们需要调整传递给traverseDir它的转换函数,以便忽略它们.