Итерация по файлам в папке и ее подпапках с помощью SWIFT FileManager

Я довольно новыми для программирования 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'

моя цель-посмотреть все подпапки и файлы, содержащиеся в основной папке, и найти все файлы с определенным расширением, чтобы затем что-то с ними сделать.

13 ответов


использовать nextObject() метод enumerator:

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

В настоящее время (в начале 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)
}

Я не мог заставить решение pNre работать вообще; цикл while просто ничего не получил. Однако я столкнулся с этим решением, которое работает для меня (в Xcode 6 beta 6, так что, возможно, все изменилось с тех пор, как pNre опубликовал вышеуказанный ответ?):

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

в случае, если вы получаете

' NSDirectoryEnumerator?' не имеет элемент с именем 'nextObject'

цикл while должен быть:

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

Это как-то связано с дополнительные цепочки


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

Swift3 + абсолютные urls

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


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
}

возвращает все файлы в директории

Swift 3

let fileNames = try! FileManager.default.contentsOfDirectory(atPath: "/Volumes/Seagate/Path/")

for fileName in fileNames {

    print(fileName)

}

обновление для 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 ....
    }
}

добавление к ответу vadian - в документах Apple упоминается, что URL-адреса на основе пути в некотором роде проще, однако URL-адреса ссылок на файлы имеют то преимущество, что ссылка остается действительной, если файл перемещен или переименован во время работы приложения.

из документации для "доступа к файлам и каталогам":

"URL-адреса на основе пути легче манипулировать, легче отлаживать и, как правило, предпочитают такие классы, как NSFileManager. Преимущество ссылки на файл URL-адреса являются менее хрупкими, чем URL-адреса на основе пути во время работы приложения. Если пользователь перемещает файл в Finder, любые URL-адреса на основе пути, ссылающиеся на файл, немедленно становятся недействительными и должны быть обновлены до нового пути. Однако до тех пор, пока файл перемещается в другое место на том же диске, его уникальный идентификатор не изменяется и все ссылки на файлы остаются url действительный."

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


Если вы хотите категорически проверить, является ли элемент файлом или подкаталогом:

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

недавно боролся с этим при обращении массив 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
    }
}

избегайте ссылочных URL-адресов, хотя у них есть некоторые преимущества, как указано выше, они едят системные ресурсы, и если вы перечисляете большую файловую систему (не такую большую на самом деле), ваше приложение быстро ударит по системной стене и выключится macOS.