使用Swift的FileManager遍历文件夹及其子文件夹中的文件


76

我对Swift编程非常陌生,并且试图遍历文件夹中的文件。我在这里查看了答案并尝试将其转换为Swift语法,但未成功。

let fileManager = NSFileManager.defaultManager()
let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath(folderPath)

for element in enumerator {
    //do something
}

我得到的错误是:

Type 'NSDirectoryEnumerator' does not conform to protocol 'SequenceType'

我的目的是查看主文件夹中包含的所有子文件夹和文件,并找到具有特定扩展名的所有文件,然后对它们进行处理。

Answers:


85

使用以下nextObject()方法enumerator

while let element = enumerator?.nextObject() as? String {
    if element.hasSuffix("ext") { // checks the extension
    }
}

如果没有按照下面的user1398498的回答进行可选的链接,此功能将无法正常工作。
比约恩

2
刚刚尝试过这一点,除了无限循环外,我一无所有,不得不大致采用雷尼尔的方法。
nhgrif

奇怪的while是,稠密的for溶液比粗溶液更受青睐。(哦,我看到我已经对其他解决方案发表了类似的评论-无论如何)
qwerty_so

9
对于那些使用enumerator(at:, includingPropertiesForKeys:, options:)该元素的用户,请注意该元素实际上是一个URL,而不是一个String,因此,如果您使用该代码,它将不会返回任何内容。while let element = enumerator?.nextObject() as? URL {...}
罗德里戈·冈萨雷斯

很好的参考。我也在下面的链接中找到了它。developer.apple.com/documentation/foundation/filemanager/...
arango_86

62

如今(2017年初),强烈建议使用与URL相关的API(功能更广泛)

let fileManager = FileManager.default

do {
    let resourceKeys : [URLResourceKey] = [.creationDateKey, .isDirectoryKey]
    let documentsURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
    let enumerator = FileManager.default.enumerator(at: documentsURL,
                            includingPropertiesForKeys: resourceKeys,
                                               options: [.skipsHiddenFiles], errorHandler: { (url, error) -> Bool in
                                                        print("directoryEnumerator error at \(url): ", error)
                                                        return true
    })!

    for case let fileURL as URL in enumerator {
        let resourceValues = try fileURL.resourceValues(forKeys: Set(resourceKeys))
        print(fileURL.path, resourceValues.creationDate!, resourceValues.isDirectory!)
    }
} catch {
    print(error)
}

感谢您的周全。(对于路径类型的版本,我一点都不走运,因为myURL.absolutePath对于NSFileEnumerator而言不是有效的基于字符串的路径,并且不会创建有效的枚举器)。有关URLResourceKeys的列表,请
参阅

这是如何执行深度搜索的一个很好的例子,对我来说非常有用。感谢您的贡献。
佛教徒

18

我根本无法获得pNre的解决方案。while循环从未收到任何东西。但是,我确实遇到了适用于我的解决方案(在Xcode 6 beta 6中,所以自pNre发布上述答案以来,情况可能已经改变了):

for url in enumerator!.allObjects {
    print("\((url as! NSURL).path!)")
}

1
有趣的是,该解决方案的投票很少,因为它的语法比当前最喜欢的语法简单得多。
qwerty_so

2
它之所以投票的原因是因为它依赖于枚举器(枚举器!)而不是枚举器的强制展开?在投票最多的答案中。
鲍里斯·苏沃洛夫

请注意,这是无法使用的大型目录-它可能会挂起几分钟。
阿披·贝克特

停止强制展开。
马克·帕金斯

9

返回目录+子目录中的所有文件

import Foundation

let path = "<some path>"

let enumerator = FileManager.default.enumerator(atPath: path)

while let filename = enumerator?.nextObject() as? String {
        print(filename)
}

2
它不会进入子目录,也不会获取隐藏文件,也不会跟随符号链接
vomi

我们如何获取完整路径而不是文件名?
ikel

您使用哪种路径?就我而言,我在xcode项目中有“ mydir”目录。当我使用时let path = "mydir",什么也没发生。我使用的是Swift5。–
Midtownguru

这是一条完整的道路。有没有一种方法可以使用相对路径,以便可以将其包含在项目文件夹中?
Midtownguru

8

Swift3 +绝对网址

extension FileManager {
    func listFiles(path: String) -> [URL] {
        let baseurl: URL = URL(fileURLWithPath: path)
        var urls = [URL]()
        enumerator(atPath: path)?.forEach({ (e) in
            guard let s = e as? String else { return }
            let relativeURL = URL(fileURLWithPath: s, relativeTo: baseurl)
            let url = relativeURL.absoluteURL
            urls.append(url)
        })
        return urls
    }
}

