Save all file names in a directory to a vector - c++

I need to save all ".xml" file names in a directory to a vector. To make a long story short, I cannot use the dirent API. It seems as if C++ does not have any concept of "directories".
Once I have the filenames in a vector, I can iterate through and "fopen" these files.
Is there an easy way to get these filenames at runtime?

Easy way is to use Boost.Filesystem library.
namespace fs = boost::filesystem;
// ...
std::string path_to_xml = CUSTOM_DIR_PATH;
std::vector<string> xml_files;
fs::directory_iterator dir_iter( static_cast<fs::path>(path_to_xml) ), dir_end;
for (; dir_iter != dir_end; ++dir_iter ) {
if ( boost::iends_with( boost::to_lower_copy( dir_iter->filename() ), ".xml" ) )
xml_files.push_back( dir_iter->filename() );
}

I suggest having a look at boost::filesystem if it should be portable and bringing boost in isn't too heavy.

If you don't like boost, try Poco. It has a DirectoryIterator. http://pocoproject.org/

Something like this (Note, Format is a sprintf:ish funciton you can replace)
bool MakeFileList(const wchar_t* pDirectory,vector<wstring> *pFileList)
{
wstring sTemp = Format(L"%s\\*.%s",pDirectory,L"xml");
_wfinddata_t first_file;
long hFile = _wfindfirst(sTemp.c_str(),&first_file);
if(hFile != -1)
{
wstring sFile = first_file.name;
wstring sPath = Format(L"%s%s",pDirectory,sFile.c_str());
pFileList->push_back(sPath);
while(_wfindnext(hFile,&first_file) != -1)
{
wstring sFile = first_file.name;
wstring sPath = Format(L"%s%s",pDirectory,sFile.c_str());
pFileList->push_back(sPath);
}
_findclose(hFile);
}else
return false;
return true;
}

Related

Get string of temp file and add custom folder

I'm new to C++ and I have been thinking how to get string of the user's temp folder on Windows and append to it a custom folder name.
e.g "\Users\user\AppData\Local\Temp\NameOfCustomFolder"
I've tried this:
std::string szOutput{};
TCHAR path_buf[MAX_PATH];
DWORD ret_val = GetTempPath(MAX_PATH, path_buf);
if (ret_val > MAX_PATH || (ret_val == 0))
{
std::cout << "GetTempPath failed";
}
else
{
szOutput = path_buf, "NameOfCustomFolder\\file.exe"
}
return szOutput;
This does not perform the string concatenation you want
szOutput = path_buf, "NameOfCustomFolder\\file.exe";
Rather do
szOutput = std::string{path_buf} + "NameOfCustomFolder\\file.exe";
If you can use C++17, I would suggest std::filesystem::path::append
It will take care of the path separators, and will make it easy to go cross-platform.

Copy files out of different folders to a specific on in C++

My problem is that i have several folders with the names "MORE0001" "MORE0002" etc and they contain one .SPE-file each.
I want to know if there is a way to extract all the .SPE-files to ONE folder by iterating through all the single-MORE...-folders.
I need sth. like this:
for (int i=0; i<10;i++){
newfile = getfile("directory/MORE%04d/filename.SPE", i);
// copy newfile to a new directory..
}
I hope you guys can help me find an easy solution, because i didn´t find a similar problem yet.
it´s just TOO easy..
i can just use the rename-function..
so it would be like:
rename(path/filename.SPE, newpath/filename.SPE);
thanks, but solved it myself ;)!
I have created one sample program, which might helps to resolve your problem.
#include<Windows.h>
#include<regex>
using namespace std;
void main()
{
regex e1("MORE\\d+");
string szDir = "C:\\*";
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(szDir.c_str(), &ffd);
do
{
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (regex_match(ffd.cFileName,e1 ))
{
string s1 = ffd.cFileName;
string s2 = "C:\\" + s1 + "\\*";
WIN32_FIND_DATA ffdMORE;
HANDLE hFindMORE = FindFirstFile(s2.c_str(), &ffdMORE);
do
{
regex e2("\\w+.SPE");
if (regex_match(ffdMORE.cFileName,e2))
{
string commondir = "C:\\CommonDir\\";
string sourcePath = "C:\\" + s1 + "\\";
CopyFile(sourcePath.append(ffdMORE.cFileName).c_str(), commondir.append(ffdMORE.cFileName).c_str(), FALSE);
}
} while (FindNextFile(hFindMORE, & ffdMORE) != 0);
}
}
} while (FindNextFile(hFind, &ffd) != 0);
}
Thanks,
Bharathraj

