[工具類]文件或文件夾xx已存在,則重命名為xx(n)(2),工具類xx
寫在前面
最近一直在弄文件傳輸組件,其中一個功能就是,在接收端接收文件時,如果文件已經存在了,則對其進行文件名+索引的方式進行自動重命名,之前也寫個類似的工具類,總感覺代碼太冗余,每回頭想想,總覺得心裡有疙瘩,下班的時候在地鐵上,又想了想,感覺是我把問題想復雜了,遂將今天的思路整理一下,寫了一個輔助類,記錄在此。
上篇文章
[工具類]文件或文件夾xx已存在,則重命名為xx(n)
ReNameHelper代碼
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Linq;
5 using System.Text;
6 using System.Text.RegularExpressions;
7 using System.Threading.Tasks;
8
9 namespace Wolfy.ReNameHelper
10 {
11 /// <summary>
12 /// 文件已存在,重命名操作類
13 /// </summary>
14 public class ReNameHelper
15 {
16 /// <summary>
17 /// 對文件進行重命名
18 /// </summary>
19 /// <param name="strFilePath"></param>
20 /// <returns></returns>
21 public static string FileReName(string strFilePath)
22 {
23 //判斷該文件是否存在,存在則返回新名字,否則返回原來的名
24 if (!File.Exists(strFilePath))
25 {
26 return Path.GetFileName(strFilePath);
27 }
28 else
29 {
30 //獲取不帶擴展名的文件名稱
31 string strFileNameWithoutExtension = Path.GetFileNameWithoutExtension(strFilePath);
32 //獲取擴展名
33 string strFileExtension = Path.GetExtension(strFilePath);
34 //獲取目錄名
35 string strDirPath = Path.GetDirectoryName(strFilePath);
36 //以文件名開頭和結尾的正則
37 string strRegex = "^" + strFileNameWithoutExtension + "(\\d+)?" ;
38 Regex regex = new Regex(strRegex);
39 //獲取該路徑下類似的文件名
40 string[] strFilePaths = Directory.GetFiles(strDirPath, "*" + strFileExtension).Where(path => regex.IsMatch(Path.GetFileNameWithoutExtension(path))).ToArray();
41 //獲得新的文件名
42 return strFileNameWithoutExtension + "(" + (strFilePaths.Length + 1).ToString() + ")" + strFileExtension;
43 }
44 }
45 /// <summary>
46 /// 文件夾已存在,重命名
47 /// </summary>
48 /// <param name="strFolderPath"></param>
49 /// <returns></returns>
50 public static string FolderReName(string strFolderPath)
51 {
52 //判斷該文件夾是否存在,存在則返回新名字,否則返回原來的名
53 if (!Directory.Exists(strFolderPath))
54 {
55 return Path.GetFileName(strFolderPath);
56 }
57 else
58 {
59 //獲取文件夾名
60 string strFolderName= Path.GetFileName(strFolderPath);
61 //獲取目錄名
62 string strDirPath = Path.GetDirectoryName(strFolderPath);
63 //以文件夾名開頭和結尾的正則
64 string strRegex = "^" + strFolderName + "(\\d+)?";
65 Regex regex = new Regex(strRegex);
66 //獲取該路徑下類似的文件夾名
67 string[] strFilePaths = Directory.GetDirectories(strDirPath).Where(path => regex.IsMatch(Path.GetFileName(path))).ToArray();
68 //獲得新的文件名
69 return strFolderName + "(" + (strFilePaths.Length + 1).ToString() + ")" ;
70 }
71 }
72 }
73 }
實現思路:使用正則進行匹配以文件名或者文件夾名字開頭的文件或者文件夾名稱,獲取文件或者文件夾的個數,則新的文件名為文件名(文件或者文件夾總數+1)。當然,對一些,名字比較特殊的,不符合windows命名規則的,在客戶端選擇文件或者文件夾的時候,盡量對其進行過濾。以保證該輔助類起到應有的作用。
簡單測試

在路徑C:\Users\Wolfy\Desktop\MVC5下有如上圖的一些文件,現在如果再次接收相同名字的文件或者文件夾,則返回新的文件或者文件夾名
結果

這裡也分享一個園友寫的一個重命名的輔助類。對文件操作的輔助類,記錄再次,供大家參考。
原文地址:http://www.cnblogs.com/lxblog/archive/2012/11/13/2768096.html

