文件重命名小程序(适用于unix系c++)

注:此代码还是有问题,如果只重编号而前缀后缀都不变的话,仍然会出现重名覆盖的情况,例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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值