How to get the stem of a filename from a path?

I want to extract a const char* filename from a const char* filepath. I tried with regex but failed:
const char* currentLoadedFile = "D:\files\file.lua";
char fileName[256];
if (sscanf(currentLoadedFile, "%*[^\\]\\%[^.].lua", fileName)) {
return (const char*)fileName; // WILL RETURN "D:\files\file!!
}
The issue is that "D:\files\file" will be returned and not the wanted "file"(note: without ".lua")
What about using std::string?
e.g.
std::string path("d:\\dir\\subdir\\file.ext");
std::string filename;
size_t pos = path.find_last_of("\\");
if(pos != std::string::npos)
filename.assign(path.begin() + pos + 1, path.end());
else
filename = path;
Just use boost::filesystem.
#include <boost/filesystem.hpp>
std::string filename_noext;
filename_noext = boost::filesystem::path("D:\\files\\file.lua").stem().string().
const char* result_as_const_char = filename_noext.c_str();
or alternatively, if you want to introduce bugs yourself :
// have fun defining that to the separator of the target OS.
#define PLATFORM_DIRECTORY_SEPARATOR '\\'
// the following code is guaranteed to have bugs.
std::string input = "D:\\files\\file.lua";
std::string::size_type filename_begin = input.find_last_of(PLATFORM_DIRECTORY_SEPERATOR);
if (filename_begin == std::string::npos)
filename_begin = 0;
else
filename_begin++;
std::string::size_type filename_length = input.find_last_of('.');
if (filename_length != std::string::npos)
filename_length = filename_length - filename_begin;
std::string result = input.substr(filename_begin, filename_length);
const char* bugy_result_as_const_char = result.c_str();
You can do this portably and easily using the new filesystem library in C++17.
#include <cstdint>
#include <cstdio>
#include <filesystem>
int main()
{
std::filesystem::path my_path("D:/files/file.lua");
std::printf("filename: %s\n", my_path.filename().u8string().c_str());
std::printf("stem: %s\n", my_path.stem().u8string().c_str());
std::printf("extension: %s\n", my_path.extension().u8string().c_str());
}
Output:
filename: file.lua
stem: file
extension: .lua
Do note that for the time being you may need to use #include <experimental/fileystem> along with std::experimental::filesystem instead until standard libraries are fully conforming.
For more documentation on std::filesystem check out the filesystem library reference.
You can easily extract the file:
int main()
{
char pscL_Dir[]="/home/srfuser/kush/folder/kushvendra.txt";
char pscL_FileName[50];
char pscL_FilePath[100];
char *pscL;
pscL=strrchr(pscL_Dir,'/');
if(pscL==NULL)
printf("\n ERROR :INvalid DIr");
else
{
strncpy(pscL_FilePath,pscL_Dir,(pscL-pscL_Dir));
strcpy(pscL_FileName,pscL+1);
printf("LENTH [%d}\n pscL_FilePath[%s]\n pscL_FileName[%s]",(pscL-pscL_Dir),pscL_FilePath,pscL_FileName);
}
return 0;
}
output:
LENTH [25}
pscL_FilePath[/home/srfuser/kush/folder]
pscL_FileName[kushvendra.txt
Here you can find an example. I'm not saying it's the best and I'm sure you could improve on that but it uses only standard C++ (anyway at least what's now considered standard).
Of course you won't have the features of the boost::filesystem (those functions in the example play along with plain strings and do not guarantee/check you'll actually working with a real filesystem path).
// Set short name:
char *Filename;
Filename = strrchr(svFilename, '\\');
if ( Filename == NULL )
Filename = svFilename;
if ( Filename[0] == '\\')
++Filename;
if ( !lstrlen(Filename) )
{
Filename = svFilename;
}
fprintf( m_FileOutput, ";\n; %s\n;\n", Filename );
You could use the _splitpath_s function to break a path name into its components. I don't know if this is standard C or is Windows specific. Anyway this is the function:
#include <stdlib.h>
#include <string>
using std::string;
bool splitPath(string const &path, string &drive, string &directory, string &filename, string &extension) {
// validate path
drive.resize(_MAX_DRIVE);
directory.resize(_MAX_DIR);
filename.resize(_MAX_FNAME);
extension.resize(_MAX_EXT);
errno_t result;
result = _splitpath_s(path.c_str(), &drive[0], drive.size(), &directory[0], directory.size(), &filename[0], filename.size(), &extension[0], extension.size());
//_splitpath(path.c_str(), &drive[0], &directory[0], &filename[0], &extension[0]); //WindowsXp compatibility
_get_errno(&result);
if (result != 0) {
return false;
} else {
//delete the blank spaces at the end
drive = drive.c_str();
directory = directory.c_str();
filename = filename.c_str();
extension = extension.c_str();
return true;
}
}
It is a lot easier and safe to use std::string but you could modify this to use TCHAR* (wchar, char)...
For your specific case:
int main(int argc, char *argv[]) {
string path = argv[0];
string drive, directory, filename, extension;
splitPath(path, drive, directory, filename, extension);
printf("FILE = %s%s", filename.c_str(), extension.c_str());
return 0;
}
If you are going to display a filename to the user on Windows you should respect their shell settings (show/hide extension etc).
You can get a filename in the correct format by calling SHGetFileInfo with the SHGFI_DISPLAYNAME flag.

