【发布时间】:2010-02-11 15:40:28
【问题描述】:
C# 是否有任何函数可以为复制的项目生成新名称?
例如,如果我有一个名为“文件夹”的字符串,我需要一个函数来生成一个字符串“文件夹副本”......并且给定字符串“文件夹副本”,该函数应该生成“文件夹副本”(1) "等等....
【问题讨论】:
-
您真的想要像 Windows 那样“复制文件夹 (1)”还是“复制文件夹 (2)”?
C# 是否有任何函数可以为复制的项目生成新名称?
例如,如果我有一个名为“文件夹”的字符串,我需要一个函数来生成一个字符串“文件夹副本”......并且给定字符串“文件夹副本”,该函数应该生成“文件夹副本”(1) "等等....
【问题讨论】:
你可以写一个循环,像这样:
string baseName = @"C:\Parent\Copy of Folder", actualName = baseName;
int index = 0;
while(File.Exists(actualName) || Directory.Exists(actualName))
actualName = baseName + " (" + (++index) + ")";
根据您的用例,您可能应该将其放入 static 实用程序方法中。
【讨论】:
@SLaks 具有基本算法,但您需要牢记竞争条件。 在 File.Exists 检查和文件创建之间的时间内,文件可能由其他线程或进程创建。以下是基于 SLak 算法的解决方案的概要:
FileStream fileCopy;
while(File.Exists(actualName) || Directory.Exists(acutalName))
{
actualName = baseName + " (" + (++index) + ")";
try
{
fileCopy = new FileStream(actualName, FileMode.CreateNew);
}
catch (IOException)
{
if (!File.Exists(actualName))
{
throw;
}
}
}
如果您无法打开文件,可能是因为某个其他进程创建了该文件,或者是由于某些意外问题。在出现意外问题的情况下重新抛出异常(永远不要吞下意外的异常。)
您可以只使用“new FileStream”操作是否引发异常作为循环条件,但我避免在非错误条件下引发异常。这使得在调试器中利用“捕获抛出”异常行为变得更加困难,并且异常在计算上是昂贵的。我假设在您的应用程序的上下文中,在文件系统上有一个与您想用于副本的文件具有相同名称的文件不是错误。如果我错了,那么我认为如果文件存在则例外。
【讨论】:
我从不喜欢 Microsoft 使用的“副本”前缀,因为副本不会排在原件旁边。 Windows 7 中的 Microsoft 现在同意我的看法(这总是很好),因为它使用后缀而不是前缀,例如“文件夹 - 复制”、“文件夹 - 复制 (2)”等。
【讨论】:
如果目标文件已经存在,您还可以使用内置的 Windows 功能来处理自动重命名。这样,您将获得与在资源管理器中复制文件时相同的名称。你需要的函数是SHFileOperation。
一个简短的示例如下所示:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
class Program
{
static void Main(string[] args)
{
string file = @"C:\tmp\test.txt";
try
{
SHFILEOPSTRUCT fileOp = new SHFILEOPSTRUCT();
fileOp.wFunc = FO_Func.FO_COPY;
fileOp.fFlags = (ushort)(FILEOP_FLAGS.FOF_RENAMEONCOLLISION);
// file names need double-null termination
fileOp.pFrom = file + '\0' + '\0';
// use the same name as target for demo purpose
fileOp.pTo = file + '\0' + '\0';
int hRes = SHFileOperation(ref fileOp);
if (hRes != 0)
{
throw new Win32Exception(hRes);
}
}
catch (Exception ex)
{
Trace.WriteLine(ex);
}
}
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
static extern int SHFileOperation([In] ref SHFILEOPSTRUCT lpFileOp);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
struct SHFILEOPSTRUCT
{
public IntPtr hwnd;
public FO_Func wFunc;
[MarshalAs(UnmanagedType.LPWStr)]
public string pFrom;
[MarshalAs(UnmanagedType.LPWStr)]
public string pTo;
public ushort fFlags;
public Int32 fAnyOperationsAborted;
public IntPtr hNameMappings;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpszProgressTitle;
}
public enum FO_Func : uint
{
FO_MOVE = 0x0001,
FO_COPY = 0x0002,
FO_DELETE = 0x0003,
FO_RENAME = 0x0004,
}
[Flags]
public enum FILEOP_FLAGS : ushort
{
FOF_MULTIDESTFILES = 0x0001,
FOF_CONFIRMMOUSE = 0x0002,
FOF_SILENT = 0x0004,
FOF_RENAMEONCOLLISION = 0x0008,
FOF_NOCONFIRMATION = 0x0010,
FOF_WANTMAPPINGHANDLE = 0x0020,
FOF_ALLOWUNDO = 0x0040,
FOF_FILESONLY = 0x0080,
FOF_SIMPLEPROGRESS = 0x0100,
FOF_NOCONFIRMMKDIR = 0x0200,
FOF_NOERRORUI = 0x0400,
FOF_NOCOPYSECURITYATTRIBS = 0x0800,
FOF_NORECURSION = 0x1000,
FOF_NO_CONNECTED_ELEMENTS = 0x2000,
FOF_WANTNUKEWARNING = 0x4000,
FOF_NORECURSEREPARSE = 0x8000,
}
}
使用FOF_WANTMAPPINGHANDLE 标志也可以获得生成的目标文件名。如需进一步阅读和更详细的示例,请参阅 CodeProject 上的这篇精彩文章:
【讨论】: