Durchlaufen Sie Dateien in einem Ordner und seinen Unterordnern mit Swifts FileManager

76

Ich bin ziemlich neu in der Programmierung eines Swift und versuche, die Dateien in einem Ordner zu durchlaufen. Ich habe mir die Antwort hier angesehen und versucht, sie in die Swift-Syntax zu übersetzen, aber es gelang mir nicht.

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

for element in enumerator {
    //do something
}

Der Fehler, den ich bekomme, ist:

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

Mein Ziel ist es, alle im Hauptordner enthaltenen Unterordner und Dateien zu betrachten und alle Dateien mit einer bestimmten Erweiterung zu finden, um dann etwas mit ihnen zu tun.

Iacopo Boccalari
quelle

Antworten:

85

Verwenden Sie die nextObject()Methode von enumerator:

while let element = enumerator?.nextObject() as? String {
    if element.hasSuffix("ext") { // checks the extension
    }
}
pNre
quelle
Dies könnte ohne optionale Verkettung nicht funktionieren, wie in der Antwort von user1398498 unten angegeben.
Björn
2
Nachdem ich das gerade ausprobiert hatte, bekam ich nichts als eine Endlosschleife und musste mich grob an Rainiers Ansatz halten.
nhgrif
Sehr seltsam, dass eine eher rohe whilegegenüber der dichten forLösung bevorzugt wird. (Oh, ich sehe, ich habe die andere Lösung ohnehin schon ähnlich kommentiert - sowieso)
qwerty_so
9
enumerator(at:, includingPropertiesForKeys:, options:)Berücksichtigen Sie bei der Verwendung , dass das Element tatsächlich eine URL und keine Zeichenfolge ist. Wenn Sie diesen Code verwenden, wird nichts zurückgegeben. while let element = enumerator?.nextObject() as? URL {...}
Rodrigo Gonzalez
Gute Referenz. Ich habe es auch über den folgenden Link gefunden. developer.apple.com/documentation/foundation/filemanager/…
arango_86
62

Heutzutage (Anfang 2017) wird dringend empfohlen, die - vielseitigere - URL-bezogene API zu verwenden

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)
}
vadian
quelle
Vielen Dank für Ihre Gründlichkeit. (Ich hatte überhaupt kein Glück mit der pfadtypisierten Version, da myURL.absolutePath kein gültiger stringbasierter Pfad für die Zwecke von NSFileEnumerator ist und keinen gültigen Enumerator erstellt.) Die Liste der URLResourceKeys finden Sie unter developer.apple.com/documentation/foundation/urlresourcekey
green_knight
Dies ist ein gutes Beispiel für die Durchführung einer tiefen Suche und hat für mich hervorragend funktioniert. Vielen Dank für den schönen Beitrag.
Buddhisthead
18

Ich konnte die Lösung von pNre überhaupt nicht zum Laufen bringen. Die while-Schleife hat einfach nie etwas erhalten. Ich bin jedoch auf diese Lösung gestoßen, die für mich funktioniert (in Xcode 6 Beta 6 haben sich die Dinge vielleicht geändert, seit pNre die obige Antwort veröffentlicht hat?):

for url in enumerator!.allObjects {
    print("\((url as! NSURL).path!)")
}
Rainier Wolfcastle
quelle
1
Komisch, dass diese Lösung so wenige Up-Votes hat, da sie eine ziemlich einfachere Syntax als der aktuelle Favorit hat.
qwerty_so
2
der Grund, warum es Stimmen hat, weil es auf dem erzwungenen Auspacken des Enumerators (Enumerator!) im Gegensatz zum Enumerator beruht? in der oben gewählten Antwort.
Boris Suworow
Beachten Sie, dass dies unbrauchbare große Verzeichnisse sind - es kann Minuten hängen bleiben.
Abhi Beckert
Stoppen Sie das gewaltsame Auspacken.
Mark Perkins
9

Gibt alle Dateien in einem Verzeichnis + in Unterverzeichnissen zurück

import Foundation

let path = "<some path>"

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

