【问题标题】:Iterate through files in a folder and its subfolders using Swift's FileManager使用 Swift 的 FileManager 遍历文件夹及其子文件夹中的文件
【发布时间】:2014-10-06 17:53:09
【问题描述】:

我对 Swift 编程很陌生,我正在尝试遍历文件夹中的文件。 我看了一下答案here 并尝试将其翻译成 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'

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

【问题讨论】:

    标签: swift cocoa nsfilemanager


    【解决方案1】:

    使用enumeratornextObject()方法:

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

    【讨论】:

    • 根据下面 user1398498 的回答,如果没有可选链接,则无法使其正常工作。
    • 刚刚尝试过这个,除了无限循环,我什么也没有,只好粗略地采用雷尼尔的方法。
    • 很奇怪,一个相当粗糙的while 比密集的for 解决方案更受欢迎。 (哦,我看到我已经类似地评论了其他解决方案 - 无论如何)
    • 对于那些使用enumerator(at:, includingPropertiesForKeys:, options:) 的人来说,考虑到该元素实际上是一个 URL 而不是一个字符串,因此如果您使用该代码,它将不会返回任何内容。 while let element = enumerator?.nextObject() as? URL {...}
    • 很好的参考。我也从下面的链接中找到了它。 developer.apple.com/documentation/foundation/filemanager/…
    【解决方案2】:

    我根本无法使用 pNre 的解决方案; while 循环从来没有收到任何东西。但是,我确实遇到了这个对我有用的解决方案(在 Xcode 6 beta 6 中,所以自从 pNre 发布上述答案后情况可能发生了变化?):

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

    【讨论】:

    • 有趣的是,这个解决方案的赞成票很少,因为它的语法比当前最喜欢的要简单得多。
    • 它之所以有投票权是因为它依赖于强制解包枚举器(枚举器!)而不是枚举器?在投票最多的答案中。
    • 当心这是无法使用的大目录 - 它可能会挂起几分钟。
    • 停止强制展开。
    【解决方案3】:

    如果你得到了

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

    while 循环应该是:

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

    optional chaining有关

    【讨论】:

      【解决方案4】:

      为 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 ....
          }
      }
      

      【讨论】:

        【解决方案5】:

        斯威夫特 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 的代码,它按预期工作。
        【解决方案6】:

        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的代码

        【讨论】:

          【解决方案7】:

          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
          }
          

          【讨论】:

            【解决方案8】:

            现在(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的列表,看developer.apple.com/documentation/foundation/urlresourcekey
            • 这是一个很好的例子,说明了如何执行深度搜索,对我来说效果很好。感谢您的精彩贡献。
            • 这段代码很有帮助。但是我想至少在根文件夹中的第一个子级目录中获得遍历文件的进度(当然,我知道在我们枚举所有文件之前,我们无法获得确切的文件总数。因此,它会很好了解“至少第一个子级别”的获取进度。)。有什么办法可以取得进展吗?
            【解决方案9】:

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

            import Foundation
            
            let path = "<some path>"
            
            let enumerator = FileManager.default.enumerator(atPath: path)
            
            while let filename = enumerator?.nextObject() as? String {
                    print(filename)
            }
            

            【讨论】:

            • 它不会进入子目录,也不会获取隐藏文件,也不会跟随符号链接
            • 我们如何获取完整路径而不是文件名?
            • 你使用什么样的路径?就我而言,我在 xcode 项目中有“mydir”目录。当我使用let path = "mydir" 时,什么都没有发生。我正在使用 swift 5。
            • 这是一个完整的路径。有没有办法使用相对路径,以便它可以包含在项目文件夹中?
            【解决方案10】:

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

            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
                }
            }
            

            【讨论】:

              【解决方案11】:

              补充到 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

              【讨论】:

                【解决方案12】:

                最近在处理一组 url 时遇到了这个问题,无论它们是否是目录(例如拖放)。在 swift 4 中结束了这个扩展,可能有用

                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
                    }
                }
                

                【讨论】:

                  【解决方案13】:

                  避免使用引用 URL,虽然它们确实具有如上所述的一些优势,但它们会占用系统资源,如果您要枚举大型文件系统(实际上并没有那么大),您的应用程序将很快撞到系统墙并被 macOS 关闭。

                  【讨论】:

                  • 不回答这个问题,这应该更像是一个评论而不是一个答案。
                  【解决方案14】:

                  我以前回答的两分钱.. 更快捷且有可选性:

                   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")
                              }
                          }
                  
                      }
                  

                  【讨论】:

                  • 这是这里最有用的条目,因为它突出显示了按目录和文件类型挑选条目。这似乎是此类功能中被遗忘的部分。
                  猜你喜欢
                  • 1970-01-01
                  • 2017-04-06
                  • 2014-09-28
                  • 2023-01-20
                  • 2018-12-20
                  • 1970-01-01
                  • 2017-02-08
                  相关资源
                  最近更新 更多