基于@ user3441734中的代码


6

迅捷3

let fd = FileManager.default
fd.enumerator(atPath: "/Library/FileSystems")?.forEach({ (e) in
    if let e = e as? String, let url = URL(string: e) {
        print(url.pathExtension)
    }
})

当我使用此代码时,我发现路径中带有空格的目录/文件被忽略了。知道为什么会这样吗?然后,我尝试了@vadian的代码,它可以按预期工作。
ATutorMe '17

5

如果您得到

“ NSDirectoryEnumerator?” 没有名为“ nextObject”的成员错误

while循环应为:

while let element = enumerator?.nextObject() as? String {
  // do things with element
}

它与可选链接有关


4

SWIFT 3.0

返回传递的目录及其子目录中具有扩展名的所有文件

func extractAllFile(atPath path: String, withExtension fileExtension:String) -> [String] {
    let pathURL = NSURL(fileURLWithPath: path, isDirectory: true)
    var allFiles: [String] = []
    let fileManager = FileManager.default
    let pathString = path.replacingOccurrences(of: "file:", with: "")
    if let enumerator = fileManager.enumerator(atPath: pathString) {
        for file in enumerator {
            if #available(iOS 9.0, *) {
                if let path = NSURL(fileURLWithPath: file as! String, relativeTo: pathURL as URL).path, path.hasSuffix(".\(fileExtension)"){
                    let fileNameArray = (path as NSString).lastPathComponent.components(separatedBy: ".")
                    allFiles.append(fileNameArray.first!)
                }
            } else {
                // Fallback on earlier versions
                print("Not available, #available iOS 9.0 & above")
            }
        }
    }
    return allFiles
}

4

我的两分钱从以前的花絮中..更加快捷,并具有可选功能:

 let enumerator = FileManager.default.enumerator(atPath: folderPath)
    while let element = enumerator?.nextObject() as? String {
        print(element)

        if let fType = enumerator?.fileAttributes?[FileAttributeKey.type] as? FileAttributeType{

            switch fType{
            case .typeRegular:
                print("a file")
            case .typeDirectory:
                print("a dir")
            }
        }

    }

2

为Swift 3更新:

let fileManager = FileManager()     // let fileManager = NSFileManager.defaultManager()
let en=fileManager.enumerator(atPath: the_path)   // let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath(folderPath)

while let element = en?.nextObject() as? String {
    if element.hasSuffix("ext") {
        // do something with the_path/*.ext ....
    }
}

1

添加到vadian的响应中-Apple文档提到在某些方面基于路径的URL更简单,但是文件引用URL的优势在于,如果在应用运行时移动或重命名文件,则引用仍然有效。

从“访问文件和目录”的文档中:

“基于路径的URL更易于操作,易于调试,并且通常被诸如NSFileManager之类的类所首选。文件引用URL的优点在于,与应用程序运行时基于路径的URL相比,它们不那么脆弱。如果用户在Finder中移动文件时,任何引用该文件的基于路径的URL都立即变为无效,必须将其更新为新路径。但是,只要该文件移动到同一磁盘上的另一个位置,其唯一ID就不会更改,所有文件引用URL仍然有效。”

https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/AccessingFilesandDirectories/AccessingFilesandDirectories.html


0

如果要分类检查元素是文件还是子目录:

let enumerator = FileManager.default.enumerator(atPath: contentsPath);
while let element = enumerator?.nextObject() as? String {             
   if(enumerator?.fileAttributes?[FileAttributeKey.type] as! FileAttributeType == FileAttributeType.typeRegular){
                //this is a file
   }
   else if(enumerator?.fileAttributes?[FileAttributeKey.type] as! FileAttributeType == FileAttributeType.typeDirectory){ 
                //this is a sub-directory
    }
}

0

最近,在处理URL数组(无论它们是否为目录)(例如,拖放)时,都为此付出了很多努力。很快以这个扩展名结束了,可能有用

extension Sequence where Iterator.Element == URL {

    var handleDir: [URL] {
        var files: [URL] = []
        self.forEach { u in
            guard u.hasDirectoryPath else { return files.append(u.resolvingSymlinksInPath()) }
            guard let dir = FileManager.default.enumerator(at: u.resolvingSymlinksInPath(), includingPropertiesForKeys: nil) else { return }
            for case let url as URL in dir {
                files.append(url.resolvingSymlinksInPath())
            }
        }
        return files
    }
}

-1

避免使用引用URL,尽管它们确实具有上述优点,但它们会占用系统资源,并且如果您枚举的是大型文件系统(实际上不是那么大),则您的应用程序将很快陷入系统壁垒并被macOS关闭。


不回答问题,这应该更多是评论而不是答案。
TruMan1
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.