注:此代码还是有问题,如果只重编号而前缀后缀都不变的话,仍然会出现重名覆盖的情况,例a0.b a1.b a2.b,想重命名为a1.b a2.b a3.b,此代码会将a0命名为a1,于是原来的a1就被覆盖了。现在想到的办法是先循环一边源文件名重命名为新名字+temp后缀,然后再循环一次将新名字+temp来rename为新名字,这样就能解决问题,但消耗比较大效率不够,期待有更好解决方法。修改代码就不贴了。
前段时间整理电脑发现好多下载的图片、剧集什么的名字很乱,想把它们都重命名一遍。网上找了一圈都是win系小程序,没有OS X适用的版本,于是只能自己写。发上来留个记录,写得不咋地但还算好用。
需求逻辑很简单,就是不改后缀只重命名前缀(后来增加为全可修改),可以递归子目录。
网上搜了下相关文章,关键函数就是readdir了。鼓捣鼓捣后先拿一个目录下的乱文件试试。运行程序重命名前缀后,发现竟然丢文件了!直觉就是后被readdir读入的文件被重命名为了已重命名文件的名字。实验了发现readdir是以首字符排序读入文件的,例如有图片名字为1.jpg、2.jpg、10.jpg,它是1、10、2的顺序读入。于是我的程序按照读入顺序将1命名为1,10命名为2,那么此时原来的2就被原来的10覆盖掉了,导致丢文件。那么就先将所有文件名读入一个vector,然后将其按数字大小排序,再重命名就行了。
单个文件下的命名解决,递归子目录的也不难,加个while循环提供选择就行。
完成后的程序还算好用,可以只命名前缀、只命名后缀、全部重命名、保留原序号重命名什么的,例如把苍老师的作品批量命名为《明治维新之艺妓篇1-10》
代码如下:
/*
* 如果直接修改文件名,就算先将文件名入栈留做比较用,
* 也会由于readdir按首字符排序读文件再修改,出现10命名为2后,
* 覆盖原2文件
*/
#include <iostream>
#include <string>
#include <dirent.h>
#include <sstream>
#include <vector>
#include <iterator>
#define STRING_SIZE 1024
using namespace std;
typedef vector<string> FileName_Vector;
typedef vector<string>::iterator FNV_Iter;
#pragma mark - 个人用debug相关
#define ABYNDEBUG
#ifdef ABYNDEBUG
// 调试用打印函数
void debugFunction(struct dirent const *dp)
{
cout << "目标文件的各属性:";
cout << "d_ino-->" << dp->d_ino << " ";
cout << "d_seekoff-->" << dp->d_seekoff << " ";
cout << "d_reclen-->" << dp->d_reclen << " ";
cout << "d_namlen-->" << dp->d_namlen << " ";
cout << "d_type-->" << dp->d_type << " ";
cout << "d_name-->" << dp->d_name << endl;
}
#else
void debugFunction(struct dirent const *dp){};
#endif
void displayVector(FileName_Vector &fnv)
{
for (FNV_Iter iter = fnv.begin(); iter != fnv.end(); iter++) {
cout << *iter << " ";
}
cout << "-----------------------------" << endl;
}
// 判断读入文件后缀是否为"ini" "db" "thumb"
string suffixFromOldName(string source);
bool isSystemObjs(struct dirent *dp)
{
if (*(dp->d_name) == '.' || strcmp(dp->d_name, "..") == 0) {
return true;
}
string result = suffixFromOldName(dp->d_name);
if (strcmp(result.c_str(), "ini") == 0 || strcmp(result.c_str(), "db") == 0 || strcmp(result.c_str(), "thumb") == 0 )
{
return true;
}
return false;
}
#pragma mark - 用户输入名字编号等操作相关函数
// 从用户输入获取newName_number
int gainNewname_Number()
{
string newName_number;
cout << "请输入新文件名开始的编号:" <<endl;
getline(cin, newName_number, '\n');
return atoi(newName_number.c_str());
}
// 获取old_name中的prefix用于比较rename
string prefixFromOldName(string source)
{
for (int i = (int)source.length() - 1; i > 0; i--) {
if (source[i] == '.') {
return (source.substr(0, i));
}
}
return source;
}
// 从用户输入获取newName_prefix
void gainNewname_Prefix(string *newName_prefix)
{
newName_prefix->clear();
cout << "请输入新文件名的前缀:" <<endl;
getline(cin, *newName_prefix, '\n');
}
// 从用户输入获取newName_suffix
void gainNewname_Suffix(string *newName_Suffix)
{
newName_Suffix->clear();
cout << "请输入新文件名的后缀:" <<endl;
getline(cin, *newName_Suffix, '\n');
}
// 重新编号时文件的index
string intToString(long index)
{
stringstream strstream;
strstream << index;
return strstream.str();
}
// 显示输出当前目录路径
void displayCurrentPath(string path)
{
cout << "当前目录-->" << path << endl;
}
// 获取old_name的后缀,从后往前找
string suffixFromOldName(string source)
{
for (int i = (int)source.length() - 1; i > 0; i--) {
if (source[i] == '.')
return source.substr(i + 1, (int)source.length() - i);
}
return "";
}
#pragma mark - 以FileNameVector的形式来处理重命名
// 遍历文件夹将所有文件入栈并升序排序
// 获取old_name中的数字用于新编号
int numbersFromOldName(string source)
{
int up, down;
int flag = 0;
for (int i = 0; i < source.length() && flag == 0; i++) {
if (source[i] >= '0' && source[i] <= '9') {
up = i;
for (int i = up; i < source.length(); i++) {
if (source[i] >= '0' && source[i] <= '9')
;
else {
down = i;
flag = 1;
}
}
}
}
return atoi( (source.substr(up, (down - up))).c_str() );
}
bool asendVector(const string s1, const string s2)
{
return numbersFromOldName(s1) < numbersFromOldName(s2);
}
// 以filename向量组的形式重命名
void renameFileNameInVector(FileName_Vector &fnv, string const *pathName, string const *newName_input, int index, string *name_suffix)
{
string *oldname = new string, *newname = new string, *newName_index = new string;
string *name_prefix = new string;
string new_suffix;
for (FNV_Iter iter = fnv.begin(); iter != fnv.end(); iter++) {
*oldname = *pathName + *iter;
*newName_index = intToString(index);
if (*newName_input == "") { //统一前缀
*name_prefix = prefixFromOldName(*iter);
}
else
*name_prefix = *newName_input + *newName_index;
if (*name_suffix == "") { //统一后缀
new_suffix = suffixFromOldName(*iter);
}
else
new_suffix = *name_suffix;
*newname = *pathName + *name_prefix + "." + new_suffix;
string::size_type point = string::npos;
if ( (point = iter->rfind('.')) == string::npos) {
cout << "该文件无后缀-->" << *oldname << endl;
}
else {
if (rename((*oldname).c_str(), (*newname).c_str()) == 0) {
printf("改名成功完成, newname-->%s\n", newname->c_str());
index++;
}
else
perror("rename");
}
}
delete oldname;
delete newname;
delete newName_index;
delete name_prefix;
}
// 递归子目录
void recursionDirectory(const string ¤t_pathName, const string &abslutePath)
{
struct dirent *dp = NULL;
DIR *dirp=NULL;
dirp = opendir(current_pathName.c_str());
if(dirp == NULL)
{
printf("打开DIR失败!也可能是递归返回。。。");
system("PAUSE");
return;
}
string *newName_suffix = new string;
string *newName_input = new string;
int index = 0;
FileName_Vector fnv;
/
if ( (dp = readdir(dirp))->d_type == DT_DIR ) {
cout << "当前目录-->" << current_pathName + dp->d_name << endl;
cout << "请选择操作:1、前缀重命名;2、后缀重命名;3、全新重命名;4、跳过命名当前文件夹" << endl;
string inputs;
getline(cin, inputs, '\n');
switch ( *(inputs.c_str()) ) {
case '1':
{
gainNewname_Prefix(newName_input);
//oldname的后缀要进入readdir才能拿到
index = gainNewname_Number();
}
break;
case '2':
{
gainNewname_Suffix(newName_suffix);
}
break;
case '3':
{
displayCurrentPath(current_pathName);
gainNewname_Prefix(newName_input);
index = gainNewname_Number();
gainNewname_Suffix(newName_suffix);
}
break;
case '4':
return; //直接跳出本次递归
}
}
/
while ( (dp = readdir(dirp)) != NULL) {
if (isSystemObjs(dp)) {
debugFunction(dp);
continue;
}
else if (dp->d_type == DT_REG) {
debugFunction(dp);
fnv.push_back(dp->d_name); //旧名字进栈
}
else if (dp->d_type == DT_DIR) {
debugFunction(dp);
string string_d_name = string(dp->d_name);
string ts = current_pathName + string_d_name + "/";
recursionDirectory(ts, abslutePath);
}
}
//拿到所有名字后进行重命名
if (fnv.size()) {
// displayVector(fnv);
sort(fnv.begin(), fnv.end(), asendVector);
// displayVector(fnv);
renameFileNameInVector(fnv, ¤t_pathName, newName_input, index, newName_suffix);
}
delete newName_input;
delete newName_suffix;
closedir(dirp);
free(dp);
}
#pragma mark - MAIN
int main(void)
{
string abslutePath;
cout << "请输入目标目录(可含有子目录):";
getline(cin, abslutePath, '\n');
abslutePath.append("/");
recursionDirectory(abslutePath, abslutePath);
return 0;
}