我想在C#中将一个目录的所有内容从一个位置复制到另一个位置。
使用System.IO
类似乎没有一种不需要大量递归的方法来实现此目的。
如果我们添加对Microsoft.VisualBasic
的引用,则可以使用VB中的一种方法:
new Microsoft.VisualBasic.Devices.Computer().
FileSystem.CopyDirectory( sourceFolder, outputFolder );
这似乎是一个相当丑陋的黑科技。有没有更好的方法?
private static void CopyFilesRecursively(string sourcePath, string targetPath)
{
//Now Create all of the directories
foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
{
Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath));
}
//Copy all the files & Replaces any files with the same name
foreach (string newPath in Directory.GetFiles(sourcePath, "*.*",SearchOption.AllDirectories))
{
File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true);
}
}
"sourceDir/things/sourceDir/things"
, 使用 Replace
就会出现问题。应该将它改为 "destinationDir/things/sourceDir/things"
,但是如果使用 replace
,结果会变成 "destinationDir/things/destinationDir/things"
。请注意不要改变原文的意思,并尽可能地使翻译通俗易懂。 - Keith*.*
而不是 *
?难道你不想复制没有扩展名的文件吗? - Daryl嗯,我觉得我可能误解了问题,但我冒一下险。以下直接的方法有什么问题吗?
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) {
foreach (DirectoryInfo dir in source.GetDirectories())
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
foreach (FileInfo file in source.GetFiles())
file.CopyTo(Path.Combine(target.FullName, file.Name));
}
编辑 鉴于这篇文章对于一个简单问题的简单回答获得了惊人的反对票数,让我补充一下解释。 请在给出反对票之前阅读此内容。
首先,这段代码并不是旨在替换问题中的代码的即插即用代码,它仅供示例目的。
Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectory
执行了一些额外的正确性测试(例如,源和目标是否是有效的目录,源是否是目标的父级等),而这些测试在本答案中缺失。那段代码可能也更加优化。
话虽如此,这段代码运行良好。它已经基本相同地用于成熟软件多年。除了所有IO处理都存在的固有的易变性之外(例如,如果用户在代码写入时手动拔掉USB驱动器会发生什么?),没有已知问题。
特别需要指出的是,在这里使用递归绝对不是问题。从理论上讲(概念上,它是最优雅的解决方案),也不会在实践中出现问题:这段代码不会溢出堆栈。堆栈足够大,可以处理即使是嵌套深层次的文件层次结构。在堆栈空间成为问题之前,文件夹路径长度限制就会触发。
请注意,一个恶意用户可能会使用每个字母的深度嵌套目录来破坏这个假设。我没有试过这个。但只是为了说明这一点:为了使这段代码在典型计算机上溢出,目录必须嵌套几千次。这根本不是一个现实的场景。
从 MSDN 复制:
using System;
using System.IO;
class CopyDir
{
public static void Copy(string sourceDirectory, string targetDirectory)
{
DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);
CopyAll(diSource, diTarget);
}
public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
Directory.CreateDirectory(target.FullName);
// Copy each file into the new directory.
foreach (FileInfo fi in source.GetFiles())
{
Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);
fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
}
// Copy each subdirectory using recursion.
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir =
target.CreateSubdirectory(diSourceSubDir.Name);
CopyAll(diSourceSubDir, nextTargetSubDir);
}
}
public static void Main()
{
string sourceDirectory = @"c:\sourceDirectory";
string targetDirectory = @"c:\targetDirectory";
Copy(sourceDirectory, targetDirectory);
}
// Output will vary based on the contents of the source directory.
}
或者,如果你想走困难的路线,在你的项目中添加对Microsoft.VisualBasic的引用,然后使用以下内容:
Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(fromDirectory, toDirectory);
System.IO.Directory
上,但这比重写它要好! - Josh M.试试这个:
Process proc = new Process();
proc.StartInfo.UseShellExecute = true;
proc.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "xcopy.exe");
proc.StartInfo.Arguments = @"C:\source C:\destination /E /I";
proc.Start();
你的xcopy参数可能会有所不同,但是你已经了解了基本思路。
这个网站总是帮助了我很多,现在轮到我用我所知道的来帮助其他人了。
我希望下面的代码对某些人有用。
string source_dir = @"E:\";
string destination_dir = @"C:\";
// substring is to remove destination_dir absolute path (E:\).
// Create subdirectory structure in destination
foreach (string dir in System.IO.Directory.GetDirectories(source_dir, "*", System.IO.SearchOption.AllDirectories))
{
System.IO.Directory.CreateDirectory(System.IO.Path.Combine(destination_dir, dir.Substring(source_dir.Length + 1)));
// Example:
// > C:\sources (and not C:\E:\sources)
}
foreach (string file_name in System.IO.Directory.GetFiles(source_dir, "*", System.IO.SearchOption.AllDirectories))
{
System.IO.File.Copy(file_name, System.IO.Path.Combine(destination_dir, file_name.Substring(source_dir.Length + 1)));
}
Path.Combine()
。永远不要使用字符串拼接来组合文件路径。 - Andysource_dir.Length + 1
,而不是 source_dir.Length
。 - PellucidWombatsource_dir.Length + 1
,如上所述或者TrimStart('\\'))
。 - juanora递归复制文件夹会导致堆栈溢出,因此需要使用非递归方法进行文件夹的递归复制。
public static void CopyDirectory(string source, string target)
{
var stack = new Stack<Folders>();
stack.Push(new Folders(source, target));
while (stack.Count > 0)
{
var folders = stack.Pop();
Directory.CreateDirectory(folders.Target);
foreach (var file in Directory.GetFiles(folders.Source, "*.*"))
{
File.Copy(file, Path.Combine(folders.Target, Path.GetFileName(file)));
}
foreach (var folder in Directory.GetDirectories(folders.Source))
{
stack.Push(new Folders(folder, Path.Combine(folders.Target, Path.GetFileName(folder))));
}
}
}
public class Folders
{
public string Source { get; private set; }
public string Target { get; private set; }
public Folders(string source, string target)
{
Source = source;
Target = target;
}
}
这是我用于像这样的IO任务的实用程序类。
using System;
using System.Runtime.InteropServices;
namespace MyNameSpace
{
public class ShellFileOperation
{
private static String StringArrayToMultiString(String[] stringArray)
{
String multiString = "";
if (stringArray == null)
return "";
for (int i=0 ; i<stringArray.Length ; i++)
multiString += stringArray[i] + '\0';
multiString += '\0';
return multiString;
}
public static bool Copy(string source, string dest)
{
return Copy(new String[] { source }, new String[] { dest });
}
public static bool Copy(String[] source, String[] dest)
{
Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();
FileOpStruct.hwnd = IntPtr.Zero;
FileOpStruct.wFunc = (uint)Win32.FO_COPY;
String multiSource = StringArrayToMultiString(source);
String multiDest = StringArrayToMultiString(dest);
FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
FileOpStruct.pTo = Marshal.StringToHGlobalUni(multiDest);
FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION;
FileOpStruct.lpszProgressTitle = "";
FileOpStruct.fAnyOperationsAborted = 0;
FileOpStruct.hNameMappings = IntPtr.Zero;
int retval = Win32.SHFileOperation(ref FileOpStruct);
if(retval != 0) return false;
return true;
}
public static bool Move(string source, string dest)
{
return Move(new String[] { source }, new String[] { dest });
}
public static bool Delete(string file)
{
Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();
FileOpStruct.hwnd = IntPtr.Zero;
FileOpStruct.wFunc = (uint)Win32.FO_DELETE;
String multiSource = StringArrayToMultiString(new string[] { file });
FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
FileOpStruct.pTo = IntPtr.Zero;
FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_SILENT | (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION | (ushort)Win32.ShellFileOperationFlags.FOF_NOERRORUI | (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMMKDIR;
FileOpStruct.lpszProgressTitle = "";
FileOpStruct.fAnyOperationsAborted = 0;
FileOpStruct.hNameMappings = IntPtr.Zero;
int retval = Win32.SHFileOperation(ref FileOpStruct);
if(retval != 0) return false;
return true;
}
public static bool Move(String[] source, String[] dest)
{
Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();
FileOpStruct.hwnd = IntPtr.Zero;
FileOpStruct.wFunc = (uint)Win32.FO_MOVE;
String multiSource = StringArrayToMultiString(source);
String multiDest = StringArrayToMultiString(dest);
FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
FileOpStruct.pTo = Marshal.StringToHGlobalUni(multiDest);
FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION;
FileOpStruct.lpszProgressTitle = "";
FileOpStruct.fAnyOperationsAborted = 0;
FileOpStruct.hNameMappings = IntPtr.Zero;
int retval = Win32.SHFileOperation(ref FileOpStruct);
if(retval != 0) return false;
return true;
}
}
}
tboswell的替换证明版本(可以抵御文件路径中重复模式的影响)
public static void copyAll(string SourcePath , string DestinationPath )
{
//Now Create all of the directories
foreach (string dirPath in Directory.GetDirectories(SourcePath, "*", SearchOption.AllDirectories))
Directory.CreateDirectory(Path.Combine(DestinationPath ,dirPath.Remove(0, SourcePath.Length )) );
//Copy all the files & Replaces any files with the same name
foreach (string newPath in Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories))
File.Copy(newPath, Path.Combine(DestinationPath , newPath.Remove(0, SourcePath.Length)) , true);
}
Path.Combine()
来拼接文件路径,不要使用字符串连接的方式。 - AndyPath.Join()
而不是Path.Combine()
。虽然我不完全明白为什么,但我猜测我可能正在做与文档中建议使用Path.Join()
相关的事情。 - davrob01namespace System.IO {
public static class ExtensionMethods {
public static void CopyTo(this DirectoryInfo srcPath, string destPath) {
Directory.CreateDirectory(destPath);
Parallel.ForEach(srcPath.GetDirectories("*", SearchOption.AllDirectories),
srcInfo => Directory.CreateDirectory($"{destPath}{srcInfo.FullName[srcPath.FullName.Length..]}"));
Parallel.ForEach(srcPath.GetFiles("*", SearchOption.AllDirectories),
srcInfo => File.Copy(srcInfo.FullName, $"{destPath}{srcInfo.FullName[srcPath.FullName.Length..]}", true));
});
}
}
}
使用方法:
new DirectoryInfo(sourcePath).CopyTo(destinationPath);
Microsoft.VisualBasic
而不是System.IO
中?之所以没有出现在Mono中是因为所有被认为是“核心”的库都是System.[something]
- 所有其他的库都不是。我并不介意引用额外的DLL,但微软没有将这个功能包含在System.IO
中,这其中有一个很好的原因。 - Keith