Lặp lại các tệp trong một thư mục và các thư mục con của nó bằng cách sử dụng FileManager của Swift


76

Tôi khá mới lập trình Swift và tôi đang cố gắng lặp lại các tệp trong một thư mục. Tôi đã xem câu trả lời ở đây và cố gắng dịch nó sang cú pháp Swift, nhưng không thành công.

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

for element in enumerator {
    //do something
}

lỗi tôi nhận được là:

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

Mục đích của tôi là xem xét tất cả các thư mục con và tệp chứa trong thư mục chính và tìm tất cả các tệp có một phần mở rộng nhất định để sau đó làm điều gì đó với chúng.

Câu trả lời:


85

Sử dụng nextObject()phương pháp enumerator:

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

Không thể làm cho điều này hoạt động mà không có chuỗi tùy chọn theo câu trả lời của người dùng1398498 bên dưới.
Bjorn

2
Chỉ mới thử điều này, tôi chẳng nhận được gì ngoài một vòng lặp vô hạn và phải đi với cách tiếp cận của Rainier.
nhgrif

Rất lạ là một dung dịch khá thô lại whileđược ưa chuộng hơn là fordung dịch đậm đặc . (Ồ, tôi thấy tôi đã nhận xét về giải pháp khác tương tự - dù sao)
qwerty_so

9
Đối với những người sử dụng, enumerator(at:, includingPropertiesForKeys:, options:)hãy lưu ý rằng phần tử thực sự là một URL không phải là một chuỗi, vì vậy nếu bạn sử dụng mã đó, nó sẽ không trả lại gì. while let element = enumerator?.nextObject() as? URL {...}
Rodrigo Gonzalez

Tham khảo tốt. Tôi cũng tìm thấy nó từ liên kết dưới đây. developer.apple.com/documentation/foundation/filemanager/…
arango_86

62

Ngày nay (đầu năm 2017), chúng tôi khuyên bạn nên sử dụng API liên quan đến URL - linh hoạt hơn

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

Cảm ơn bạn vì sự kỹ lưỡng của bạn. (Tôi không gặp may với phiên bản được gõ đường dẫn, vì myURL.absolutePath không phải là đường dẫn dựa trên chuỗi hợp lệ cho các mục đích của NSFileEnumerator và không tạo ra một liệt kê hợp lệ). Để biết danh sách URLResourceKeys, hãy xem developer.apple.com/documentation/foundation/urlresourcekey
green_knight

Đây là một ví dụ điển hình về cách thực hiện tìm kiếm sâu và rất hiệu quả đối với tôi. Cảm ơn bạn đã đóng góp tốt đẹp.
Buddhisthead

19

Tôi không thể làm cho giải pháp của pNre hoạt động; vòng lặp while không bao giờ nhận được bất cứ điều gì. Tuy nhiên, tôi đã tìm thấy giải pháp này phù hợp với tôi (trong Xcode 6 beta 6, vì vậy có lẽ mọi thứ đã thay đổi kể từ khi pNre đăng câu trả lời ở trên?):

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

1
Thật buồn cười là giải pháp này có rất ít lượt bình chọn vì nó có cú pháp khá dễ dàng hơn so với giải pháp ưa thích hiện tại.
qwerty_so

2
lý do tại sao nó có phiếu bầu bởi vì nó dựa vào việc buộc điều tra viên (điều tra viên!) được mở ra trái ngược với điều tra viên? trong câu trả lời được bình chọn hàng đầu.
Boris Suvorov

Hãy coi chừng đây là thư mục lớn không sử dụng được - nó có thể bị treo trong vài phút.
Abhi Beckert

Dừng buộc mở gói.
Mark Perkins

9

trả về tất cả các tệp trong một thư mục + trong các thư mục con

import Foundation

let path = "<some path>"

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

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

2
Nó sẽ không đi vào bên trong các thư mục con cũng như không nhận được các tệp ẩn cũng như đi theo các liên kết tượng trưng
vomi

