效果图:
一款可圈可点的改名小工具设计实现还是不错的,主要实现代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace Beinet.cn.FilenameReplace
{
public partial class MainForm : Form
{
/// <summary>
/// 是否中断更名,比如正则错误时,要中断
/// </summary>
private bool cancel = false;
/// <summary>
/// 检查是否数字的正则
/// </summary>
Regex regNum = new Regex(@"^\d*$", RegexOptions.Compiled);
const string chUpChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/// <summary>
/// 检查是否大写字母的正则
/// </summary>
Regex regUpChar = new Regex(@"^[" + chUpChar + "]*$", RegexOptions.Compiled);
const string chLowChar = "abcdefghijklmnopqrstuvwxyz";
/// <summary>
/// 检查是否大写字母的正则
/// </summary>
Regex regLowChar = new Regex(@"^[" + chLowChar + "]*$", RegexOptions.Compiled);
const string chNum = "零一二三四五六七八九";
/// <summary>
/// 检查是否中文数字的正则
/// </summary>
Regex regChNum = new Regex(@"^[" + chNum + "]*$", RegexOptions.Compiled);
/// <summary>
/// 提取旧文件名的正则
/// </summary>
Regex regOldName = new Regex(@"\\o(\d+)?(?:-(\d+))?", RegexOptions.Compiled);
/// <summary>
/// 验证文件名是否合法的正则
/// </summary>
Regex regValidFileName = new Regex(@"[\<\>\/\\\|\:""\*\?]", RegexOptions.Compiled);
/// <summary>
/// 记录上一次选中的目录
/// </summary>
private string lastSelectedFolder = string.Empty;
public MainForm()
{
InitializeComponent();
lstSnType.SelectedIndex = 0;
}
#region 按钮事件
private int fileIdx = 1; // 文件列表的序号
private SortedList<string, int> files = new SortedList<string, int>(); // 保存文件列表,因为ListView判断Contains慢,用这个代替
// private SortedList<string, int> filenew = new SortedList<string, int>();// 保存文件新名列表
private void btnAddFiles_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog { Multiselect = true };
DialogResult result = dialog.ShowDialog();
if (result != DialogResult.OK)
{
return;
}
foreach (var file in dialog.FileNames)
{
if (files.ContainsKey(file))//(ContainsFile(lvFiles, file))
continue;
files.Add(file, 0);
ListViewItem item = new ListViewItem(new[] { (fileIdx++).ToString(), file, string.Empty, string.Empty });
lvFiles.Items.Add(item);
}
}
//bool ContainsFile(ListView lv, string fileName)
//{
// foreach (ListViewItem item in lv.Items)
// {
// if (item.SubItems[1].Text == fileName)
// return true;
// }
// return false;
//}
private void btnAddDir_Click(object sender, EventArgs e)
{
FolderBrowserDialog dialog = new FolderBrowserDialog();
if (!string.IsNullOrEmpty(lastSelectedFolder))
dialog.SelectedPath = lastSelectedFolder;
DialogResult result = dialog.ShowDialog();
if (result != DialogResult.OK)
{
return;
}
lastSelectedFolder = dialog.SelectedPath;
var subdir = chkSubdirs.Checked ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
foreach (var file in Directory.GetFiles(lastSelectedFolder, "*", subdir))
{
if (files.ContainsKey(file))//(ContainsFile(lvFiles, file))
continue;
files.Add(file, 0);
ListViewItem item = new ListViewItem(new[] { (fileIdx++).ToString(), file, string.Empty, string.Empty });
lvFiles.Items.Add(item);
}
}
private void btnClear_Click(object sender, EventArgs e)
{
lvFiles.Items.Clear();
files.Clear();
}
private void txtSnStep_TextChanged(object sender, EventArgs e)
{
TextBox txt = sender as TextBox;
if (txt == null)
return;
if (!regNum.IsMatch(txt.Text))
{
txt.Text = Regex.Replace(txt.Text, @"[^\d]", string.Empty);
}
}
private void txtSnBegin_TextChanged(object sender, EventArgs e)
{
TextBox txt = sender as TextBox;
if (txt == null)
return;
Regex regValid;
string regReplace;
switch (lstSnType.Text)
{
//case "数字编号":
default:
regValid = regNum;
regReplace = @"[^\d]";
break;
case "大写字母":
regValid = regUpChar;
regReplace = @"[^" + chUpChar + "]";
break;
case "小写字母":
regValid = regLowChar;
regReplace = @"[^" + chLowChar + "]";
break;
case "汉字编号":
regValid = regChNum;
regReplace = @"[^" + chNum + "]";
break;
}
if (!regValid.IsMatch(txt.Text))
{
txt.Text = Regex.Replace(txt.Text, regReplace, string.Empty);
}
}
private void lstSnType_SelectedIndexChanged(object sender, EventArgs e)
{
ComboBox lst = sender as ComboBox;
if (lst == null)
return;
switch (lst.Text)
{
//case "数字编号":
default:
txtSnBegin.Text = "1";
break;
case "大写字母":
txtSnBegin.Text = "A";
break;
case "小写字母":
txtSnBegin.Text = "a";
break;
case "汉字编号":
txtSnBegin.Text = "一";
break;
}
}
private void chkNormalAllReplace_CheckedChanged(object sender, EventArgs e)
{
txtNormalFrom.Enabled = !((CheckBox)sender).Checked;
}
private void btnHelper_Click(object sender, EventArgs e)
{
new HelpForm().ShowDialog();
}
private void btnRemoveSuccess_Click(object sender, EventArgs e)
{
for (int i = lvFiles.Items.Count - 1; i >= 0; i--)
{
if (lvFiles.Items[i].SubItems[3].Text == "成功")
lvFiles.Items.RemoveAt(i);
}
}
#endregion
#region 主要的按钮事件
private void btnPreview_Click(object sender, EventArgs e)
{
if (lvFiles.Items.Count <= 0)
{
ShowMsg("请先添加文件");
return;
}
lastSn = string.Empty;
lastNum = 0;
cancel = false;
int idx = 0;
int step = int.Parse(txtSnStep.Text);
foreach (ListViewItem item in lvFiles.Items)
{
if (cancel)
break;
string filename = item.SubItems[1].Text;
var ret = false;
string newName = operateName(filename, idx, ref ret);
if (ret)
{
item.SubItems[2].Text = newName;
item.SubItems[3].Text = string.Empty;
idx += step;
}
else
{
ShowMsg(newName);
}
}
}
private void btnGo_Click(object sender, EventArgs e)
{
if (lvFiles.Items.Count <= 0)
{
ShowMsg("请先添加文件");
return;
}
lastSn = string.Empty;
lastNum = 0;
cancel = false;
int idx = 0;
int step = int.Parse(txtSnStep.Text);
int sucessCnt = 0, failCnt = 0;
foreach (ListViewItem item in lvFiles.Items)
{
if (cancel)
break;
string filename = item.SubItems[1].Text;
var ret = true;
string newName = operateName(filename, idx, ref ret);
if (ret)
{
item.SubItems[2].Text = newName;
item.SubItems[3].BackColor = Color.Green;
item.SubItems[3].Text = "成功";
sucessCnt++;
}
else
{
item.SubItems[3].BackColor = Color.Red;
item.SubItems[3].Text = newName;
if (cancel)
ShowMsg(newName);
failCnt++;
}
idx += step;
}
if (chkMoveSuccess.Checked)
{
for (int i = lvFiles.Items.Count - 1; i >= 0; i--)
{
if (lvFiles.Items[i].SubItems[3].Text == "成功")
lvFiles.Items.RemoveAt(i);
}
}
ShowMsg("本次操作成功" + sucessCnt + "个,失败" + failCnt + "个");
}
#endregion
#region 主要方法,根据配置修改文件名
StringBuilder sbForOld = new StringBuilder();
/// <summary>
/// 根据配置修改文件名
/// </summary>
/// <param name="name"></param>
/// <param name="idx">处理到第几个文件了</param>
/// <param name="rename">是否执行文件重名,返回时,true表示成功,false表示失败</param>
/// <returns></returns>
string operateName(string name, int idx, ref bool rename)
{
string ret = name;
string ext = string.Empty;
if (chkWithExt.Checked)
{
ret = Path.GetFileNameWithoutExtension(ret);
ext = Path.GetExtension(name);
}
else
ret = Path.GetFileName(ret);
// 备份旧名,后面替换\o时使用
string old = ret;
//if(tabReg.Focused)
switch (tabControl1.SelectedIndex)
{
case 0:
{
#region 普通替换
string tmp = chkNormalAllReplace.Checked ? ret : txtNormalFrom.Text;
if(!string.IsNullOrEmpty(tmp))
ret = ret.Replace(tmp, txtNormalTo.Text);
#endregion
}
break;
case 1:
{
#region 正则替换
try
{
RegexOptions opn = chkRegIgnoreCase.Checked ? RegexOptions.IgnoreCase : RegexOptions.None;
ret = Regex.Replace(ret, txtReg.Text, txtRegTo.Text, opn);
}
catch (Exception exp)
{
ShowMsg(exp.ToString());
cancel = true;
}
#endregion
}
break;
}
// 序号处理
if (ret.IndexOf(@"\d") >= 0)
ret = ret.Replace(@"\d", GetSn(idx));
#region \o取旧文件名的字符替换处理
Match m = regOldName.Match(ret);
if (m.Success)
{
sbForOld.Length = 0;
sbForOld.Append(ret);
while (m.Success)
{
int mI = m.Index;
sbForOld.Remove(mI, m.Length);// 移除匹配到的串
if (!m.Groups[1].Success)//m.Value == @"\o")
{
// 使用全部旧文件名
sbForOld.Insert(mI, old);
}
else if (m.Groups[2].Success)
{
// 使用旧文件名中的部分
// 交换大小
int i1 = int.Parse(m.Groups[1].Value);
int i2 = int.Parse(m.Groups[2].Value);
if (i1 > i2)
{
int i3 = i1;
i1 = i2;
i2 = i3;
}
// 小的位置存在才替换
if (i1 <= old.Length)
{
if (i2 > old.Length)
i2 = old.Length;
sbForOld.Insert(mI, old.Substring(i1 - 1, i2 - i1 + 1));
}
}
else
{
// 使用旧文件名中的单个字符
int i1 = int.Parse(m.Groups[1].Value);
if (i1 <= old.Length)
sbForOld.Insert(mI, old[i1 - 1]);
}
m = m.NextMatch();
}
ret = sbForOld.ToString();
}
#endregion
#region 大小写处理
if (radAllUpper.Checked)
ret = ret.ToUpper();
else if (radAllLower.Checked)
ret = ret.ToLower();
else if (radUpperFirst.Checked)
{
// 首字母大写,其余小写
ret = ret.ToLower();
ret = ret.Substring(0, 1).ToUpper() + ret.Substring(1);
}
else if (radRevert.Checked)
{
string tmp = string.Empty;
// 反转大小写
foreach (var c in ret)
{
if (c >= 'a' && c <= 'z')
{
tmp += (char)(c - 32);
}
else if (c >= 'A' && c <= 'Z')
{
tmp += (char)(c + 32);
}
else
tmp += c;
}
ret = tmp;
}
else if (radAlterBig.Checked)
{
// 一大写一小写
string tmp = string.Empty;
var i = 0;
// 反转大小写
foreach (var c in ret)
{
if (i++ % 2 == 0)
tmp += c.ToString().ToUpper();
else
tmp += c.ToString().ToLower();
}
ret = tmp;
}
#endregion
ret = ret + ext;
if (rename)
{
try
{
string fileDir = Path.GetDirectoryName(name);
string newName = Path.Combine(fileDir, ret);
if (!File.Exists(name))
{
cancel = !chkContinue.Checked;
rename = false;
ret = "源文件不存在:" + name;
}
else if (regValidFileName.IsMatch(ret))
{
cancel = !chkContinue.Checked;
rename = false;
ret = "文件名存在非法字符:" + newName + " 旧文件名:" + name;
}
else if (File.Exists(newName))
{
if (name.Equals(newName, StringComparison.OrdinalIgnoreCase))
{
// 文件名等于自己时,先重命名为临时格式,再重命名回来,比如不改名,只是大小写替换时
string tmpName = Path.Combine(fileDir, Guid.NewGuid().ToString());
File.Move(name, tmpName);
File.Move(tmpName, newName);
}
else
{
cancel = !chkContinue.Checked;
rename = false;
ret = "文件已经存在:" + newName + " 旧文件名:" + name;
}
}
else
File.Move(name, newName);
//rename = true;// 判断为true进来的,没必要再赋值
}
catch (Exception exp)
{
cancel = !chkContinue.Checked;
rename = false;
ret = exp.ToString();
}
}
else
rename = true;
return ret;
}
#endregion
string GetSn(int idx)
{
string ret = string.Empty;
int minLen = int.Parse(txtSnBit.Text); // 序号的最小长度
string padleft; // 序号长度不足时,前面补充的字符
string tmp = string.Empty;
switch (lstSnType.Text)
{
//case "数字编号":
default:
ret = (idx + int.Parse(txtSnBegin.Text)).ToString();
padleft = "0";
break;
case "大写字母":
ret = AddChar(idx, txtSnBegin.Text, chUpChar);
padleft = "_";
break;
case "小写字母":
ret = AddChar(idx, txtSnBegin.Text, chLowChar);
padleft = "_";
break;
case "汉字编号":
foreach (var c in txtSnBegin.Text)
{
tmp += chNum.IndexOf(c);
}
idx += int.Parse(tmp);
foreach (var c in idx.ToString())
{
ret += chNum[int.Parse(c.ToString())];
}
padleft = "零";
break;
}
while (ret.Length < minLen)
ret = padleft + ret;
return ret;
}
private string lastSn;
private int lastNum = 0;
StringBuilder sbForSn = new StringBuilder();
string AddChar(int idx, string begin, string arr)
{
int len = arr.Length;
string t1 = string.IsNullOrEmpty(lastSn) ? begin : lastSn;
sbForSn.Length = 0;
for (var i = lastNum; i < idx; i++)
{
var m = t1.Length - 1; // 从最后一个字符开始处理
var carr = true; // 是否进位
while (carr && m >= 0)
{
var chR = arr.IndexOf(t1[m]) + 1;
char chN;
if (chR >= len)
{
chN = arr[0];
//carr = true;
}
else
{
chN = arr[chR];
carr = false;
}
sbForSn.Insert(0, chN);
m--;
}
if (carr)
sbForSn.Insert(0, arr[0]);
else if (m >= 0)
sbForSn.Insert(0, t1.Substring(0, m + 1));
t1 = sbForSn.ToString();
sbForSn.Length = 0;
}
lastSn = t1;
lastNum = idx;
return t1;
}
#region 51aspx下载 http://www.51aspx.com
#endregion
#region 右键菜单操作
private void mnuDelSelected_Click(object sender, EventArgs e)
{
if (lvFiles.SelectedItems.Count <= 0)
{
ShowMsg("都没选择东西,你让我删啥?");
return;
}
for (int i = lvFiles.SelectedItems.Count - 1; i >= 0; i--)
{
var item = lvFiles.SelectedItems[i];
lvFiles.Items.Remove(item);
files.Remove(item.SubItems[1].Text);
}
}
private void mnuDelAll_Click(object sender, EventArgs e)
{
btnClear_Click(null, null);
}
private void mnuMovePrev_Click(object sender, EventArgs e)
{
if (lvFiles.SelectedItems.Count <= 0)
{
ShowMsg("都没选择东西,你让我干啥?");
return;
}
var idx = lvFiles.SelectedItems[0].Index;
//if (idx == 0)
//{
// ShowMsg("已经在最前头了,我把它移哪去?");
// return;
//}
idx--;// 要上移一行,所以要减一
var tmpArr = new List<ListViewItem>(lvFiles.SelectedItems.Count);
foreach (ListViewItem item in lvFiles.SelectedItems)
{
tmpArr.Add(item);
lvFiles.Items.RemoveAt(item.Index);
}
foreach (var item in tmpArr)
{
lvFiles.Items.Insert(idx++, item);
}
ReIndex(lvFiles);
//idx--;
//foreach (ListViewItem item in lvFiles.SelectedItems)
for (int i = lvFiles.SelectedItems.Count - 1; i >= 0; i--)
//{
// var tmp = item.SubItems[1].Text;
// item.SubItems[1].Text = lvFiles.Items[idx].SubItems[1].Text;
// lvFiles.Items[idx].SubItems[1].Text = tmp;
// idx++;
// //var item = lvFiles.SelectedItems[i];
// //lvFiles.Items.RemoveAt(item.Index);
// //lvFiles.Items.Insert(idx, item); // 直接Insert会导致顺序号错乱
//}
}
private void mnuMoveNext_Click(object sender, EventArgs e)
{
if (lvFiles.SelectedItems.Count <= 0)
{
ShowMsg("都没选择东西,你让我干啥?");
return;
}
var idx = lvFiles.SelectedItems[lvFiles.SelectedItems.Count - 1].Index;
//if (idx == lvFiles.Items.Count - 1)
//{
// ShowMsg("已经在最后头了,我把它移哪去?");
// return;
//}
idx = idx - lvFiles.SelectedItems.Count + 2;// 要下移一行,所以要加一
var tmpArr = new List<ListViewItem>(lvFiles.SelectedItems.Count);
foreach (ListViewItem item in lvFiles.SelectedItems)
{
tmpArr.Insert(0, item);
lvFiles.Items.RemoveAt(item.Index);
}
foreach (var item in tmpArr)
{
lvFiles.Items.Insert(idx, item);
}
ReIndex(lvFiles);
}
private void mnuMoveFirst_Click(object sender, EventArgs e)
{
if (lvFiles.SelectedItems.Count <= 0)
{
ShowMsg("都没选择东西,你让我干啥?");
return;
}
var idx = lvFiles.SelectedItems[0].Index;
if (idx == 0)
{
ShowMsg("已经在最前头了,我把它移哪去?");
return;
}
var tmpArr = new List<ListViewItem>(lvFiles.SelectedItems.Count);
foreach (ListViewItem item in lvFiles.SelectedItems)
{
tmpArr.Insert(0, item);
lvFiles.Items.RemoveAt(item.Index);
}
foreach (var item in tmpArr)
{
lvFiles.Items.Insert(0, item);
}
ReIndex(lvFiles);
}
private void mnuMoveBottom_Click(object sender, EventArgs e)
{
if (lvFiles.SelectedItems.Count <= 0)
{
ShowMsg("都没选择东西,你让我干啥?");
return;
}
var idx = lvFiles.SelectedItems[lvFiles.SelectedItems.Count - 1].Index;
if (idx == lvFiles.Items.Count - 1)
{
ShowMsg("已经在最后头了,我把它移哪去?");
return;
}
var tmpArr = new List<ListViewItem>(lvFiles.SelectedItems.Count);
foreach (ListViewItem item in lvFiles.SelectedItems)
{
tmpArr.Add(item);
lvFiles.Items.RemoveAt(item.Index);
}
foreach (var item in tmpArr)
{
lvFiles.Items.Add(item);
}
ReIndex(lvFiles);
}
#endregion
void ShowMsg(string msg)
{
MessageBox.Show(msg);
}
/// <summary>
/// 重新给排序
/// </summary>
/// <param name="lv"></param>
private void ReIndex(ListView lv)
{
var i = 1;
foreach (ListViewItem item in lv.Items)
{
item.SubItems[0].Text = i++.ToString();
}
}
private void lvFiles_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete)
{
for (int i = lvFiles.SelectedItems.Count - 1; i >= 0; i--)
{
var item = lvFiles.SelectedItems[i];
lvFiles.Items.Remove(item);
files.Remove(item.SubItems[1].Text);
}
}
}
}
}