曾经有这么一个需要,就想自己实现一个。虽然我知道java内置文件操作的库而C++没有,但还是不想换语言(文人相轻,程序猿更相轻)。于是就上网找了一段C版本的代码,就是这个:
1 #include <iostream> 2 #include <vector> 3 #include <string> 4 #include <cstring> 5 #include <io.h> 6 using namespace std; 7 /** 8 *获取文件夹中所有文件名 9 input 10 path: 文件夹路径 11 exd: 文件后缀,如为空则全选 12 output 13 files: 容器 14 */ 15 void getFileName(string path, string exd, vector<string>& files) 16 { 17 /// 文件句柄 18 long hFile = 0; 19 /// 文件信息 20 struct _finddata_t fileinfo; 21 string pathName(path), exdName = "\*."+exd; 22 if(hFile = _findfirst((pathName+exdName).c_str(), &fileinfo) != -1) 23 { 24 do 25 {///如果有文件夹,迭代之 26 if(fileinfo.attrib&_A_SUBDIR) 27 { 28 if(strcmp(fileinfo.name,".") && strcmp(fileinfo.name,"..")) 29 getFileName(pathName+"\"+fileinfo.name, exd, files); 30 } 31 else 32 { 33 if(strcmp(fileinfo.name,".") && strcmp(fileinfo.name,"..")) 34 files.push_back(fileinfo.name); 35 } 36 }while(_findnext(hFile, &fileinfo)==0); 37 /**这段代码跑出来实际上只能找到一个文件就结束了,找原因还很麻烦 38 */ 39 _findclose(hFile); 40 } 41 } 42 int main() 43 { 44 string path("E:"); 45 vector<string> files; 46 getFileName(path,"",files); 47 for(int i = 0; i < files.size(); i++) 48 cout<<files[i]<<endl; 49 return 0; 50 }
结果调了一上午还没调过,真是坑爹。
后来发现用dos也能做,而且简单到让我想撞墙,就像这样
@echo off
dir /s/b *.txt > 文件名.txt
exit
就这么一行,dir就不用说了,/s表示递归遍历,/b表示一个文件一行,*.txt表示查找txt文件,然后写入到一个文件中。
把它保存为bat,放入当前文件夹就可了。不过它输出的是绝对路径,还得自己再加工。
问题是解决了,可是也太快了,我还没感觉呢。
后来,我试着用NB的python解决,在实现的过程中,我感觉我就是在搞翻译。比如我知道要“获取当前文件夹”,然后python里是哪个函数有这个功能呢?找。我知道要“递归遍历”,但是python里是哪个函数有这个功能呢?找。大部分时间就是在看文档,学习语法,模块,并没有什么乐趣,也许是因为我对这个语言不熟吧。最后实现了递归与非递归两个版本,如下所示:
1 import os 2 import fnmatch 3 f=open("图片.txt","w+") 4 for file in os.listdir(os.getcwd()): 5 if fnmatch.fnmatch(file, '*.jpg'): 6 f.write(file+" ") 7 # f.write(os.path.realpath(file)+" ") 8 f.close()
1 import os 2 import fnmatch 3 f=open("图片.txt","w+") 4 for tup in os.walk(os.getcwd()): 5 lst = tup[2] 6 dirpath = tup[0] 7 for file in lst: 8 if fnmatch.fnmatch(file, '*.jpg'): 9 f.write(os.path.join(dirpath, file)+" ") 10 # f.write(file+" ") 11 f.close()
其中,getcwd()就是获取当前目录,listdir()是获取目录下所有子目录(非递归),walk()是递归获取,返回一个list of tuple,tuple包含三个元素,分别是当前目录,list of当前目录下的文件夹,list of当前目录下的文件。fnmatch()就是一个匹配的函数;realpath()返回绝对路径,join()连接路径。不得不说,脚本语言还是很方便的,源代码可以当exe直接运行。
后来,我遇到了吊炸天的Boost,终于又可以用C++实现了。Boost的filesystem库实现了文件操作,而且它的可读性非常好,几乎不需要解读。
1 #include <iostream> 2 #include <fstream> 3 #include <string> 4 #include <boost/filesystem/operations.hpp> 5 using namespace std; 6 using namespace boost; 7 namespace fs = boost::filesystem; 8 using rd_iter = fs::recursive_directory_iterator; 9 using d_iter = fs::directory_iterator; 10 int main() 11 { 12 string ext; 13 cout<<"请输入文件格式(含.)"<<endl; 14 cin>>ext; 15 ofstream fout("output.txt"); 16 rd_iter pos("./"), End; 17 for(; pos != End; ++pos) 18 { 19 if(!is_directory(*pos) && 20 pos->path().extension() == ext) 21 { //仅文件 22 // fout << pos->path().filename().string() << endl; 23 //绝对路径 24 // fout << system_complete(pos->path()).string() << endl; 25 //相对路径 26 fout << pos->path().relative_path().string() << endl; 27 } 28 } 29 }
用"./"表示当前目录,多么简单直观。d_iter是非递归迭代器,rd_iter是递归迭代器,这里用迭代器来遍历路径。Boost把迭代器玩出魔性来了,好多用法第一次看的时候是跪着看的。更多的内容可以去看Chrono的书,这里就不说了。