按顺序读取文件夹所有文件并重命名

本周计划
  • 完成文件的依次读取并且命名的修改
  • 完成点云轨迹的绘制

1.完成文件的依次读取并且命名

在这里插入图片描述

文件的命名是这样的,依次排列,我们的目标是将其修改为1.pcd,2.pcd …,以下使用C++完成任务,以及记录下在这过程遇到的错误,参考的网址放在最下面

1.读取某个文件夹下面所有“.xxx”格式的文件,读取的函数在io.h中,用到的函数有这就要靠_findfirst、_findnext和_fineclose,接下来先来讲下这三个函数的作用

_findfirst函数原型为:long _findfirst( char *filespec, struct _finddata_t *fileinfo );

findfirst功能:搜索与指定的文件名称匹配的第一个实例,若成功则返回第一个实例的句柄,否则返回-1L。

filespec:标明文件的字符串,可支持通配符。比如:*.c,则表示当前文件夹下的所有后缀为C的文件。

fileinfo :这里就是用来存放文件信息的结构体的指针。这个结构体必须在调用此函数前声明,不过不用初始化,只要分配了内存空间就可以了。函数成功后,函数会把找到的文件的信息放入这个结构体中。

_findnext的函数原型是: int _findnext( long handle, struct _finddata_t *fileinfo );

findnext功能:搜索与findfirst函数提供的文件名称匹配的下一个实例,若成功则返回0,否则返回-1

handle:即由_findfirst函数返回回来的句柄。

fileinfo:文件信息结构体的指针。找到文件后,函数将该文件信息放入此结构体中。

_findclose的函数原型是int _findclose( long handle );

_findclose的功能是关闭指定的搜索句柄并释放相关资源,成功返回0,失败返回-1

handle :_findfirst函数返回回来的句柄。

//文件的路径,也可以使用cin自己指定,例如cin>>path
char * filePath = "D:\\sample";  



void getFiles( string path, vector<string>& files )  
{  
    //文件句柄  
    long   hFile   =   0;  
    //文件信息  
    struct _finddata_t fileinfo;  
    string p;  
    if((hFile = _findfirst(p.assign(path).append("\\*").c_str(),&fileinfo)) !=  -1)  
    {  
        do  
        {  
            //如果是目录,迭代之  
            //如果不是,加入列表  
            if((fileinfo.attrib &  _A_SUBDIR))  
            {  
                if(strcmp(fileinfo.name,".") != 0  &&  strcmp(fileinfo.name,"..") != 0)  
                    getFiles( p.assign(path).append("\\").append(fileinfo.name), files );  
            }  
            else  
            {  
                //如果是文件的化,放进来,这个加了路径名的path的命名
                files.push_back(p.assign(path).append("\\").append(fileinfo.name) );  
            }  
        }while(_findnext(hFile, &fileinfo)  == 0);  
        _findclose(hFile);  
    }  
}
int main() {
	string file_path;
	cout << "请输入文件夹的名字:";
	cin >> file_path;
    
	vector<string> files;
	getFiles(file_path, files);
	int size = files.size();
    
	cout << "file size is :" << size << endl;
	cout << "----------------------" << endl;
    
	for (int i = 0; i < size; i++) {
		cout << files[i]<< endl;
	}
}

在这里插入图片描述

直接这样用的话,会出现这种情况,就是读取的顺序发生了变化,比如上面的图第一个文件时2973157.pcd,但是现在已经发生了变化,如果我直接修改名字的话,那么重命名的文件0.pcd 对应着是1002980999.pcd ,而不是2973157.pcd ,产生这个的原因是由于window下读取文件是按 字符串比较的顺序进行排列的 ,字符串“22”比“1000”大。所以我们需要对排序进行调整,也就是使用比较器。

我们的目标是:

在这里插入图片描述

1.首先我们对刚才的getFiles函数进行一个小修改

//files.push_back(p.assign(path).append("\\").append(fileinfo.name) ); 
files.push_back(fileinfo.name);//不包含路径,只包含文件名

