I'm trying to retrieve all files from the C:/ directory using Boost library.
I can retrieve all files when the input is a file path with a directory (e.g : C:\Windows), but I get an error when the specified path is only C:\. I also tried with C: but Boost search file from my project directory and not from the root.
I have also added an exclusion to C:\Windows and this part works great.
So how to launch recursive_directory_iterator from C:\ ?
Here is my code :
//string rootPath = boost::filesystem::current_path().root_directory().string();
string rootPath = "C:";
string exclusionPath = rootPath+"\\"+"Windows";
void myClass::getFile()
{
for (boost::filesystem::recursive_directory_iterator end, dir(rootPath); dir != end; ++dir)
{
string filePath = dir->path().string();
if (boost::filesystem::is_regular_file(*dir) && filePath.find(exclusionPath) == string::npos)
{
cout << filePath << endl;
}
}
}
If you are using the c++17 standard library, you can take advantage of the standard filesystem library. It works just like the boost filesystem and the two libraries have a very similar API.
You have to include the filesystem header via
#include <filesystem>
You can recursively iterate through each file in a directory by calling:
for (std::filesystem::directory_entry entry : std::filesystem::recursive_directory_iterator(rootPath))
That will give you a directory entry, which, just like the boost directory entry, contains a path. I was able to replicate your example code with the standard library and got a working example like so:
#include <filesystem>
#include <string>
#include <iostream>
std::filesystem::path rootPath = "C:";
std::filesystem::path exclusionPath = rootPath / "Windows";
int main()
{
for (std::filesystem::directory_entry entry : std::filesystem::recursive_directory_iterator(rootPath))
{
std::string filePath = entry.path().string();
if (std::filesystem::is_regular_file(entry.path()) && filePath.find(exclusionPath.string()) == std::string::npos)
{
std::cout << filePath << std::endl;
}
}
}
As you can see, I converted the strings you use for paths above to paths. This is not necessary but it is better to construct a path up-front, because else every function you call will construct a new path with the string you put into it. Paths weirdly use the / operator to append two paths together, so on Windows
std::filesystem::path exclusionPath = rootPath / "Windows";
will give you C:\Windows.
Related
While using a simulator and opening a terminal within a node, I need to read a file from the parent directory that starts with the current directory name. The structure is the following:
/path/to/directory/session#/node.conf
|
|_node.xy
I can get the current path (as well as the parent path) /path/to/directory/session#/node.conf using the boost library.
std::string cwd = getcwd(NULL, 0);
boost::filesystem::path p1(cwd);
... p1.parent_path()
I'm not familiar with Boost, but I would like to get the folder name only node.conf, parse to get node, navigate to the parent directory and read from a file called node.xy.
What is the best way to do it? I looked for other questions here but couldn't find one that worked for me.
Thanks
The Boost Filesystem is rich with methods. There are many ways to do the same thing. Always keep the docs handy, you will need them.
Documentation Links
Reference Documentation
So:
#include <iostream>
#include <boost/filesystem.hpp>
int main( )
{
namespace bfs= boost::filesystem;
bfs::path p1( "/path/to/directory/session#/node.conf" );
bfs::path target( p1.parent_path( ) / "_node.xy" );
std::cout << target << std::endl;
//or
bfs::path targ2( p1 );
targ2.remove_filename( );
targ2/= "_node.xy";
std::cout << targ2 << std::endl;
return 0;
}
I'm trying to code a program where it opens and reads a file automatically. But the problem is the file is stored in a folder which name is unknown. I only know where the folder is located and the file's name. How to get to that file's path in char* ?
Edit: example: d:\files\<random folder>\data.txt
I don't know the name of random folder but I know that it exists in d:\files
Since this is tagged windows, you might as well use the Windows API functions:
FindFirstFile()
FindNextFile()
to enumerate and loop through all the files in a given directory.
To check for a directory, look at dwFileAttributes contained in the WIN32_FIND_DATA structure (filled by the calls to Find...File()). But make sure to skip . and .. directories. If needed, this can be done recursively.
You can check the links for some examples, or see Listing the Files in a Directory.
In case you are using MFC, you can use CFileFind (which is a wrapper around the API functions):
CFileFind finder;
BOOL bWorking = finder.FindFile(_T("*.*"));
while (bWorking)
{
bWorking = finder.FindNextFile();
TRACE(_T("%s\n"), (LPCTSTR)finder.GetFileName());
}
Just for fun, I implemented this using the new, experimental <filesystem> FS Technical Specification supported by GCC 5.
#include <iostream>
#include <experimental/filesystem>
// for readability
namespace fs = std::experimental::filesystem;
int main(int, char* argv[])
{
if(!argv[1])
{
std::cerr << "require 2 parameters, search directory and filename\n";
return EXIT_FAILURE;
}
fs::path search_dir = argv[1];
if(!fs::is_directory(search_dir))
{
std::cerr << "First parameter must be a directory: " << search_dir << '\n';
return EXIT_FAILURE;
}
if(!argv[2])
{
std::cerr << "Expected filename to search for\n";
return EXIT_FAILURE;
}
// file to search for
fs::path file_name = argv[2];
const fs::directory_iterator dir_end; // directory end sentinel
// used to iterate through each subdirectory of search_dir
fs::directory_iterator dir_iter(search_dir);
for(; dir_iter != dir_end; ++dir_iter)
{
// skip non directories
if(!fs::is_directory(dir_iter->path()))
continue;
// check directory for file
// iterate through files in this subdirectory dir_iter->path()
auto file_iter = fs::directory_iterator(dir_iter->path());
for(; file_iter != dir_end; ++file_iter)
{
// ignore directories and wrong filenames
if(fs::is_directory(file_iter->path())
|| file_iter->path().filename() != file_name)
continue;
// Ok we found it (the first one)
std::cout << "path: " << file_iter->path().string() << '\n';
return EXIT_SUCCESS;
}
}
// Not found
std::cout << file_name << " was not found in " << search_dir.string() << '\n';
return EXIT_FAILURE;
}
The idea is: list the directories under d:\files and try to open
the file in each directory.
There isn't (yet) a standard C++ way of getting all the existing files/directories. A crude but easy way of doing this would be
system("dir d:\\files /b /ad > tmpfile");
This lists all directories (/ad), redirected to a temporary file. Then open the file:
std::ifstream list("tmpfile");
And read it:
std::string dirname;
std::string filename;
while (std::getline(list, dirname))
{
filename = "d:\\files\\" + dirname + "\\data.txt";
if ( ... file exists ... )
break;
}
I call this method crude because it has problems that are hard/impossible to fix:
It overwrites a potentially useful file
It doesn't work if current directory is read-only
It will only work in Windows
It might be possible to use _popen and fgets instead of redirecting to file.
I want to make a method that read all files in directory and subdirectories of it and return a vector with the names of the files. So far I can't find something useful about that problem :/
here is an example of my method wchich is read the files in directory but not in subdrictories
vector open_directories(const string path_dir)
{
DIR *directory;
dirent *pdir;
vector<string> files_name;
dir = opendir(path_dir.c_str());
while((pdir = reddir(dir))) {
files_name.push_back(pdir->d_name);
}
return files_name;
}
there is any solution to open subdirectories also ?
If you have the <filesystem> header available on your environment, then you can use std::filesystem::recursive_directory_iterator for this
i.e.
#include <filesystem>
#include <vector>
std::vector<std::string> getFilenamesRecursive(const std::string& directory) {
std::vector<std::string> retVal;
std::tr2::sys::path path(directory);
for (auto it = std::tr2::sys::recursive_directory_iterator(path);
it != std::tr2::sys::recursive_directory_iterator(); ++it) {
retVal.push_back(it->path().generic_string());
}
return retVal;
}
I'm working under windows 7 with mingw. I have encountered some weird behaviour with unicode filenames. My program needs to be portable, and I'm using boost::filesystem (v 1.53) to handle the file paths.
This has all been going well, until I needed to open files with unicode filenames.
This is not about the content of the file, but the file's name.
I tried the following: For testing I made a folder named C:\UnicodeTest\вячеслав and I tried creating a file inside of it, by appending the file name test.txt to the boost wpath.
For some reason the creation of the file fails. I'm using boost's fstreams and when I try to open the file, the failbit of the stream is set.
Now the funny thing is, that when I append a foldername to the path instead, a call to create_directories() succeeds and creates the correct directory C:\UnicodeTest\вячеслав\folder.
I really don't understand why it won't work with a file. This is the code I use:
boost::filesystem::wpath path;
// find the folder to test
boost::filesystem::wpath dirPath = "C:\\UnicodeTest";
vector<boost::filesystem::wpath> files;
copy(boost::filesystem::directory_iterator(dirPath), boost::filesystem::directory_iterator(), back_inserter(files));
for(boost::filesystem::wpath &file : files)
{
if(boost::filesystem::is_directory(file))
{
path = file;
break;
}
}
// create a path for the folder
boost::filesystem::wpath folderPath = path / "folder";
// this works just fine
boost::filesystem::create_directories(folderPath);
// create a path for the file
boost::filesystem::wpath filePath = path / "test.txt";
boost::filesystem::ofstream stream;
// this fails
stream.open(filePath);
if(!stream)
{
cout << "failed to open file " << path << endl;
}
else
{
cout << "success" << endl;
}
If I understand the issue correctly, the issue of being unable to create a file directly within C:\UnicodeTest\вячеслав occurs when you do not create the folder directory, as illustrated below.
// create a path for the folder
//boost::filesystem::wpath folderPath = path / "folder";
// this works just fine
//boost::filesystem::create_directories(folderPath);
// create a path for the file
boost::filesystem::wpath filePath = path / "test.txt";
I was able to get this to work by making the filename a wchar_t string:
// create a path for the file
boost::filesystem::wpath filePath = path / L"test.txt";
i need a way to search the computer for files like Windows Explorer. i want my program to search lets say hard drive c:. i need it to search C:\ for folders and files (just the ones you could see in c:\ then if the user clicks on a file on the list like the folder test (C:\test) it would search test and let the user see what files/folders are in it.
Since you mentioned windows, the most straight forward winapi way to do it is with FindFirstFile and FindNextFile functions.
edit: Here's an example that shows you how to enumerate all files/folders in a directory.
#include <Windows.h>
#include <iostream>
int main()
{
WIN32_FIND_DATA file;
HANDLE search_handle=FindFirstFile(L"C:\\*",&file);
if (search_handle)
{
do
{
std::wcout << file.cFileName << std::endl;
}while(FindNextFile(search_handle,&file));
FindClose(search_handle);
}
}
This will be OS dependent. The SO question
How can I get a list of files in a directory using C or C++?
handles this problem well. You can download DIRENT here.
Now that you have this, I'd recommend recursively searching for a file with a DFS/BFS algorithm. You can assume the whole directory structure is a tree where each file is a leaf node and each subdirectory is an internal node.
So all you have to do is,
Get the list of files/folders in a directory with a function such as:
void getFilesFolders(vector<string> & dir_list, const string & folder_name)
If it's a directory, go to 1 with the directory name
If it's a file, terminate if it's the file you're looking for, else move on to the next file.
boost::filesystem can be a cross-platform solution for that (check out for such functions in it).
You can use Directory class members to do this with C# or managed C++. See the following MSDN article:
http://support.microsoft.com/kb/307009
If you wish to use C++ with MFC you can use CFileFind
http://msdn.microsoft.com/en-us/library/f33e1618%28v=VS.80%29.aspx
You'll have to supply your own browse window to present the file system tree.
Or you can use one of the directory/file controls to do both for you.
#include <Windows.h>
#include <iostream>
int FindF(char* pDirectory)
{
char szFindPath[MAX_PATH] = {0};
strcpy(szFindPath, pDirectory);
strcat(szFindPath, "\\*");
WIN32_FIND_DATA file;
HANDLE search_handle=FindFirstFile(szFindPath,&file);
if (search_handle)
{
do
{
if(file.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
{
strcpy(szFindPath, pDirectory);
strcat(szFindPath, "\\");
strcat(szFindPath, file.cFileName);
FindF(szFindPath);
}
std::wcout << file.cFileName << std::endl;
}while(FindNextFile(search_handle,&file));
CloseHandle(search_handle);
}
}
There really is no need to use 3rd party library to accomplish this. This is a short, independent function which lists all files (with their paths) in a directory, including subdiretories' files. std::string folderName has to finish with \, and if you want to list all files on computer, just create a loop in calling function along with GetLogicalDriveStrings (It returns strings with \, so it couldn't be more convenient in this case).
void FindAllFiles(std::string folderName)
{
WIN32_FIND_DATA FileData;
std::string folderNameWithSt = folderName + "*";
HANDLE FirstFile = FindFirstFile(folderNameWithSt.c_str(), &FileData);
if (FirstFile != INVALID_HANDLE_VALUE) {
do {
if (strcmp(FileData.cFileName, ".") != 0 && strcmp(FileData.cFileName, "..") != 0)
{
if(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
std::string NewPath = folderName + FileData.cFileName;
NewPath = NewPath + "\\";
FindAllFiles(NewPath);
}
else
{
std::cout /*<< folderName*/ << FileData.cFileName << std::endl;
}
}
} while(FindNextFile(FirstFile, &FileData));
}
}
This is ASCII version, remember that files and folders can be named in Unicode