làm thế nào để chúng tôi nhận được đường dẫn đầy đủ thay vì tên tệp?
ikel

Bạn sử dụng loại đường dẫn nào? Trong trường hợp của tôi, tôi có thư mục "mydir" trong dự án xcode. Khi tôi sử dụng let path = "mydir", không có gì xảy ra. Tôi đang sử dụng nhanh chóng 5.
midtownguru

Đó là một con đường đầy đủ. Có cách nào để sử dụng một đường dẫn tương đối để nó có thể được chứa trong thư mục dự án không?
midtownguru

8

Swift3 + url tuyệt đối

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

Dựa trên mã từ @ user3441734


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

Khi tôi sử dụng mã này, tôi thấy rằng các thư mục / tệp có khoảng trắng trong đường dẫn đã bị bỏ qua. Bất kỳ ý tưởng tại sao điều này xảy ra? Sau đó, tôi đã thử mã của @ vadian và nó hoạt động như mong đợi.
ATutorMe

5

Trong trường hợp bạn nhận được

'NSDirectoryEnumerator?' không có thành viên có tên lỗi 'nextObject'

vòng lặp while phải là:

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

Nó có liên quan đến chuỗi tùy chọn


5

hai xu của tôi từ các anwers trước đây .. swifty hơn và với các tùy chọn:

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

    }

Đây là mục nhập hữu ích nhất ở đây vì nó làm nổi bật việc chọn các mục nhập theo dir và loại tệp. Đó dường như là một phần rất bị lãng quên của chức năng như thế này.
ChrisH

4

SWIFT 3.0

Trả về tất cả các tệp có phần mở rộng trong Thư mục đã chuyển và các thư mục con của nó

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
}

2

Cập nhật cho 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

Thêm vào phản hồi của vadian - tài liệu của Apple đề cập rằng các URL dựa trên đường dẫn đơn giản hơn theo một số cách, tuy nhiên URL tham chiếu tệp có lợi thế là tham chiếu vẫn hợp lệ nếu tệp được di chuyển hoặc đổi tên trong khi ứng dụng của bạn đang chạy.

Từ tài liệu về "Truy cập Tệp và Thư mục":

"URL dựa trên đường dẫn dễ thao tác hơn, dễ gỡ lỗi hơn và thường được các lớp như NSFileManager ưa thích hơn. Một lợi thế của URL tham chiếu tệp là chúng ít mỏng manh hơn URL dựa trên đường dẫn khi ứng dụng của bạn đang chạy. Nếu người dùng di chuyển tệp trong Trình tìm kiếm, mọi URL dựa trên đường dẫn tham chiếu đến tệp ngay lập tức trở nên không hợp lệ và phải được cập nhật lên đường dẫn mới. Tuy nhiên, miễn là tệp được di chuyển đến một vị trí khác trên cùng một đĩa, ID duy nhất của nó sẽ không thay đổi và mọi URL tham chiếu tệp vẫn hợp lệ. "

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


0

Nếu bạn muốn kiểm tra phân loại xem một phần tử là tệp hay thư mục con:

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

Gần đây đã phải vật lộn với vấn đề này khi xử lý một mảng url, cho dù chúng có phải là một thư mục hay không (ví dụ: kéo và thả). Đã kết thúc với phần mở rộng này trong swift 4, có thể được sử dụng

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

Tránh các URL tham chiếu, mặc dù chúng có một số ưu điểm như đã nêu ở trên, nhưng chúng ăn tài nguyên hệ thống và nếu bạn đang liệt kê một hệ thống tệp lớn (thực tế không lớn như vậy) thì ứng dụng của bạn sẽ nhanh chóng chạm vào tường hệ thống và bị macOS tắt.


Không trả lời câu hỏi, đây phải là một bình luận hơn là một câu trả lời.
TruMan1
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.