C++ equivalent of MATLAB's "fileparts" function

In MATLAB there's a nice function called fileparts that takes a full file path and parses it into path, filename (without extension), and extension as in the following example from the documentation:
file = 'H:\user4\matlab\classpath.txt';
[pathstr, name, ext] = fileparts(file)
>> pathstr = H:\user4\matlab
>> name = classpath
>> ext = .txt
So I was wondering if there's an equivalent function in any standard C++ or C libraries that I could use? Or would I have to implement this myself? I realize it's fairly simple, but I figured if there's already something pre-made that would be preferable.
Thanks.
The boost library has a file system component "basic_path" that allows you use iterators to discover each component in the filename. Such a component would be OS specific, and I believe you need to compile boost separately for Windows, Linux etc.
I just wrote this simple function. It behaves similar as Matlab's fileparts and works independent of platform.
struct FileParts
{
string path;
string name;
string ext;
};
FileParts fileparts(string filename)
{
int idx0 = filename.rfind("/");
int idx1 = filename.rfind(".");
FileParts fp;
fp.path = filename.substr(0,idx0+1);
fp.name = filename.substr(idx0+1,idx1-idx0-1);
fp.ext = filename.substr(idx1);
return fp;
}
A platform-independent way with C++11/14.
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
void fileparts(string full, string& fpath, string& fname, string& fext)
{
auto source = fs::path(full);
fpath = source.parent_path().string();
fname = source.stem().string();
fext = source.extension().string();
}
...
string fpath, fname, fext;
fileparts(full_file_path,fpath,fname,fext);
Some possible solutions, depending on your OS:
Visual C++ _splitpath function
Win32 Shell Path Handling Functions such as PathFindExtension, PathFindFileName, PathStripPath, PathRemoveExtension, PathRemoveFileSpec
Ekalic's text-only approach is useful, but it didn't check for errors. Here's one that does, and also works with both / and \
struct FileParts
{
std::string path; //!< containing folder, if provided, including trailing slash
std::string name; //!< base file name, without extension
std::string ext; //!< extension, including '.'
};
//! Using only text manipulation, splits a full path into component file parts
FileParts fileparts(const std::string &fullpath)
{
using namespace std;
size_t idxSlash = fullpath.rfind("/");
if (idxSlash == string::npos) {
idxSlash = fullpath.rfind("\\");
}
size_t idxDot = fullpath.rfind(".");
FileParts fp;
if (idxSlash != string::npos && idxDot != string::npos) {
fp.path = fullpath.substr(0, idxSlash + 1);
fp.name = fullpath.substr(idxSlash + 1, idxDot - idxSlash - 1);
fp.ext = fullpath.substr(idxDot);
} else if (idxSlash == string::npos && idxDot == string::npos) {
fp.name = fullpath;
} else if (/* only */ idxSlash == string::npos) {
fp.name = fullpath.substr(0, idxDot);
fp.ext = fullpath.substr(idxDot);
} else { // only idxDot == string::npos
fp.path = fullpath.substr(0, idxSlash + 1);
fp.name = fullpath.substr(idxSlash + 1);
}
return fp;
}