while let filename = enumerator?.nextObject() as? String {
        print(filename)
}
Alex Shubin
quelle
2
Es wird nicht in Unterverzeichnisse gehen, keine versteckten Dateien erhalten oder symbolischen Links folgen
vomi
Wie erhalten wir den vollständigen Pfad anstelle der Dateinamen?
Ikel
Was für einen Weg benutzt du? In meinem Fall habe ich das Verzeichnis "mydir" im xcode-Projekt. Wenn ich benutze let path = "mydir", passiert nichts. Ich benutze schnelle 5.
Midtownguru
Es ist ein voller Weg. Gibt es eine Möglichkeit, einen relativen Pfad zu verwenden, damit er im Projektordner enthalten sein kann?
Midtownguru
8

Swift3 + absolute 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
    }
}

Basierend auf dem Code von @ user3441734

neoneye
quelle
6

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)
    }
})
user3441734
quelle
Als ich diesen Code verwendete, stellte ich fest, dass Verzeichnisse / Dateien mit Leerzeichen im Pfad ignoriert wurden. Irgendeine Idee, warum das passiert? Ich habe dann @ vadians Code ausprobiert und er funktioniert wie erwartet.
ATutorMe
5

Für den Fall, dass Sie die bekommen

"NSDirectoryEnumerator?" hat kein Mitglied mit dem Namen 'nextObject' Fehler

Die while-Schleife sollte sein:

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

Es hat etwas mit optionaler Verkettung zu tun

user1398498
quelle
4

SWIFT 3.0

Gibt alle Dateien mit der Erweiterung im übergebenen Verzeichnis und seinen Unterverzeichnissen zurück

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
}
Ranjith
quelle
4

meine zwei Cent aus früheren Antworten .. schneller und mit Optionen:

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

    }
ingconti
quelle
2

Aktualisierung für 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 ....
    }
}
dawg
quelle
1

Hinzufügen zur Antwort von vadian - In den Apple-Dokumenten wird erwähnt, dass pfadbasierte URLs in gewisser Weise einfacher sind. Dateireferenz-URLs haben jedoch den Vorteil, dass die Referenz gültig bleibt, wenn die Datei verschoben oder umbenannt wird, während Ihre App ausgeführt wird.

Aus der Dokumentation zu "Zugriff auf Dateien und Verzeichnisse":

"Pfadbasierte URLs sind einfacher zu bearbeiten, einfacher zu debuggen und werden im Allgemeinen von Klassen wie NSFileManager bevorzugt. Ein Vorteil von Dateireferenz-URLs besteht darin, dass sie weniger anfällig sind als pfadbasierte URLs, während Ihre App ausgeführt wird. Wenn der Benutzer Wenn eine Datei im Finder verschoben wird, werden alle pfadbasierten URLs, die auf die Datei verweisen, sofort ungültig und müssen auf den neuen Pfad aktualisiert werden. Solange die Datei jedoch an einen anderen Speicherort auf derselben Festplatte verschoben wird, wird ihre eindeutige ID nicht verwendet ändern und alle Dateireferenz-URLs bleiben gültig. "

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

Dougzilla
quelle
0

Wenn Sie kategorisch prüfen möchten, ob ein Element eine Datei oder ein Unterverzeichnis ist:

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
    }
}
Chris Amelinckx
quelle
0

Vor kurzem hatte ich Probleme damit, wenn ich ein Array von URLs behandelte, unabhängig davon, ob es sich um ein Verzeichnis handelt oder nicht (z. B. Drag & Drop). Endete mit dieser Erweiterung in Swift 4, kann von Nutzen sein

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
    }
}
Joe Maher
quelle
-1

Vermeiden Sie Referenz-URLs, obwohl sie, wie oben angegeben, einige Vorteile haben. Sie verbrauchen Systemressourcen. Wenn Sie ein großes Dateisystem aufzählen (das eigentlich nicht so groß ist), stößt Ihre App schnell an eine Systemwand und wird von macOS heruntergefahren.

David Hitchen
quelle
Beantwortet die Frage nicht, dies sollte eher ein Kommentar als eine Antwort sein.
TruMan1