【问题标题】:How do I compare one collection of files to another in c#?如何在 C# 中将一个文件集合与另一个文件集合进行比较?
【发布时间】:2009-12-06 10:59:55
【问题描述】:

我刚刚学习 C#(现在已经摆弄了大约 2 天)并且我决定,出于学习目的,我将重建我在 VB6 中制作的用于同步文件的旧应用程序(通常通过网络) .

当我在 VB 6 中编写代码时,它的工作方式大致如下:

  1. 创建Scripting.FileSystemObject
  2. 为源和目标创建目录对象
  3. 为源和目标创建文件列表对象
  4. 遍历源对象,并检查它是否存在于目标中
    • 如果没有,请创建它
    • 如果是,请检查源版本是否更新/更大,如果是,则覆盖另一个

到目前为止,这就是我所拥有的:

private bool syncFiles(string sourcePath, string destPath) {
    DirectoryInfo source = new DirectoryInfo(sourcePath);
    DirectoryInfo dest = new DirectoryInfo(destPath);

    if (!source.Exists) {
        LogLine("Source Folder Not Found!");
        return false;
    }

    if (!dest.Exists) {
        LogLine("Destination Folder Not Found!");
        return false;
    }

    FileInfo[] sourceFiles = source.GetFiles();
    FileInfo[] destFiles = dest.GetFiles();

    foreach (FileInfo file in sourceFiles) {
        // check exists on file
    }

    if (optRecursive.Checked) {
        foreach (DirectoryInfo subDir in source.GetDirectories()) {
            // create-if-not-exists destination subdirectory
            syncFiles(sourcePath + subDir.Name, destPath + subDir.Name);
        }
    }
    return true;
}

我已经阅读了似乎提倡使用 FileInfo 或 DirectoryInfo 对象对“Exists”属性进行检查的示例,但我专门寻找一种方法来搜索现有的文件集合/列表,而不是实时检查每个文件的文件系统,因为我将通过网络这样做并不断返回到一个包含数千个文件的目录,这很慢。

提前致谢。

【问题讨论】:

    标签: c# fileinfo directoryinfo


    【解决方案1】:

    GetFiles() 方法只会获取确实存在的文件。它不会组成不存在的随机文件。所以你所要做的就是检查它是否存在于另一个列表中。

    这行中的某些东西可能会起作用:

    var sourceFiles = source.GetFiles();
    var destFiles = dest.GetFiles();
    
    foreach (var file in sourceFiles)
    {
        if(!destFiles.Any(x => x.Name == file.Name))
        {
            // Do whatever
        }
    }
    

    注意:您当然不能保证在您致电GetFiles() 后没有任何变化。例如,如果您稍后尝试复制某个文件,该文件可能已被删除或重命名。


    也许可以通过使用Except 方法或类似的方法做得更好。例如这样的:

    var sourceFiles = source.GetFiles();
    var destFiles = dest.GetFiles();
    
    var sourceFilesMissingInDestination = sourceFiles.Except(destFiles, new FileNameComparer());
    
    foreach (var file in sourceFilesMissingInDestination)
    {
        // Do whatever
    }
    

    FileNameComparer 的实现方式如下:

    public class FileNameComparer : IEqualityComparer<FileInfo>
    {
        public bool Equals(FileInfo x, FileInfo y)
        {
            return Equals(x.Name, y.Name);
        }
    
    
        public int GetHashCode(FileInfo obj)
        {
            return obj.Name.GetHashCode();
        }
    }     
    

    虽然未经测试:p

    【讨论】:

    • 对于子目录也使用 Path.Combine(sourcePath,subDir.Name) 而不是 sourcePath + subDir.Name
    • 也看看GetFileSystemInfos方法:msdn.microsoft.com/en-us/library/…
    • 至于您的注释:我理解,但这是我正在承担的风险,预先加载获取文件列表的操作,因此我不必对无数个文件进行单独的存在检查。非常感谢您的回答,我要去检查“Any”的语法!
    • 从 DirectoryInfo 中获取所有目标文件更快。不要在所有目标文件上调用 Exists,但要检查文件打开调用是否成功,因为文件可以被删除或重命名,就像 Svish 解释的那样。
    • @Martinho:这是个好主意。试图想出像这样聪明的东西,但我的大脑让我失望了:p
    【解决方案2】:

    一个小细节,而不是

     sourcePath + subDir.Name
    

    我会用

     System.IO.Path.Combine(sourcePath, subDir.Name)
    

    Path 对文件名和文件夹名进行可靠的、独立于操作系统的操作。

    我还注意到optRecursive.Checked 突然冒出来。作为一个好的设计,把它作为一个参数:

    bool syncFiles(string sourcePath, string destPath, bool checkRecursive)
    

    由于您提到它可能用于大量文件,因此请留意 .NET 4,它有一个 GetFiles() 的 IEnumerable 替代品,可以让您以流方式处理它。

    【讨论】:

    • +1 表示“而且由于您提到它可能用于大量文件,请留意 .NET 4,它有一个 GetFiles() 的 IEnumerable 替代品,可以让您处理这是一种流媒体方式。”
    • +1 使用Path.Combine 可以帮助您解决结尾斜线存在有时不存在的问题。这让生活变得痛苦。
    • 我也喜欢使用Path.Combine进行这种连接,但是在这种情况下,为什么不直接使用subDir.FullName呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-22
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    相关资源
    最近更新 更多