2.接着我们写一个C++比较器,然后再利用sort函数便可以实现排序了。

//比较器的思路是先将字符串转化为数字,然后对数字进行比较
bool comparator(const string& src1, const string& src2) {

	string find_src = ".pcd";//

	 int num1 = stoi(src1.substr(0, src1.find(find_src)));//关于字符串的使用可以查看末尾的链接
	 int num2 = stoi(src2.substr(0, src2.find(find_src)));//stoi是将字符转数字,
	if (num1 > num2) {
		return 0;
	}
	else
	{
		return 1;//return 1 实现从小到大 
	}
}
//接着我们main函数打印之前sort一下

	vector<string> files;
	getFiles(file_path, files);
    sort(files.begin(), files.end(), comparator);

	for (int i = 0; i < size; i++) {
		cout << files[i]<< endl;
	}

接下来才是大坑来临了,一运行这个程序就出现了在这里插入图片描述

由于是新手,不知道啥原因,开始上网搜各种帖子,还是没能解决,开始单步调试大法,一直运行感觉也没问题,然后突然想到文件的末尾是

在这里插入图片描述

这已经超出了int 类型了,所以比较器那里用int来接肯定不行,于是想到了修改比较器,由于自动转型c++中只有提供到stoi,而我们需要long long int ,所以再多些一个函数

long long int f(const string& str) {
	long long int res = 0;
	for (int i = 0; i < str.size(); i++)
	{
		res = res * 10 + str[i] - '0';

	}
	return res;
}
bool comparator(const string& src1, const string& src2) {

	string find_src = ".pcd";
    
	long long int num1 = f(src1.substr(0, src1.find(find_src)));
	long long int num2 = f(src2.substr(0, src2.find(find_src)));
	if (num1 > num2) {
		return 0;
	}
	else
	{
		return 1;
	}
}

修改完这个比较器后,就可以实现按顺序进行打印了,接下来我们要对这个排好序的文件进行重命名,以便我在Linux下对这些文件进行顺序读取,这些文件其实是点云文件,本来就是按时间顺序进行排序的。

这里会使用到rename函数,下文也有链接,大家可以自己看下简单的示例,然后我们对他进行稍微的修改

int rename( const char *oldname, const char *newname );

The rename() function takes a two arguments: oldname, newname and returns an integer value. It renames the file represented by the string pointed to by oldname to the string pointed to by newname.


	string path = "C:\\Users\\zhuang\\Desktop\\dataset\\";
	for (int i = 0; i < size; i++) {
		if (rename((path + files[i]).c_str(), (path + to_string(i)).c_str()) != 0) {
			perror("Error renaming file");
		}
		else {
			cout << "File renamed successfully";
		}
	}
//rename((path + files[i]).c_str(), (path + to_string(i)).c_str())这个语句是找到我们文件夹下的文件并进行命名修改
//注意一点就是今天3/28给蠢了自己一下,想把生成的文件放进一个新的目录中,使用rename((file_path + files[i]).c_str(), (path + to_string(i)).c_str()),
//rename指令只是修改文件的名字,并不能实现文件的拷贝!

总结:

​ 应该要对ROS上面采集的雷达数据格式进行修改,毕竟现在只采集了几分钟,数据文件的命名就变成这样子,要是再大点,long long int 也装不下了.


参考的文章:

微软c++官方文档https://docs.microsoft.com/en-us/cpp/c-runtime-library/filename-search-functions?view=vs-2017

文件读取https://www.cnblogs.com/fnlingnzb-learner/p/6424563.html

rename 函数https://www.programiz.com/cpp-programming/library-function/cstdio/rename

类型转换https://blog.csdn.net/HiccupHiccup/article/details/62421032

c++字符串截取,替换,查找函数 https://blog.csdn.net/ezhou_liukai/article/details/13779091

批量修改名字https://blog.csdn.net/tom555cat/article/details/17389247

rename 的用法https://stackoverflow.com/questions/37229205/use-of-atoi-function-in-c

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值