最近工作需要,需要遍历文件夹下的文件,但是我发现遇到两个非常有意思的问题,记录一下。 测试的平台为
PC linux | Tina linux |
---|
Linux version 5.15.0-41-generic (buildd@lcy02-amd64-105) (gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #44~20.04.1-Ubuntu SMP Fri Jun 24 13:27:29 UTC 2022 | Linux version 4.9.191 (@AI-S003) (gcc version 6.4.1 (OpenWrt/Linaro GCC 6.4-2017.11 2017-11) ) #1 PREEMPT Tue Sep 6 02:14:55 UTC 2022 | ubuntu | 全志 |
Tina linxu 和PC linux的运行结果不同,直接点,就是编译链不同导致的结果不同,linux 系统版本不一样,导致的结果也不一样 ,说了跟没说一样
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "time.h"
#include <iostream>
#include <math.h>
#include <vector>
#include <dirent.h>
#include <string.h>
#include<algorithm>
#include <sys/stat.h>
#include <sys/types.h>
#include <fstream>
#include <cstdarg>
#include <vector>
using namespace std;
vector<string> files;
int getAbsoluteFiles(string path, vector<string>& filesAbsolutePath)
{
DIR* dir = opendir(path.c_str());
if ( dir == NULL ){
cout<< path <<" is not a directory or not exist!"<<endl;
return -1;
}
struct dirent* d_ent = NULL;
char fullpath[128] = {0};
while ( (d_ent = readdir(dir)) != NULL ){
if ( (strncmp(d_ent->d_name, "." ,1) != 0) ){
if ( d_ent->d_type == DT_DIR ){
string newDirectory = path + string("/") + string(d_ent->d_name);
if( path[path.length()-1] == '/'){
newDirectory = path + string(d_ent->d_name);
}
if ( -1 == getAbsoluteFiles(newDirectory, filesAbsolutePath) ){
return -1;
}
}else {
string absolutePath = path + string("/") + string(d_ent->d_name);
if( path[path.length()-1] == '/'){
absolutePath = path + string(d_ent->d_name);
}
filesAbsolutePath.push_back(absolutePath);
}
}
}
sort(filesAbsolutePath.begin(), filesAbsolutePath.end());
closedir(dir);
return 0;
}
void listDir(char *path)
{
DIR *pDir ;
struct dirent *ent ;
int i=0 ;
char childpath[512];
pDir=opendir(path);
memset(childpath,0,sizeof(childpath));
while((ent=readdir(pDir))!=NULL)
{
if(ent->d_type & DT_DIR){
if(strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
continue;
sprintf(childpath,"%s/%s",path,ent->d_name);
listDir(childpath);
}else{
char fileName[1024] = {0};
snprintf(fileName, 1024, "%s/%s",path,ent->d_name);
files.push_back(fileName);
}
}
sort(files.begin(), files.end());
}
vector<string> getfiles(string strCurrentDir){
vector<string> vFiles;
DIR *dir;
struct dirent *pDir;
if((dir = opendir(strCurrentDir.c_str())) == NULL){
cout << "open dir Faild" << endl;
exit(1);
}
while((pDir = readdir(dir)) != NULL){
if(strcmp(pDir->d_name,".")==0 || strcmp(pDir->d_name,"..")==0){
continue;
}else if(pDir->d_type == DT_REG){
vFiles.push_back(strCurrentDir + "/" + pDir->d_name);
}else if(pDir->d_type == DT_UNKNOWN){
continue;
}else if(pDir->d_type == DT_DIR){
string strNextdir = strCurrentDir + "/" + pDir->d_name;
vector<string> ss = getfiles(strNextdir);
vFiles.insert(vFiles.end(),ss.begin(),ss.end());
}
}
closedir(dir);
sort(vFiles.begin(), vFiles.end());
return vFiles;
}
int main(int argc, char *argv[])
{
char path[512] = {0};
int flag = 0;
if(argc == 3)
flag = atoi(argv[2]);
if(flag == 0)
getAbsoluteFiles(argv[1],files);
else if(flag == 1)
listDir(argv[1]);
else
files = getfiles(argv[1]);
for(vector<string>::iterator it = files.begin(); it != files.end();it++){
string name = *it;
cout << name.c_str()<<endl;
}
cout << "count " << files.size() << endl;
return 0;
}
上述代码是三种不同的方式实现遍历文件夹,但是都大同小异, 下面是在pc linxu 的执行结果,其中test_image是测试文件路径,其中有多重子目录,
./a.out ~/test_image/ 0
......
/test_image/xxx/xxxx/we-027.jpg
count 5407
./a.out ~/test_image/ 1
......
/test_image/xxx/xxxx/we-027.jpg
count 5407
./a.out ~/test_image/ 2
......
/test_image/xxx/xxxx/we-027.jpg
count 5407
很明显结果没什么问题,但是当我移到Tina linux上时,结果如下
./a.out ~/test_image/ 0
......
/test_image/xxx/xxxx/we-027.jpg
count 5407
./a.out ~/test_image/ 1
......
/test_image/xxx/xxxx/we-027.jpg
count 5411
./a.out ~/test_image/ 2
......
/test_image/xxx/xxxx/we-021.jpg
count 1588
结果相差甚远
问题一:.和…被认为是普通文件
查看了下编译链的版本,交叉编译链是6.4.1 pc本地编译链是9.4.0,编译链的差距暂时是更改不了的,至于要怎么解决问题,只能从代码入手
通过保存log文件来对比 getAbsoluteFiles 和 listDir 的结果差异,发现是其中某个文件夹中的**.和…**也被算进去了, 但是这里就有一个有意思的问题,test_image中有四个文件夹,唯独只有这一个文件夹出现这种情况,而且这四个文件夹本质上是一样的,不同的只是 里面的文件不同,为什么其他文件夹没有这个问题,而单独的就一个文件夹有问题,我至今都没有搞懂是什么原因。
仔细对别getAbsoluteFiles 和listDir, 发现getAbsoluteFiles是先排除**.和…,然后才进行目录和文件的判断。而listDir则是先判断文件和目录,只有是目录时才进行排除.和…** 那是不是在交叉编译链中,有个时候或者说是在某种情况下会把**.和…当成文件,所以listDir才会出现这种情况,那直接在非目录的代码里加入排除.和…**,看看效果
void listDir(char *path)
{
DIR *pDir ;
struct dirent *ent ;
int i=0 ;
char childpath[512];
pDir=opendir(path);
memset(childpath,0,sizeof(childpath));
while((ent=readdir(pDir))!=NULL)
{
if(ent->d_type & DT_DIR){
if(strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
continue;
sprintf(childpath,"%s/%s",path,ent->d_name);
listDir(childpath);
}else{
if(strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
continue;
char fileName[1024] = {0};
snprintf(fileName, 1024, "%s/%s",path,ent->d_name);
files.push_back(fileName);
}
}
sort(files.begin(), files.end());
}
运行结果
//1 代表执行的是listDir
./a.out ~/test_image/ 1
......
/test_image/xxx/xxxx/we-027.jpg
count 5407
现在就正确了,但是其中为什么会出现这个问题,我目前是说不出来一个所以然的,可能是知识面匮乏,暂时还不清楚。 目前只这样认为的:因为编译链和操作系统版本不一样,可能是版本相差太大的原因,底层的实现逻辑可能有很大的变化,对于目前的这个问题,可能是有些操作系统会把.和…认为是普通文件,而不是目录文件。 但是又有点说不通,四个文件夹中为什么只有一个文件夹出现这个问题,我也看过文件夹有什么不同,但是并没有什么不同,权限也是一样的。如果有哪位大佬知道具体原因,麻烦指导一下。
问题二:有些文件被认为在不同的系统被认为的文件类型不一样
现在来看getfiles函数的实现,这个函数是根据得到的文件类型来进行判断,至于这些宏定义是什么意义,可以通过man来查看,如下图所示
然后再pc端跑的结果和其他两个函数是正常的,但是在嵌入式平台上是及其不正常,少了很多文件,我们来做如下测试,把代码给成如下所示
vector<string> getfiles(string strCurrentDir){
vector<string> vFiles;
DIR *dir;
struct dirent *pDir;
if((dir = opendir(strCurrentDir.c_str())) == NULL){
cout << "open dir Faild" << endl;
exit(1);
}
while((pDir = readdir(dir)) != NULL){
if(strcmp(pDir->d_name,".")==0 || strcmp(pDir->d_name,"..")==0){
continue;
}else if(pDir->d_type == DT_DIR){
string strNextdir = strCurrentDir + "/" + pDir->d_name;
vector<string> ss = getfiles(strNextdir);
vFiles.insert(vFiles.end(),ss.begin(),ss.end());
}else if(pDir->d_type == DT_REG){
vFiles.push_back(strCurrentDir + "/" + pDir->d_name);
}
else
{
cout << "else" << ":" << pDir->d_name <<endl;
else_count++;
}
}
closedir(dir);
sort(vFiles.begin(), vFiles.end());
return vFiles;
}
我们来对比一下结果
./a.out ~/test_image/ 2
......
/test_image/xxxx/xxxx
count 5407
elsecount 0
./a.out ~/test_image/ 2
......
/data/test_image/xxxx/xxxx/we-021.jpg
count 1588
elsecount 3819
结果如上,在Tina系统中,有三千多的文件被认为是其他类型的文件,我这些测试文件中全部都是.jpg文件,所以理论上类型应该是一样的,所以解决方法也很简单 ,就是目录文件为一类,其他都当作普通文件即可,如下代码可以得到我们想要的结果
vector<string> getfiles(string strCurrentDir){
vector<string> vFiles;
DIR *dir;
struct dirent *pDir;
if((dir = opendir(strCurrentDir.c_str())) == NULL){
cout << "open dir Faild" << endl;
exit(1);
}
while((pDir = readdir(dir)) != NULL){
if(strcmp(pDir->d_name,".")==0 || strcmp(pDir->d_name,"..")==0){
continue;
}else if(pDir->d_type == DT_DIR){
string strNextdir = strCurrentDir + "/" + pDir->d_name;
vector<string> ss = getfiles(strNextdir);
vFiles.insert(vFiles.end(),ss.begin(),ss.end());
}else{
vFiles.push_back(strCurrentDir + "/" + pDir->d_name);
}
}
closedir(dir);
sort(vFiles.begin(), vFiles.end());
return vFiles;
}
目前得出的结论是,有些文件在不同的系统看来是不同类型的文件。
正确代码
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "time.h"
#include <iostream>
#include <math.h>
#include <vector>
#include <dirent.h>
#include <string.h>
#include<algorithm>
#include <sys/stat.h>
#include <sys/types.h>
#include <fstream>
#include <cstdarg>
#include <vector>
using namespace std;
vector<string> files;
int getAbsoluteFiles(string path, vector<string>& filesAbsolutePath)
{
DIR* dir = opendir(path.c_str());
if ( dir == NULL ){
cout<< path <<" is not a directory or not exist!"<<endl;
return -1;
}
struct dirent* d_ent = NULL;
char fullpath[128] = {0};
while ( (d_ent = readdir(dir)) != NULL ){
if ( (strncmp(d_ent->d_name, "." ,1) != 0) ){
if ( d_ent->d_type == DT_DIR ){
string newDirectory = path + string("/") + string(d_ent->d_name);
if( path[path.length()-1] == '/'){
newDirectory = path + string(d_ent->d_name);
}
if ( -1 == getAbsoluteFiles(newDirectory, filesAbsolutePath) ){
return -1;
}
}else {
string absolutePath = path + string("/") + string(d_ent->d_name);
if( path[path.length()-1] == '/'){
absolutePath = path + string(d_ent->d_name);
}
filesAbsolutePath.push_back(absolutePath);
}
}
}
sort(filesAbsolutePath.begin(), filesAbsolutePath.end());
closedir(dir);
return 0;
}
void listDir(char *path)
{
DIR *pDir ;
struct dirent *ent ;
int i=0 ;
char childpath[512];
pDir=opendir(path);
memset(childpath,0,sizeof(childpath));
while((ent=readdir(pDir))!=NULL)
{
if(ent->d_type & DT_DIR){
if(strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
continue;
sprintf(childpath,"%s/%s",path,ent->d_name);
listDir(childpath);
}else{
if(strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
continue;
char fileName[1024] = {0};
snprintf(fileName, 1024, "%s/%s",path,ent->d_name);
files.push_back(fileName);
}
}
sort(files.begin(), files.end());
}
int else_count = 0;
vector<string> getfiles(string strCurrentDir){
vector<string> vFiles;
DIR *dir;
struct dirent *pDir;
if((dir = opendir(strCurrentDir.c_str())) == NULL){
cout << "open dir Faild" << endl;
exit(1);
}
while((pDir = readdir(dir)) != NULL){
if(strcmp(pDir->d_name,".")==0 || strcmp(pDir->d_name,"..")==0){
continue;
}else if(pDir->d_type == DT_DIR){
string strNextdir = strCurrentDir + "/" + pDir->d_name;
vector<string> ss = getfiles(strNextdir);
vFiles.insert(vFiles.end(),ss.begin(),ss.end());
}else{
vFiles.push_back(strCurrentDir + "/" + pDir->d_name);
}
}
closedir(dir);
sort(vFiles.begin(), vFiles.end());
return vFiles;
}
int main(int argc, char *argv[])
{
char path[512] = {0};
int flag = 0;
if(argc == 3)
flag = atoi(argv[2]);
if(flag == 0)
getAbsoluteFiles(argv[1],files);
else if(flag == 1)
listDir(argv[1]);
else
files = getfiles(argv[1]);
for(vector<string>::iterator it = files.begin(); it != files.end();it++){
string name = *it;
cout << name.c_str()<<endl;
}
cout << "count " << files.size() << endl;
cout << "elsecount " << else_count << endl;
return 0;
}
|