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))
{
正如你所看到的,有很多前臂环,有没有一种方法可以简化它?
生成目标目录的哈希(字典),然后遍历源目录,查看文件是否已经存在。
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
做两件事:
- 将当前目录中的所有文件复制/覆盖到目标目录中
- 删除源目录中不再存在的冗余文件
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
:的方式进行
- 在目标目录中创建丢失的目录(
CreateMissingDirectories
) - 删除源目录中不再存在的冗余目录(
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
,它可以递归地检测所选目录中的所有更改。