【问题标题】:Iterating through files in a folder with nested folders - Cocoa遍历具有嵌套文件夹的文件夹中的文件 - Cocoa
【发布时间】:2011-08-10 14:33:17
【问题描述】:

我需要访问文件夹中的每个文件,包括嵌套文件夹中存在的文件。示例文件夹可能如下所示。

animals/
 -k.txt
 -d.jpg
 cat/
   -r.txt
   -z.jpg
   tiger/
      -a.jpg
      -p.pdf
 dog/
   -n.txt
   -f.jpg
 -p.pdf

假设我想对“动物”中不是文件夹的每个文件运行一个进程。遍历文件夹“animals”及其所有子文件夹以访问每个文件的最佳方法是什么?

谢谢。

【问题讨论】:

  • 为每个文件夹调用自身并查找文件的递归函数

标签: macos cocoa directory iteration


【解决方案1】:

也许你可以使用这样的东西:

+(void)openEachFileAt:(NSString*)path
{
  NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtPath:path];
  for (NSString * file in enumerator)
  {
     // check if it's a directory
     BOOL isDirectory = NO;
    NSString* fullPath = [path stringByAppendingPathComponent:file];
    [[NSFileManager defaultManager] fileExistsAtPath:fullPath
                                         isDirectory: &isDirectory];
    if (!isDirectory)
    {
      // open your file (fullPath)…
    }
    else
    {
      [self openEachFileAt: fullPath];
    }
  }
}

【讨论】:

  • 这能正常工作吗?枚举很深,因此它也会列出所有子目录的内容。我真的很想使用它,但有没有办法将枚举器限制为当前目录?
  • 只是因为我刚刚看到了这个评论:如果你不想要子目录就不要调用 [self openEachFileAt: file];再次。
  • 非常晚了,但只是想说谢谢你解决了我的一个主要问题。
【解决方案2】:

使用NSDirectoryEnumerator递归枚举你想要的目录下的文件和目录,并要求它告诉你是文件还是目录。以下内容基于-[NSFileManager enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:] 的文档中列出的示例:

NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *directoryURL = … // URL pointing to the directory you want to browse
NSArray *keys = [NSArray arrayWithObject:NSURLIsDirectoryKey];

NSDirectoryEnumerator *enumerator = [fileManager
    enumeratorAtURL:directoryURL
    includingPropertiesForKeys:keys
    options:0
    errorHandler:^BOOL(NSURL *url, NSError *error) {
        // Handle the error.
        // Return YES if the enumeration should continue after the error.
        return YES;
}];

for (NSURL *url in enumerator) { 
    NSError *error;
    NSNumber *isDirectory = nil;
    if (! [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:&error]) {
        // handle error
    }
    else if (! [isDirectory boolValue]) {
        // No error and it’s not a directory; do something with the file
    }
}

【讨论】:

  • 我相信初始化 NSFileManager 的更简洁的方法是使用单例方法 [NSFileManager defaultManager] 而不是 [[[NSFileManager alloc] init] autorelease]
  • 完美答案..!!谢谢。
  • 嗨,也许你知道是否有一种方法可以使用每个挂载点或文件系统而不是路径或 URL 来迭代文件?
【解决方案3】:

这是一个快速版本:

func openEachFile(inDirectory path: String) {
    let subs = try! FileManager.default.subpathsOfDirectory(atPath: path)
    let totalFiles = subs.count
    print(totalFiles)
    for sub in subs {
        if sub.hasSuffix(".DS_Store") {
            //a DS_Store file
        }
        else if sub.hasSuffix(".xcassets") {
            //a xcassets file
        }
        else if (sub as NSString).substring(to: 4) == ".git" {
            //a git file
        }
        else if sub.hasSuffix(".swift") {
            //a swift file
        }
        else if sub.hasSuffix(".m") {
            //a objc file
        }
        else if sub.hasSuffix(".h") {
            //a header file
        }
        else {
            // some other file
        }
        let fullPath = (path as NSString).appendingPathComponent(sub)
    }
}

【讨论】:

  • 我一直在寻找它,找不到它,然后我不得不解决它。我本可以问一个新问题并粘贴我的答案,或者只是将我的答案粘贴在这里。哪个更好?
  • 在此处留下这个答案,然后再创建一个问题并粘贴答案。记得标记 swift 而不是 obj-c 或 cocoa。
  • @AnoopVaidya 这个问题是在 Swift 公开 3 年前提出的;我敢肯定,如果同一个人今天(或者,甚至可能在 2015 年 6 月 30 日左右)问这个问题,他们更喜欢 Swift 的答案。
