C#如何循环遍历与目标匹配的源文件夹或子文件夹

本文关键字:文件夹 源文件 目标 何循环 循环 遍历 | 更新日期: 2023-09-27 18:25:46

我需要将文件从源目录覆盖到目标目录。每个文件夹的结构都不一样,所以我试着用一种通用的方式来做。问题是,每个文件夹(源和目标)可能有很多子目录,或者根本没有。

我目前拥有的代码是:

//copy and overwrite the files depending on whatever is in the destination
//search through the destination to find the file
foreach (var dstfile in Directory.GetFiles(targetDir))
{
  //search through the source to find the matching file
  foreach (var srcfile  in Directory.GetFiles(sourceDir))
  {
     //cut off the source file from the source path 
     strSrcFile = srcfile.Split(Path.DirectorySeparatorChar).Last();
     strDstFile = dstfile.Split(Path.DirectorySeparatorChar).Last();
    //if the destination and source files match up, replace the desination with the source
    if (strSrcFile == strDstFile)
    {
      File.Copy(srcfile, Path.Combine(targetDir, Path.GetFileName(strSrcFile)), true);
    }
  }
}

//look through the subfolders to see if any files match up 
foreach (var srcFolder in Directory.GetDirectories(sourceDir))
{
   //search through the source for the files 
   foreach (var srcFile in Directory.GetFiles(srcFolder))
   {
      //search through the destination for the files 
      foreach (var dstFile in Directory.GetFiles(targetDir))
      {

正如你所看到的,有很多前臂环,有没有一种方法可以简化它?

C#如何循环遍历与目标匹配的源文件夹或子文件夹

生成目标目录的哈希(字典),然后遍历源目录,查看文件是否已经存在。

Dictionary<string,string> lut1 = new Dictionary<string,string>();
foreach (var dstfile in Directory.GetFiles(targetDir))
{
   strDstFile = dstfile.Split(Path.DirectorySeparatorChar).Last();
   lut1 [strDstFile ] = dstfile;
}
foreach (var srcfile  in Directory.GetFiles(sourceDir))
{
   strSrcFile = srcfile.Split(Path.DirectorySeparatorChar).Last();
   string dstfile;
   if (lut1.TryGetValue(strSrcFile,  out dstfile)) {
       File.Copy( srcfile,dstfile,true);
   }
}       

我还没有测试过这个,但它应该可以工作(不是100%有效),至少应该给你一些指针

public void UpdateFiles(string directory, string otherDir)
{
   var dirFiles = Directory.EnumerateFiles(directory, "*", 
                        SearchOption.AllDirectories);
   var otherDirFiles = Directory.EnumerateFiles(otherDir, "*", 
                        SearchOption.AllDirectories);
   foreach (var file in dirFiles)
   {
       string fi = Path.GetFileName(file);
       var newFile = otherDirFiles.Where(x => fi == Path.GetFileName(x));
       foreach(var foundFile in newFile)
          File.Copy(file , foundFile, true);
   }
}

我刚刚在控制台应用程序中这样做。。。对它进行了测试,使其适用于主目标文件夹和子文件夹,尽管可能不是最有效的。

称之为:

OperateOnSourceFiles(sourceDir, targetDir);

它将检查源中的当前文件,然后递归地查找所有源子目录。

private static void OperateOnSourceFiles(string source, string targetDir)
{
    //Processes current source folder files
    foreach (var file in Directory.GetFiles(source))
    {
        OverWrite(targetDir, file);
    }
    //Recursively processes files in source subfolders
    List<string> subfolders = Directory.GetDirectories(source).ToList();
    foreach (var subfolder in subfolders)
    {
        OperateOnSourceFiles(subfolder, targetDir);
    }
}

然后你的覆盖功能可能看起来像这样:

private static void OverWrite(string target, string sourcefile)
{
    //Grab file name
    var strSrcFile = sourcefile.Split(Path.DirectorySeparatorChar).Last();
    //Search current target directory FILES, and copy only if same file name
    List<string> targetfiles = Directory.GetFiles(target).Select(file=>file.Split(Path.DirectorySeparatorChar).Last()).ToList();
    if (targetfiles.Contains(strSrcFile))
    {
        File.Copy(sourcefile, Path.Combine(target, Path.GetFileName(strSrcFile)), true);
    }
    //Recursively search current target directory SUBFOLDERS if any
    List<string> subfolders = Directory.GetDirectories(target).ToList();
    foreach (var subfolder in subfolders)
    {
        OverWrite(subfolder, sourcefile);
    }
}

请随时更正我:)

注意:我意识到它仍然有很多foreach循环,但至少它们没有嵌套,并且在调试时更容易。

我喜欢这个主意,所以我自己尝试了一下。结果比我想象的要复杂一些。让我们深入潜水,好吗?

基本思想是同步目录,因此我们希望引用DirectoryInfo实例。

var source = new DirectoryInfo(@"C:'SynchSource");
var target = new DirectoryInfo(@"C:'SynchTarget");
Synchronize(source, target);

Synchronize基本上以以下方式工作:

  • 确保所有文件都相同
  • 确保所有目录都相同
  • 遍历所有子目录并遍历

我的实现看起来是这样的:

void Synchronize(DirectoryInfo sourceDir, DirectoryInfo targetDir)
{
    SynchronizeFiles(sourceDir, targetDir);
    SynchronizeDirectories(sourceDir, targetDir);
    TraverseDirectories(sourceDir, targetDir);
}

注意.Single()——我们永远不能假设只有一个人/进程在目录中工作。

SynchronizeFiles做两件事:

  1. 将当前目录中的所有文件复制/覆盖到目标目录中
  2. 删除源目录中不再存在的冗余文件
void MoveFiles(DirectoryInfo sourceDir, DirectoryInfo targetDir)
{
    foreach (FileInfo sourceFile in sourceDir.GetFiles())
    {
        string targetFilePath = Path.Combine(targetDir.FullName, sourceFile.Name);
        File.Copy(sourceFile.FullName, targetFilePath);
    }
}
void RemoveRedundantFiles(DirectoryInfo sourceDir, DirectoryInfo targetDir)
{
    foreach (var targetFile in targetDir.GetFiles())
    {
        var sourceFilePath = Path.Combine(sourceDir.FullName, targetFile.Name);
        if (!File.Exists(sourceFilePath))
        {
            targetFile.Delete();
        }
    }
}

我们现在可以假设当前目录中的所有文件都是相同的,不多也不少。为了遍历子目录,我们首先必须确保目录结构是相同的。我们以类似于SynchronizeFiles:的方式进行

  1. 在目标目录中创建丢失的目录(CreateMissingDirectories
  2. 删除源目录中不再存在的冗余目录(RemoveRedundantDirectories
void CreateMissingDirectories(DirectoryInfo sourceDir, DirectoryInfo targetDir)
{
    foreach (DirectoryInfo sourceSubDir in sourceDir.GetDirectories())
    {
        string targetSubDirPath = Path.Combine(targetDir.FullName, sourceSubDir.Name);
        if (!Directory.Exists(targetSubDirPath))
        {
            Directory.CreateDirectory(targetSubDirPath);
        }
    }
}
void RemoveRedundantDirectories(DirectoryInfo sourceDir, DirectoryInfo targetDir)
{
    foreach (DirectoryInfo targetSubDir in targetDir.GetDirectories())
    {
        string sourceSubDirPath = Path.Combine(sourceDir.FullName, targetSubDir.Name);
        if (!Directory.Exists(sourceSubDirPath))
        {
            targetSubDir.Delete(true);
        }
    }
}

我们处于当前层次结构级别中的文件和目录相等的状态。我们现在可以遍历所有子目录并调用Synchronize:

void TraverseDirectories(DirectoryInfo sourceDir, DirectoryInfo targetDir)
{
    foreach (DirectoryInfo sourceSubDir in sourceDir.GetDirectories())
    {
        DirectoryInfo targetSubDir = targetDir.GetDirectories(sourceSubDir.Name).Single();
        Synchronize(sourceSubDir, targetSubDir);
    }
}

我们完了。

对于庞大的目录层次结构、大量或大型文件,甚至在目录中工作的并发进程,都有很大的改进空间。要使其快速(您可能想要缓存GetFiles/GetDirectories),跳过不必要的File.Copy调用(在假设需要副本之前获取文件哈希),还有很多工作要做。

顺便说一句:除了根据需要不时同步文件之外,您可能还想看看FileSystemWatcher,它可以递归地检测所选目录中的所有更改。