【问题标题】:Fast way to check existance of a file in a directory with filename检查具有文件名的目录中是否存在文件的快速方法
【发布时间】:2012-10-07 10:24:09
【问题描述】:

我有一个物理目录结构:

根目录 (X) -> 侧根中的许多子目录 (1,2,3,4..) -> 在每个子目录中存在许多文件。

Photos(Root)    
            ----        
               123456789(Child One)
                 ----
                     1234567891_w.jpg (Child two)
                     1234567891_w1.jpg(Child two)
                     1234567891_w2.jpg(Child two)
                     1234567892_w.jpg (Child two)
                     1234567892_w1.jpg(Child two)
                     1234567892_w2.jpg(Child two)
                     1234567893_w.jpg(Child two)
                     1234567893_w1.jpg(Child two)
                     1234567893_w2.jpg(Child two)
                     -----Cont      
              232344343(Child One)      
              323233434(Child One)      
              232323242(Child One)      
              232324242(Child One)      
              ----Cont..

在数据库中,我有一个表,其中包含大量“1234567891_w.jpg”类型的名称。

注意:数据库中的数据数量和照片数量均以 lacs 为单位。

我需要一种有效且更快的方法来检查从数据库表到物理目录结构的每个名称是否存在。

  • Ex : Photos (Root) 内的物理文件夹中是否存在任何名称为“1234567891_w.jpg”的文件。*

如果我错过了此处提供的任何信息,请告诉我。

更新:

我知道如何在目录中查找文件名。但我正在寻找一种有效的方法,因为在超过 40 GB 的数据中检查每个文件名(来自记录的 lacs)存在性会消耗太多资源。

【问题讨论】:

标签: c# .net search directory directorysearcher


【解决方案1】:

您可以尝试根据数据所在的目录对数据库中的数据进行分组。以某种方式对它们进行排序(例如基于文件名),然后获取该目录中的文件数组 string[] filePaths = Directory.GetFiles(@"c:\MyDir\");。现在你只需要比较字符串。

【讨论】:

  • 是的。如果没有找到更好的方法,这应该是我的实现方式。你可以看到“1234567891_w.jpg”文件名的前9位是子目录名。
【解决方案2】:

如果你可以这样做的话,文件似乎是唯一命名的

var fileNames = GetAllFileNamesFromDb();
var physicalFiles = Directory.GetFiles(rootDir, 
                                        string.Join(",",fileNames),
                                        SearchOptions.AllDirectories)
                                        .Select(f=>Path.GetFileName(f));
var setOfFiles = new Hashset<string>(physicalFiles);
var notPresent = from name in fileNames
                 where setOfFiles.Contains(name)
                 select name;
  • 首先从数据库中获取所有文件的名称
  • 然后一次搜索所有文件,从根目录开始搜索,包括所有子目录,得到所有物理文件
  • 为快速查找创建哈希集
  • 然后将文件名与集合匹配,那些不在集合中的将被选中。

Hashset 基本上只是一个集合。这是一个只能包含一个项目一次的集合(即没有重复项) Hashset 中的相等性基于 HashCode 并且确定项目是否在集合中的查找是 O(1)。

这种方法要求您在内存中存储一​​个可能非常大的 Hashset,并且根据该集合的大小,它可能会在一定程度上影响系统,使其不再优化应用程序的速度,而是通过一个最佳值。

与大多数优化一样,它们都是权衡取舍,关键是在应用程序为最终用户产生的价值的背景下找到所有权衡取舍之间的平衡

【讨论】:

  • 仅供参考-所有文件名都是唯一的,我必须更多地探索这个 Hashset 选项,因为我不知道它..确实是一个很好的方法。
  • @Learner 更新了更多关于 Hashset 的信息并更正了代码中的一些错误
【解决方案3】:

不幸的是,它们并不是你可以用来提高表现的灵丹妙药。与往常一样,这将是速度和内存之间的权衡。此外,它们还有两个方面可能缺乏性能:数据库站点和硬盘驱动器 i/o 速度。

因此,为了提高速度,我首先会提高数据库查询的性能,以确保它可以足够快地返回名称以进行搜索。因此,请确保您的查询速度很快,并且可能使用(im MS SQL case)关键字,如@9​​87654321@,在这种情况下,您将在查询仍在运行时检索到第一个结果,您不必等到查询完成并给你一个大块的名字。

在硬盘的另一端,您可以调用Directory.GetFiles(),但此调用会阻塞,直到它遍历所有文件并返回一个包含所有文件名的大数组。这将是消耗内存的路径,第一次搜索需要一段时间,但如果您之后只处理该数组,您将获得所有连续搜索的速度改进。另一种方法是调用Directory.EnumerateFiles(),它会在每次调用时动态搜索驱动器,因此可能会为第一次搜索获得速度,但它们不会为下一次搜索发生任何内存存储,这会提高内存占用但会降低速度,因为它们在你的记忆中没有可以被搜索到的数组。另一方面,如果检测到您一遍又一遍地迭代相同的文件并且某些缓存发生在较低级别上,操作系统也会进行一些缓存。

因此,如果返回的数组不会破坏您的记忆并对此进行所有搜索,请使用Directory.GetFiles() 检查硬盘站点(如果仅文件名或完整路径,可以将其放入HashSet 以进一步提高性能取决于您从数据库中获得的信息),在另一种情况下使用 Directory.EnumerateFiles() 并希望对某些缓存最好的是操作系统。

更新

在重新阅读您的问题和 cmets 后,据我了解,您有一个类似 1234567891_w.jpg 的名称,并且您不知道名称的哪个部分代表目录部分。因此,在这种情况下,您需要进行显式搜索,因为遍历所有目录只需要很长时间。下面是一些示例代码,它应该让您了解如何在第一时间解决这个问题:

string rootDir = @"D:\RootDir";

// Iterate over all files reported from the database
foreach (var filename in databaseResults)
{
    var fullPath = Path.Combine(rootDir, filename);

    // Check if the file exists within the root directory
    if (File.Exists(Path.Combine(rootDir, filename)))
    {
        // Report that the file exists.
        DoFileFound(fullPath);
        // Fast exit to continue with next file.
        continue;
    }

    var directoryFound = false;

    // Use the filename as a directory
    var directoryCandidate = Path.GetFileNameWithoutExtension(filename);
    fullPath = Path.Combine(rootDir, directoryCandidate);

    do
    {
        // Check if a directory with the given name exists
        if (Directory.Exists(fullPath))
        {
            // Check if the filename within this directory exists
            if (File.Exists(Path.Combine(fullPath, filename)))
            {
                // Report that the file exists.
                DoFileFound(fullPath);
                directoryFound = true;
            }

            // Fast exit, cause we looked into the directory.
            break;
        }

        // Is it possible that a shorter directory name
        // exists where this file exists??
        // If yes, we have to continue the search ...
        // (Alternative code to the above one)
        ////// Check if a directory with the given name exists
        ////if (Directory.Exists(fullPath))
        ////{
        ////    // Check if the filename within this directory exists
        ////    if (File.Exists(Path.Combine(fullPath, filename)))
        ////    {
        ////        // Report that the file exists.
        ////        DoFileFound(fullPath);

        ////        // Fast exit, cause we found the file.
        ////        directoryFound = true;
        ////        break;
        ////    }
        ////}

        // Shorten the directory name for the next candidate
        directoryCandidate = directoryCandidate.Substring(0, directoryCandidate.Length - 1);
    } while (!directoryFound
              && !String.IsNullOrEmpty(directoryCandidate));

    // We did our best but we found nothing.
    if (!directoryFound)
        DoFileNotAvailable(filename);
}

我能想到的唯一进一步的性能改进是将找到的目录放入HashSet 并在检查Directory.Exists() 之前使用它来检查现有目录,但也许这不会获得任何好处,因为操作系统已经在目录查找中进行了一些缓存,然后几乎与本地缓存一样快。但是对于这些事情,您只需要衡量您的具体问题。

【讨论】:

  • 是的,它是 MS SQL..但是由于查询只会执行一次以获取所有文件名,我认为这不是一个值得关注的领域..我在想我们是否可以有一些一次在 DIRECTORY 和 SUBDIRECTORY 中搜索文件的有效逻辑..
  • @Learner:Directory 的两种方法都有一个重载,它接受一个可以设置为 SearchOption.AllDirectoriesSearchOption,导致对给定目录和所有子目录进行搜索。跨度>
  • 是的..你能谈谈我找到解决这个问题的方式,我添加的答案..我是否遗漏了什么?
【解决方案4】:

这听起来可能很有趣,或者可能是我不清楚或没有提供太多信息..

但是从目录模式中我得到了一种处理它的好方法:

因为文件名存在的概率只在一个位置,那就是:

根目录/子目录/文件名

我应该使用:

File.Exists(Root/SubDir/filename);

即 - 照片/123456789/1234567891_w.jpg

我认为这将是 O(1)

【讨论】:

    猜你喜欢
    • 2011-09-23
    • 1970-01-01
    • 1970-01-01
    • 2015-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多