【解决方案4】:

这是一个使用 -subpathsOfDirectoryAtPath:rootPath 的解决方案,带有文件 URL 和现代 Objective-C 可空性花里胡哨。

typedef void (^FileEnumerationBlock)(NSURL *_Nonnull fileURL);

@interface NSFileManager (Extensions)

- (void)enumerateWithRootDirectoryURL:(nonnull NSURL *)rootURL
                          fileHandler:(FileEnumerationBlock _Nonnull)fileHandler
                                error:(NSError *_Nullable *_Nullable)error;

@end

@implementation NSFileManager (Extensions)

- (void)enumerateWithRootDirectoryURL:(NSURL *)rootURL
                          fileHandler:(FileEnumerationBlock)fileHandler
                                error:(NSError **)error {
    NSString *rootPath = rootURL.path;
    NSAssert(rootPath != nil, @"Invalid root URL %@ (nil path)", rootURL);

    NSArray *subs = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:rootPath
                                                                        error:error];

    if (!subs) {
        return;
    }

    for (NSString *sub in subs) {
        fileHandler([rootURL URLByAppendingPathComponent:sub]);
    }
}

@end

……在 Swift 中也是如此:

func enumerate(rootDirectoryURL rootURL: NSURL, fileHandler:(URL:NSURL)->Void) throws {
    guard let rootPath = rootURL.path else {
        preconditionFailure("Invalid root URL: \(rootURL)")
    }

    let subs = try NSFileManager.defaultManager().subpathsOfDirectoryAtPath(rootPath)
    for sub in subs {
        fileHandler(URL: rootURL.URLByAppendingPathComponent(sub))
    }
}

【讨论】:

    【解决方案5】:

    这段代码对我有用。

    NSMutableString *allHash;  
    
      -(NSString*)getIterate:(NSString*)path {
    
            allHash = [NSMutableString stringWithString:@""];
    
            NSDirectoryEnumerator *de= [[NSFileManager defaultManager] enumeratorAtPath:path];
            NSString *file;
            BOOL isDirectory;
    
            for(file in de)
            {
    
                //first check if it's a file
                NSString* fullPath = [NSString stringWithFormat:@"%@/%@",path,file];
    
                BOOL fileExistsAtPath = [[NSFileManager defaultManager] fileExistsAtPath:fullPath isDirectory:&isDirectory];
                NSLog(@"Check=>%d",fileExistsAtPath);
    
                if (!isDirectory) //its a file
                {
                    //Do with filepath
                }
                else{ //it's a folder, so recurse
                    [self enumerateFolder:fullPath];
                }
            }
    
            return allHash;
    
    
        }
    
        -(void) enumerateFolder:(NSString*)fileName
        {
    
            NSDirectoryEnumerator *de= [[NSFileManager defaultManager] enumeratorAtPath:fileName];
            NSString* file;
            BOOL isDirectory;
    
            for(file in de)
            {
                //first check if it's a file
                BOOL fileExistsAtPath = [[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory];
    
                if (fileExistsAtPath) {
                    if (!isDirectory) //its a file
                    { 
                      //Do with file  
                    }
                    else{ //it's a folder, so recurse
    
                        [self enumerateFolder:file];
                    }
                }
                else printf("\nenumeratefolder No file at path %s",[file UTF8String]);
            }
        }
    

    【讨论】:

      【解决方案6】:

      在 Swift 中我们可以尝试以下方法。

      let docsDir = NSHomeDirectory().appending("/Documents")
      let localFileManager = FileManager()
      
      let dirEnum = localFileManager.enumerator(atPath: docsDir)
      
      while let file = dirEnum?.nextObject() as? String {
          if file.hasSuffix(".doc") {
              print(docsDir.appending("/\(file)"))
          }
      }
      

      参考 https://developer.apple.com/documentation/foundation/filemanager/1408726-enumerator

      【讨论】:

        猜你喜欢
        • 2017-04-03
        • 1970-01-01
        • 2014-09-28
        • 1970-01-01
        • 2023-01-20
        • 2023-03-03
        • 2017-02-08
        • 1970-01-01
        相关资源
        最近更新 更多