1

我有一个物理目录结构:

根目录 (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 为单位。

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

  • 例如:照片(根)内的物理文件夹中是否存在任何名称为“1234567891_w.jpg”的文件。*

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

更新 :

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

4

4 回答 4

2

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

于 2012-10-17T08:05:46.100 回答
1

如果是这种情况,您可以执行类似的操作,这些文件似乎是唯一命名的

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,并且根据该集合的大小,它可能会在一定程度上影响系统,它不再优化应用程序的速度,而是通过一个最佳值。

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

于 2012-10-17T08:15:43.497 回答
1

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

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

由于文件名存在的概率仅在一个位置,即:

根目录/子目录/文件名

我应该使用:

File.Exists(根/子目录/文件名);

即 - 照片/123456789/1234567891_w.jpg

我认为这将是O(1)

于 2012-10-17T09:05:36.403 回答
0

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

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

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

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

更新

重新阅读您的问题和评论后,据我了解,您有一个类似的名称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);
}

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

于 2012-10-17T08:25:20.547 回答