Recursive file search using C++ MFC?

What is the cleanest way to recursively search for files using C++ and MFC?
EDIT: Do any of these solutions offer the ability to use multiple filters through one pass? I guess with CFileFind I could filter on *.* and then write custom code to further filter into different file types. Does anything offer built-in multiple filters (ie. *.exe,*.dll)?
EDIT2: Just realized an obvious assumption that I was making that makes my previous EDIT invalid. If I am trying to do a recursive search with CFileFind, I have to use *.* as my wildcard because otherwise subdirectories won't be matched and no recursion will take place. So filtering on different file-extentions will have to be handled separately regardless.
Using CFileFind.
Take a look at this example from MSDN:
void Recurse(LPCTSTR pstr)
{
CFileFind finder;
// build a string with wildcards
CString strWildcard(pstr);
strWildcard += _T("\\*.*");
// start working for files
BOOL bWorking = finder.FindFile(strWildcard);
while (bWorking)
{
bWorking = finder.FindNextFile();
// skip . and .. files; otherwise, we'd
// recur infinitely!
if (finder.IsDots())
continue;
// if it's a directory, recursively search it
if (finder.IsDirectory())
{
CString str = finder.GetFilePath();
cout << (LPCTSTR) str << endl;
Recurse(str);
}
}
finder.Close();
}
Use Boost's Filesystem implementation!
The recursive example is even on the filesystem homepage:
bool find_file( const path & dir_path, // in this directory,
const std::string & file_name, // search for this name,
path & path_found ) // placing path here if found
{
if ( !exists( dir_path ) ) return false;
directory_iterator end_itr; // default construction yields past-the-end
for ( directory_iterator itr( dir_path );
itr != end_itr;
++itr )
{
if ( is_directory(itr->status()) )
{
if ( find_file( itr->path(), file_name, path_found ) ) return true;
}
else if ( itr->leaf() == file_name ) // see below
{
path_found = itr->path();
return true;
}
}
return false;
}
I know it is not your question, but it is also easy to to without recursion by using a CStringArray
void FindFiles(CString srcFolder)
{
CStringArray dirs;
dirs.Add(srcFolder + "\\*.*");
while(dirs.GetSize() > 0) {
CString dir = dirs.GetAt(0);
dirs.RemoveAt(0);
CFileFind ff;
BOOL good = ff.FindFile(dir);
while(good) {
good = ff.FindNextFile();
if(!ff.IsDots()) {
if(!ff.IsDirectory()) {
//process file
} else {
//new directory (and not . or ..)
dirs.InsertAt(0,nd + "\\*.*");
}
}
}
ff.Close();
}
}
Check out the recls library - stands for recursive ls - which is a recursive search library that works on UNIX and Windows. It's a C library with adaptations to different language, including C++. From memory, you can use it something like the following:
using recls::search_sequence;
CString dir = "C:\\mydir";
CString patterns = "*.doc;abc*.xls";
CStringArray paths;
search_sequence files(dir, patterns, recls::RECURSIVE);
for(search_sequence::const_iterator b = files.begin(); b != files.end(); b++) {
paths.Add((*b).c_str());
}
It'll find all .doc files, and all .xls files beginning with abc in C:\mydir or any of its subdirectories.
I haven't compiled this, but it should be pretty close to the mark.
CString strNextFileName , strSaveLog= "C:\\mydir";
Find.FindFile(strSaveLog);
BOOL l = Find.FindNextFile();
if(!l)
MessageBox("");
strNextFileName = Find.GetFileName();
Its not working. Find.FindNextFile() returning false even the files are present in the same directory``