![]()
using System;
using System.Runtime.InteropServices;
using System.IO;
namespace LxFile
{
/// <summary>
/// 文件操作代理,該類提供類似於Windows的文件操作體驗
/// </summary>
public class FileOperateProxy
{
#region 【內部類型定義】
private struct SHFILEOPSTRUCT
{
public IntPtr hwnd; //父窗口句柄
public wFunc wFunc; //要執行的動作
public string pFrom; //源文件路徑,可以是多個文件,以結尾符號"\0"結束
public string pTo; //目標路徑,可以是路徑或文件名
public FILEOP_FLAGS fFlags; //標志,附加選項
public bool fAnyOperationsAborted; //是否可被中斷
public IntPtr hNameMappings; //文件映射名字,可在其它 Shell 函數中使用
public string lpszProgressTitle; // 只在 FOF_SIMPLEPROGRESS 時,指定對話框的標題。
}
private enum wFunc
{
FO_MOVE = 0x0001, //移動文件
FO_COPY = 0x0002, //復制文件
FO_DELETE = 0x0003, //刪除文件,只是用pFrom
FO_RENAME = 0x0004 //文件重命名
}
private enum FILEOP_FLAGS
{
FOF_MULTIDESTFILES = 0x0001, //pTo 指定了多個目標文件,而不是單個目錄
FOF_CONFIRMMOUSE = 0x0002,
FOF_SILENT = 0x0044, // 不顯示一個進度對話框
FOF_RENAMEONCOLLISION = 0x0008, // 碰到有抵觸的名字時,自動分配前綴
FOF_NOCONFIRMATION = 0x10, // 不對用戶顯示提示
FOF_WANTMAPPINGHANDLE = 0x0020, // 填充 hNameMappings 字段,必須使用 SHFreeNameMappings 釋放
FOF_ALLOWUNDO = 0x40, // 允許撤銷
FOF_FILESONLY = 0x0080, // 使用 *.* 時, 只對文件操作
FOF_SIMPLEPROGRESS = 0x0100, // 簡單進度條,意味者不顯示文件名。
FOF_NOCONFIRMMKDIR = 0x0200, // 建新目錄時不需要用戶確定
FOF_NOERRORUI = 0x0400, // 不顯示出錯用戶界面
FOF_NOCOPYSECURITYATTRIBS = 0x0800, // 不復制 NT 文件的安全屬性
FOF_NORECURSION = 0x1000 // 不遞歸目錄
}
#endregion 【內部類型定義】
#region 【DllImport】
[DllImport("shell32.dll")]
private static extern int SHFileOperation(ref SHFILEOPSTRUCT lpFileOp);
#endregion 【DllImport】
#region 【刪除文件操作】
/// <summary>
/// 刪除單個文件。
/// </summary>
/// <param name="fileName">刪除的文件名</param>
/// <param name="toRecycle">指示是將文件放入回收站還是永久刪除,true-放入回收站,false-永久刪除</param>
/// <param name="showDialog">指示是否顯示確認對話框,true-顯示確認刪除對話框,false-不顯示確認刪除對話框</param>
/// <param name="showProgress">指示是否顯示進度對話框,true-顯示,false-不顯示。該參數當指定永久刪除文件時有效</param>
/// <param name="errorMsg">反饋錯誤消息的字符串</param>
/// <returns>操作執行結果標識,刪除文件成功返回0,否則,返回錯誤代碼</returns>
public static int DeleteFile(string fileName, bool toRecycle, bool showDialog, bool showProgress, ref string errorMsg)
{
try
{
string fName = GetFullName(fileName);
return ToDelete(fName, toRecycle, showDialog, showProgress, ref errorMsg);
}
catch (Exception ex)
{
errorMsg = ex.Message;
return -200;
}
}
/// <summary>
/// 刪除一組文件。
/// </summary>
/// <param name="fileNames">字符串數組,表示一組文件名</param>
/// <param name="toRecycle">指示是將文件放入回收站還是永久刪除,true-放入回收站,false-永久刪除</param>
/// <param name="showDialog">指示是否顯示確認對話框,true-顯示確認刪除對話框,false-不顯示確認刪除對話框</param>
/// <param name="showProgress">指示是否顯示進度對話框,true-顯示,false-不顯示。該參數當指定永久刪除文件時有效</param>
/// <param name="errorMsg">反饋錯誤消息的字符串</param>
/// <returns>操作執行結果標識,刪除文件成功返回0,否則,返回錯誤代碼</returns>
public static int DeleteFiles(string[] fileNames, bool toRecycle, bool showDialog, bool showProgress, ref string errorMsg)
{
try
{
string fName = "";
foreach (string str in fileNames)
{
fName += GetFullName(str) + "\0"; //組件文件組字符串
}
return ToDelete(fName, toRecycle, showDialog, showProgress, ref errorMsg);
}
catch (Exception ex)
{
errorMsg = ex.Message;
return -200;
}
}
#endregion 【刪除文件操作】
#region 【移動文件操作】
/// <summary>
/// 移動一個文件到指定路徑下
/// </summary>
/// <param name="sourceFileName">要移動的文件名</param>
/// <param name="destinationPath">移動到的目的路徑</param>
/// <param name="showDialog">指示是否顯示確認對話框,true-顯示確認對話框,false-不顯示確認對話框</param>
/// <param name="showProgress">指示是否顯示進度對話框</param>
/// <param name="autoRename">指示當文件名重復時,是否自動為新文件加上後綴名</param>
/// <param name="errorMsg">反饋錯誤消息的字符串</param>
/// <returns>返回移動操作是否成功的標識,成功返回0,失敗返回錯誤代碼</returns>
public static int MoveFile(string sourceFileName, string destinationPath, bool showDialog, bool showProgress, bool autoRename, ref string errorMsg)
{
try
{
string sfName = GetFullName(sourceFileName);
string dfName = GetFullName(destinationPath);
return ToMoveOrCopy(wFunc.FO_MOVE, sfName, dfName, showDialog, showProgress, autoRename, ref errorMsg);
}
catch (Exception ex)
{
errorMsg = ex.Message;
return -200;
}
}
/// <summary>
/// 移動一組文件到指定的路徑下
/// </summary>
/// <param name="sourceFileNames">要移動的文件名數組</param>
/// <param name="destinationPath">移動到的目的路徑</param>
/// <param name="showDialog">指示是否顯示確認對話框,true-顯示確認對話框,false-不顯示確認對話框</param>
/// <param name="showProgress">指示是否顯示進度對話框</param>
/// <param name="autoRename">指示當文件名重復時,是否自動為新文件加上後綴名</param>
/// <param name="errorMsg">反饋錯誤消息的字符串</param>
/// <returns>返回移動操作是否成功的標識,成功返回0,失敗返回錯誤代碼,-200:表示其他異常</returns>
public static int MoveFiles(string[] sourceFileNames, string destinationPath, bool showDialog, bool showProgress, bool autoRename, ref string errorMsg)
{
try
{
string sfName = "";
foreach (string str in sourceFileNames)
{
sfName += GetFullName(str) + "\0"; //組件文件組字符串
}
string dfName = GetFullName(destinationPath);
return ToMoveOrCopy(wFunc.FO_MOVE, sfName, dfName, showDialog, showProgress, autoRename, ref errorMsg);
}
catch (Exception ex)
{
errorMsg = ex.Message;
return -200;
}
}
#endregion 【移動文件操作】
#region 【復制文件操作】
/// <summary>
/// 復制一個文件到指定的文件名或路徑
/// </summary>
/// <param name="sourceFileName">要復制的文件名</param>
/// <param name="destinationFileName">復制到的目的文件名或路徑</param>
/// <param name="showDialog">指示是否顯示確認對話框,true-顯示確認對話框,false-不顯示確認對話框</param>
/// <param name="showProgress">指示是否顯示進度對話框</param>
/// <param name="autoRename">指示當文件名重復時,是否自動為新文件加上後綴名</param>
/// <returns>返回移動操作是否成功的標識,成功返回0,失敗返回錯誤代碼,-200:表示其他異常</returns>
public static int CopyFile(string sourceFileName, string destinationFileName, bool showDialog, bool showProgress, bool autoRename, ref string errorMsg)
{
try
{
string sfName = GetFullName(sourceFileName);
string dfName = GetFullName(destinationFileName);
return ToMoveOrCopy(wFunc.FO_COPY, sfName, dfName, showDialog, showProgress, autoRename, ref errorMsg);
}
catch (Exception ex)
{
errorMsg = ex.Message;
return -200;
}
}
/// <summary>
/// 復制一組文件到指定的路徑
/// </summary>
/// <param name="sourceFileNames">要復制的文件名數組</param>
/// <param name="destinationPath">復制到的目的路徑</param>
/// <param name="showDialog">指示是否顯示確認對話框,true-顯示確認對話框,false-不顯示確認對話框</param>
/// <param name="showProgress">指示是否顯示進度對話框</param>
/// <param name="autoRename">指示當文件名重復時,是否自動為新文件加上後綴名</param>
/// <returns>返回移動操作是否成功的標識,成功返回0,失敗返回錯誤代碼,-200:表示其他異常</returns>
public static int CopyFiles(string[] sourceFileNames, string destinationPath, bool showDialog, bool showProgress, bool autoRename, ref string errorMsg)
{
try
{
string sfName = "";
foreach (string str in sourceFileNames)
{
sfName += GetFullName(str) + "\0"; //組件文件組字符串
}
string dfName = GetFullName(destinationPath);
return ToMoveOrCopy(wFunc.FO_COPY, sfName, dfName, showDialog, showProgress, autoRename, ref errorMsg);
}
catch (Exception ex)
{
errorMsg = ex.Message;
return -200;
}
}
#endregion 【復制文件操作】
#region 【重命名文件】
/// <summary>
/// 重命名一個文件為新名稱,建議您使用更方便的Microsoft.VisualBasic.FileSystem.ReName();替換該方法
/// </summary>
/// <param name="sourceFileName">要復制的文件名</param>
/// <param name="destinationFileName">復制到的目的文件名或路徑</param>
/// <param name="showDialog">指示是否顯示確認對話框,true-顯示確認對話框,false-不顯示確認對話框</param>
/// <returns>返回移動操作是否成功的標識,成功返回0,失敗返回錯誤代碼,-200:表示其他異常</returns>
[Obsolete("建議使用 Microsoft.VisualBasic.FileSystem.ReName()方法")]
public static int ReNameFile(string sourceFileName, string destinationFileName, bool showDialog, ref string errorMsg)
{
try
{
SHFILEOPSTRUCT lpFileOp = new SHFILEOPSTRUCT();
lpFileOp.wFunc = wFunc.FO_RENAME;
lpFileOp.pFrom = GetFullName(sourceFileName) + "\0\0"; //將文件名以結尾字符"\0\0"結束
lpFileOp.pTo = GetFullName(destinationFileName) + "\0\0";
lpFileOp.fFlags = FILEOP_FLAGS.FOF_NOERRORUI;
if (!showDialog)
lpFileOp.fFlags |= FILEOP_FLAGS.FOF_NOCONFIRMATION; //設定不顯示提示對話框
lpFileOp.fAnyOperationsAborted = true;
int n = SHFileOperation(ref lpFileOp);
if (n == 0)
return 0;
string tmp = GetErrorString(n);
errorMsg = string.Format("{0}({1})", tmp, sourceFileName);
return n;
}
catch (Exception ex)
{
errorMsg = ex.Message;
return -200;
}
}
/// <summary>
/// 利用Microsoft.VisualBasic.FileSystem.ReName()方法實現
/// </summary>
/// <param name="filePath"></param>
/// <param name="newFileName"></param>
public static void ReNameFile(string filePath, string newFileName)
{
try
{
string extensName = Path.GetExtension(filePath);
string newName = newFileName + extensName;
Microsoft.VisualBasic.FileIO.FileSystem.RenameFile(filePath, newName);
}
catch (Exception ex)
{
throw ex;
}
}
#endregion 【重命名文件】
/// <summary>
/// 刪除單個或多個文件
/// </summary>
/// <param name="fileName">刪除的文件名,如果是多個文件,文件名之間以字符串結尾符'\0'隔開</param>
/// <param name="toRecycle">指示是將文件放入回收站還是永久刪除,true-放入回收站,false-永久刪除</param>
/// <param name="showDialog">指示是否顯示確認對話框,true-顯示確認刪除對話框,false-不顯示確認刪除對話框</param>
/// <param name="showProgress">指示是否顯示進度對話框,true-顯示,false-不顯示。該參數當指定永久刪除文件時有效</param>
/// <param name="errorMsg">反饋錯誤消息的字符串</param>
/// <returns>操作執行結果標識,刪除文件成功返回0,否則,返回錯誤代碼</returns>
private static int ToDelete(string fileName, bool toRecycle, bool showDialog, bool showProgress, ref string errorMsg)
{
SHFILEOPSTRUCT lpFileOp = new SHFILEOPSTRUCT();
lpFileOp.wFunc = wFunc.FO_DELETE;
lpFileOp.pFrom = fileName + "\0"; //將文件名以結尾字符"\0"結束
lpFileOp.fFlags = FILEOP_FLAGS.FOF_NOERRORUI;
if (toRecycle)
lpFileOp.fFlags |= FILEOP_FLAGS.FOF_ALLOWUNDO; //設定刪除到回收站
if (!showDialog)
lpFileOp.fFlags |= FILEOP_FLAGS.FOF_NOCONFIRMATION; //設定不顯示提示對話框
if (!showProgress)
lpFileOp.fFlags |= FILEOP_FLAGS.FOF_SILENT; //設定不顯示進度對話框
lpFileOp.fAnyOperationsAborted = true;
int n = SHFileOperation(ref lpFileOp);
if (n == 0)
return 0;
string tmp = GetErrorString(n);
//.av 文件正常刪除了但也提示 402 錯誤,不知道為什麼。屏蔽之。
if ((fileName.ToLower().EndsWith(".av") && n.ToString("X") == "402"))
return 0;
errorMsg = string.Format("{0}({1})", tmp, fileName);
return n;
}
/// <summary>
/// 移動或復制一個或多個文件到指定路徑下
/// </summary>
/// <param name="flag">操作類型,是移動操作還是復制操作</param>
/// <param name="sourceFileName">要移動或復制的文件名,如果是多個文件,文件名之間以字符串結尾符'\0'隔開</param>
/// <param name="destinationFileName">移動到的目的位置</param>
/// <param name="showDialog">指示是否顯示確認對話框,true-顯示確認對話框,false-不顯示確認對話框</param>
/// <param name="showProgress">指示是否顯示進度對話框</param>
/// <param name="autoRename">指示當文件名重復時,是否自動為新文件加上後綴名</param>
/// <param name="errorMsg">反饋錯誤消息的字符串</param>
/// <returns>返回移動操作是否成功的標識,成功返回0,失敗返回錯誤代碼</returns>
private static int ToMoveOrCopy(wFunc flag, string sourceFileName, string destinationFileName, bool showDialog, bool showProgress, bool autoRename, ref string errorMsg)
{
SHFILEOPSTRUCT lpFileOp = new SHFILEOPSTRUCT();
lpFileOp.wFunc = flag;
lpFileOp.pFrom = sourceFileName + "\0"; //將文件名以結尾字符"\0\0"結束
lpFileOp.pTo = destinationFileName + "\0\0";
lpFileOp.fFlags = FILEOP_FLAGS.FOF_NOERRORUI;
lpFileOp.fFlags |= FILEOP_FLAGS.FOF_NOCONFIRMMKDIR; //指定在需要時可以直接創建路徑
if (!showDialog)
lpFileOp.fFlags |= FILEOP_FLAGS.FOF_NOCONFIRMATION; //設定不顯示提示對話框
if (!showProgress)
lpFileOp.fFlags |= FILEOP_FLAGS.FOF_SILENT; //設定不顯示進度對話框
if (autoRename)
lpFileOp.fFlags |= FILEOP_FLAGS.FOF_RENAMEONCOLLISION; //自動為重名文件添加名稱後綴
lpFileOp.fAnyOperationsAborted = true;
int n = SHFileOperation(ref lpFileOp);
if (n == 0)
return 0;
string tmp = GetErrorString(n);
errorMsg = string.Format("{0}({1})", tmp, sourceFileName);
return n;
}
/// <summary>
/// 獲取一個文件的全名
/// </summary>
/// <param name="fileName">文件名</param>
/// <returns>返回生成文件的完整路徑名</returns>
private static string GetFullName(string fileName)
{
FileInfo fi = new FileInfo(fileName);
return fi.FullName;
}
/// <summary>
/// 解釋錯誤代碼
/// </summary>
/// <param name="n">代碼號</param>
/// <returns>返回關於錯誤代碼的文字描述</returns>
private static string GetErrorString(int n)
{
if (n == 0) return string.Empty;
switch (n)
{
case 2:
return "系統找不到指定的文件。";
case 7:
return "存儲控制塊被銷毀。您是否選擇的“取消”操作?";
case 113:
return "文件已存在!";
case 115:
return "重命名文件操作,原始文件和目標文件必須具有相同的路徑名。不能使用相對路徑。";
case 117:
return "I/O控制錯誤";
case 123:
return "指定了重復的文件名";
case 116:
return "The source is a root directory, which cannot be moved or renamed.";
case 118:
return "Security settings denied access to the source.";
case 124:
return "The path in the source or destination or both was invalid.";
case 65536:
return "An unspecified error occurred on the destination.";
case 1026:
return "在試圖移動或拷貝一個不存在的文件.";
case 1223:
return "操作被取消!";
default:
return "未識別的錯誤代碼:" + n;
}
}
}
}
View Code
總結
通過這個工具類的改寫,發現實現一個功能,有很多方法,有時候換個思路,代碼也許會更簡潔,邏輯更清晰。