Extracting the immediate parent directory from a file path using boost - c++

Assuming I have
String t = "c:/foo/foo1/foo2/foo3/file.txt"
I want to extract "foo3/file.txt".
How can I do this (using boost or std)?
Here is what I've been trying to so far:
boost::filesystem::path pathToObject(t);
Using pathToObject.filename() I can extract the file name of course. And I've played around with t.find_last_of("/") but I really need like t.find_second_to_last_of("/").

string::find_last_of has an optional argument which lets you specify how far into the string you are looking.
So you can define
size_t second_to_last = t.find_last_of("/", t.find_last_of("/")-1);
std::string file_with_parent = t.substr(second_to_last+1);
The second argument tells him to only search before the last /.
WARNING: This might differ from what you want if you have stuff like "C:/bli/bla//blubb.txt". In general, paths can be complex and confusing and trying to conquer them with string manipulation will only work for very well-behaved input, which one usually can't assume.
I therefore recommended using a proper tool for this job.* But since the question claimed that find_last_of wouldn't do the job I felt quite compelled to remind people that the standard facilities are not entirely as impotent as many seem to believe them to be.
*I suspect the boost path lib to be one but I have never worked with it.

It is rather odd to extract a path like that. Maybe you are looking for a relative path? boost filesystem has a tool for that. Be sure to give the documentation a good look over. But to answer your question:
namespace bfs= boost::filesystem;
int main( ) {
bfs::path path( "c:/foo/foo1/foo2/foo3/file.txt" );
bfs::path target( path );
target.remove_filename( );
target= target.filename( ) / path.filename( );
std::cout << target << std::endl;
}

I don't have a compiler handy to test it, but based on the example here, this code should basically work or point you in about the right direction. It could probably be simplified a little bit even from what I've written here.
path p1( "c:/foo/foo1/foo2/foo3/file.txt" );
path p2;
for (path::iterator it(p1.end()), int i = 0; it != p1.begin() && i < 2; --it, ++i) {
path temp = p2;
p2 = it;
p2 /= temp;
}

Here is the solution I ended up using:
std::string t = pathObject.parent_path().filename().string();
t.append("/");
t.append(pathObject.filename().string());
Using parent_path gave me just the path. Then I used the filename to extract just the directory. Then I appended the filename of the child directory.

Following method return immediate parent directory.
#include <string>
string getParentDirectory(string& filePath)
{
if (filePath.empty() == false)
{
size_t toPos = filePath.find_last_of('\\') - 1;
if (toPos != string::npos)
{
size_t fromPos = filePath.find_last_of('\\', toPos);
if (fromPos != string::npos)
{
return filePath.substr(fromPos + 1, toPos - fromPos);
}
}
}
return "";
}
int main()
{
string str = "D:\\Devs\\Test\\sprite.png";
string parentDir = getParentDirectory(str);
return 0;
}
It prints the value of parentDir is "Test".

Related

Searching for files in a directory by name using Visual Studio C++

I'm trying to create a program where I can search for some files in a directory on my PC, using Visual Studio C++.
As I'm not very experienced with that, I found this code (below) in another answer but couldn't find any explanation to the code.
I'm having a hard time figuring it out and would strongly appreciate any help possible.
If there's another way of doing this I would be pleased to know how.
Thank you!
"
Now you can get file names. Just compare a file name.
while ((dirp = readdir(dp)) != NULL) {
std::string fname = dirp->d_name;
if(fname.find("abc") != std::string::npos)
files.push_back(fname);
}
Also you can use scandir function which can register filter function.
static int filter(const struct dirent* dir_ent)
{
if (!strcmp(dir_ent->d_name, ".") || !strcmp(dir_ent->d_name, ".."))
return 0;
std::string fname = dir_ent->d_name;
if (fname.find("abc") == std::string::npos) return 0;
return 1;
}
int main()
{
struct dirent **namelist;
std::vector<std::string> v;
std::vector<std::string>::iterator it;
n = scandir( dir_path , &namelist, *filter, alphasort );
for (int i=0; i<n; i++) {
std::string fname = namelist[i]->d_name;
v.push_back(fname);
free(namelist[i]);
}
free(namelist);
return 0;
}
"
A better way of doing this would probably be using the new std::filesystem library. directory_iterators allow you to go through the contents of a directory. Since they are just iterators, you can combine them with standard algorithms like std::find_if to search for a particular entry:
#include <filesystem>
#include <algorithm>
namespace fs = std::filesystem;
void search(const fs::path& directory, const fs::path& file_name)
{
auto d = fs::directory_iterator(directory);
auto found = std::find_if(d, end(d), [&file_name](const auto& dir_entry)
{
return dir_entry.path().filename() == file_name;
});
if (found != end(d))
{
// we have found what we were looking for
}
// ...
}
We first create a directory_iterator d for the directory in which we want to search. We then use std::find_if() to go through the contents of the directory and search for an entry that matches the filename we are looking for. std::find_if() expects a function object as last argument that is applied to every visited element and returns true if the element matches what we are looking for. std::find_if() returns the iterator to the first element for which this predicate function returns true, otherwise it returns the end iterator. Here, we use a lambda as predicate that returns true when the filename component of the path of the directory entry we're looking at matches the wanted filename. Afterwards, we compare the iterator returned by std::find_if() to the end iterator to see if we have found an entry or not. In case we did find an entry, *found will evaluate to a directory_entry representing the respective file system object.
Note that this will require a recent version of Visual Studio 2017. Don't forget to set the language standard to /std:c++17 or /std:c++latest in the project properties (C++/Language).
Both methods use the find function of a std::string:
fname.find("abc")
This looks for "abc" in the fname string. If it's found it returns the index it starts at, otherwise it retruns std::string::npos, so they both check for that substring.
You may want to see if you have an exact match, using == instead. It depends.
If an appropriate filename is found, it's pushed back into a vector.
Your main function has
std::vector<std::string>::iterator it;
which it doesn't use.
I suspect that came over with some copy/paste.
You can use a range based for loop to see what's in your vector:
for(const std::string & name : v)
{
std::cout << name << '\n';
}
The filter function also checks against "." and ".." since these have special meanings - current dir and up one dir.
At that point, th C API has returned a char *, so they use strcmp, rather than std::string methods.
Edit:
n = scandir( dir_path , &namelist, *filter, alphasort );
uses n which you haven't declared.
Try
int n = scandir( dir_path , &namelist, *filter, alphasort );
Also, that uses dir_path which needs declaring somewhere.
For a quick fix, try
const char * dir_path = "C:\\";
(or whatever path you want, watching out for escaping backslashes with an extra backslash.
You probably want to pass this in as an arg to main.

get directory from file path c++

What is the simplest way to get the directory that a file is in? I'm using this to find the working directory.
string filename = "C:\MyDirectory\MyFile.bat"
In this example, I should get "C:\MyDirectory".
The initialisation is incorrect as you need to escape the backslashes:
string filename = "C:\\MyDirectory\\MyFile.bat";
To extract the directory if present:
string directory;
const size_t last_slash_idx = filename.rfind('\\');
if (std::string::npos != last_slash_idx)
{
directory = filename.substr(0, last_slash_idx);
}
The quick and dirty:
Note that you must also look for / because it is allowed alternative path separator on Windows
#include <string>
#include <iostream>
std::string dirnameOf(const std::string& fname)
{
size_t pos = fname.find_last_of("\\/");
return (std::string::npos == pos)
? ""
: fname.substr(0, pos);
}
int main(int argc, const char *argv[])
{
const std::string fname = "C:\\MyDirectory\\MyFile.bat";
std::cout << dirnameOf(fname) << std::endl;
}
Use the Boost.filesystem parent_path() function.
Ex. argument c:/foo/bar => c:/foo
More examples here : path decomposition table and tutorial here.
C++17 provides std::filesystem::path. It may be available in C++11 in ; link with -lstdc++fs. Note the function does not validate the path exists; use std::filesystem::status to determine type of file (which may be filetype::notfound)
The MFC way;
#include <afx.h>
CString GetContainingFolder(CString &file)
{
CFileFind fileFind;
fileFind.FindFile(file);
fileFind.FindNextFile();
return fileFind.GetRoot();
}
or, even simpler
CString path(L"C:\\my\\path\\document.txt");
path.Truncate(path.ReverseFind('\\'));
As Question is old but I would like to add an answer so that it will helpful for others.
in Visual c++ you can use CString or char array also
CString filename = _T("C:\\MyDirectory\\MyFile.bat");
PathRemoveFileSpec(filename);
OUTPUT:
C:\MyDirectory
Include Shlwapi.h in your header files.
MSDN LINK here you can check example.
A very simple cross-platform solution (as adapted from this example for string::find_last_of):
std::string GetDirectory (const std::string& path)
{
size_t found = path.find_last_of("/\\");
return(path.substr(0, found));
}
This works for both cases where the slashes can be either backward or forward pointing (or mixed), since it merely looks for the last occurrence of either in the string path.
However, my personal preference is using the Boost::Filesystem libraries to handle operations like this. An example:
std::string GetDirectory (const std::string& path)
{
boost::filesystem::path p(path);
return(p.parent_path().string());
}
Although, if getting the directory path from a string is the only functionality you need, then Boost might be a bit overkill (especially since Boost::Filesystem is one of the few Boost libraries that aren't header-only). However, AFIK, Boost::Filesystem had been approved to be included into the TR2 standard, but might not be fully available until the C++14 or C++17 standard (likely the latter, based on this answer), so depending on your compiler (and when you're reading this), you may not even need to compile these separately anymore since they might be included with your system already. For example, Visual Studio 2012 can already use some of the TR2 filesystem components (according to this post), though I haven't tried it since I'm still using Visual Studio 2010...
You can use the _spliltpath function available in stdlib.h header. Please refer to this link for the same.
http://msdn.microsoft.com/en-us/library/aa273364%28v=VS.60%29.aspx
Since C++17 you can use std::filesystem::parent_path:
#include <filesystem>
#include <iostream>
int main() {
std::string filename = "C:\\MyDirectory\\MyFile.bat";
std::string directory = std::filesystem::path(filename).parent_path().u8string();
std::cout << directory << std::endl;
}
This is proper winapi solution:
CString csFolder = _T("c:\temp\file.ext");
PathRemoveFileSpec(csFolder.GetBuffer(0));
csFolder.ReleaseBuffer(-1);
If you have access to Qt, you can also do it like this:
std::string getDirectory(const std::string & file_path)
{
return QFileInfo(QString(file_path)).absolutePath().toStdString();
}
The way of Beetle)
#include<tchar.h>
int GetDir(TCHAR *fullPath, TCHAR *dir) {
const int buffSize = 1024;
TCHAR buff[buffSize] = {0};
int buffCounter = 0;
int dirSymbolCounter = 0;
for (int i = 0; i < _tcslen(fullPath); i++) {
if (fullPath[i] != L'\\') {
if (buffCounter < buffSize) buff[buffCounter++] = fullPath[i];
else return -1;
} else {
for (int i2 = 0; i2 < buffCounter; i2++) {
dir[dirSymbolCounter++] = buff[i2];
buff[i2] = 0;
}
dir[dirSymbolCounter++] = fullPath[i];
buffCounter = 0;
}
}
return dirSymbolCounter;
}
Using :
TCHAR *path = L"C:\\Windows\\System32\\cmd.exe";
TCHAR dir[1024] = {0};
GetDir(path, dir);
wprintf(L"%s\n%s\n", path, dir);
You can simply search the last "\" and then cut the string:
string filePath = "C:\MyDirectory\MyFile.bat"
size_t slash = filePath.find_last_of("\");
string dirPath = (slash != std::string::npos) ? filePath.substr(0, slash) : filePath;
make sure in Linux to search "/" instead of "\":
size_t slash = filePath.find_last_of("/");

is this how i check a file extension in a specifc text file?

Hello I'm trying to implement a way too check a file extension in a specific text file the code below doesn't seem to do what I want but I was wondering if this is somewhat the right direction to take in doing this. if not what library are there that would allow me to do this in as less code as possible.
string fn = ".txt";
if(fn.substr(fn.find_last_of(".") + 1) == ".txt") {
fprintf(stderr,"yes");
} else {
fprintf(stderr,"no");
}
im on windows 7 32bit
My preferred solution would be boost::filesystem, as minus suggests,
but if not:
static std::string const targetExtension( ".txt" );
if ( filename.size() >= targetExtension.size()
&& std::equal( filename.end() - targetExtension.size(),
filename.end(),
targetExtension.begin() ) ) {
std::cerr << "yes";
} else {
std::cerr << "no";
}
This would seem the simplest to me.
fn.substr(fn.find_last_of(".") + 1) returns "txt" without the dot. So either remove +1 from substr or compare with "txt" without the dot.
If you only need this to work on Windows, the best solution is to use the Win32 API PathMatchSpec.
For your example:
string fn = ".txt";
if(PathMatchSpecA(fn.c_str(), "*.txt") {
fprintf(stderr,"yes");
} else {
fprintf(stderr,"no");
}
Or as a function:
BOOL HasTxtFileExtension(LPCSTR filename)
{
return PathMatchSpecA(filename, "*.txt");
}
As would be expected, this function is case-insensitive and correctly handles the case where the directory contains dots but the file does not, e.g. C:\\directory.with.dots\\testtxt.
I think boost filesystem library should help you (extension path function).
http://www.boost.org/doc/libs/release/libs/filesystem/index.html

Help me translate Python code which replaces an extension in file name to C++

I apologize if you know nothing about Python, however, the following snippet should be very readable to anyone. The only trick to watch out for - indexing a list with [-1] gives you the last element if there is one, or raises an exception.
>>> fileName = 'TheFileName.Something.xMl'
>>> fileNameList = fileName.split('.')
>>> assert(len(fileNameList) > 1) # Must have at least one period in it
>>> assert(fileNameList[-1].lower() == 'xml')
>>> fileNameList[-1] = 'bak'
>>> fileName = '.'.join(fileNameList)
>>> print(fileName)
TheFileName.Something.bak
I need to convert this logic into C++ (the language I am actually using, but so far suck at) function with the following signature: void PopulateBackupFileNameOrDie(CAtlString& strBackupFileName, CAtlString& strXmlFileName);. Here strXmlFileName is "input", strBackupFileName is "output" (should I reverse the oprder of the two?). The tricky part is that (correct me if I am wrong) I am working with a Unicode string, so looking for these characters: .xmlXML is not as straight-forward. Latest Python does not have these issues because '.' and "." are both Unicode strings (not a "char" type) of length 1, both contain just a dot.
Notice that the return type is void - do not worry much about it. I do not want to bore you with details of how we communicate an error back to the user. In my Python example I just used an assert. You can do something like that or just include a comment such as // ERROR: [REASON].
Please ask if something is not clear. Suggestions to use std::string, etc. instead of CAtlString for function parameters are not what I am looking for. You may convert them inside the function if you have to, but I would prefer not mixing different string types in one function. I am compiling this C++ on Windows, using VS2010. This implies that I WILL NOT install BOOST, QTString or other libraries which are not available out of the box. Stealing a boost or other header to enable some magic is also not the right solution.
Thanks.
If you're using ATL why not just use CAtlString's methods?
CAtlString filename = _T("TheFileName.Something.xMl");
//search for '.' from the end
int dotIdx = filename.ReverseFind( _T('.') );
if( dotIdx != -1 ) {
//extract the file extension
CAtlString ext = filename.Right( filename.GetLength() - dotIdx );
if( ext.CompareNoCase( _T(".xml" ) ) == 0 ) {
filename.Delete( dotIdx, ext.GetLength() ); //remove extension
filename += _T(".bak");
}
}
I didn't split the string as your code does because that's a bit more work in C++ for really no gain (it's slower, and for this task you really don't need to do it).
string filename = "TheFileName.Something.xMl";
size_t pos = filename.rfind('.');
assert(pos > 0 && pos == filename.length()-4); // the -4 here is for length of ".xml"
for(size_t i = pos+1; i < filename.length(); ++i)
filename[i] = tolower(filename[i]);
assert(filename.substr(pos+1) == "xml");
filename = filename.substr(0,pos+1) + "bak";
std::cout << filename << std::endl;

Reading in image files without specifying name

Are there any facilities in SDL or C++ that allow you to read image files in from a folder without specifying their name, like reading them in sequential order, etc.? If not are there any techniques you use to accomplish something along the same lines?
Doing something like this:
foo_ani[0] = LoadImage("Animations/foo1.png");
foo_ani[1] = LoadImage("Animations/foo2.png");
foo_ani[2] = LoadImage("Animations/foo3.png");
can become quite tedious, and a loop can't be used because the file name is specific each time.
The only way I could really think of is maybe having a string that you modify through each loop iterator and insert the loop number into the specific part of the string assuming that's how your files are labeled, and using that string as the LoadImage parameter. That seems like more work though than just doing the above.
Use boost::filesystem.
The tiny program shown here lists all files in the directory files/, matching the pattern fileN.type, where N is from 0 and upwards, unspecified.
#include <iostream>
#include <sstream>
#include <string>
#include <boost/filesystem.hpp>
using namespace std;
namespace fs = boost::filesystem;
int main(int argc, char** argv)
{
fs::path dir ("./files");
string prefix = "file";
string suffix = "type";
int i = 0;
fs::path file;
do {
stringstream ss;
ss << prefix << i++ << "." << suffix;
file = fs::path(dir / fs::path(ss.str()));
if(fs::exists(file)) {
cout << file.leaf() << " exists." << endl;
}
} while(fs::exists(file));
return 0;
}
Link with -lboost_filesystem.
boost::filesystem also provides a simple directory iterator.
For this type of situation, you would typically get a list of the filenames in the directory (with opendir/readdir or FindFirstFile/FindNextFile as appropriate), and loop on each filename in the directory. Given each filename, you can call LoadImage() and append the result to your array.
This technique doesn't require that you know the filenames ahead of time.
How about loading all files in that directory automatically?
foo_ani = LoadImages("Animations/");
Just traverse the directory given and load all files inside that fit.
Another solution, if you have several animations with different prefix is to use regular expressions. I suggest you use boost for that or std::tr1::regex, like this:
foo_ani = LoadImageSet("Animations/", std::tr1::regex("foo*.png"));
Given that you are are currently hard coding the name of the frames, I'm going to assume you know / have control over the naming scheme of the files. I'm also assuming you want them sequentially since it seems to be frames in an animation. Finally I'm assuming you know how many frames there are since you seem to have an array big enough to accommodate them all ready and waiting.
Given the names of the files presented in the question, you can't just do FindFirst / FindNext because once you get past 10 frames, they're almost certainly going to come in out of order (given the naming scheme presented).
So I think that you're right that the best way to do it is in a loop, but wrong that it's more effort than doing it by hand.
char* fname = new char[50]; // buffer big enough to hold filenames
int numFrames = 8; // or however many, you seem to know what this value should be
for(int i = 0; i < numFrames; ++i)
{
sprint(fname, "Animations/foo%d.png",(i+1));
foo_ani[i] = LoadImage(fname);
}
delete[] fname;
That's about 6 lines of code. So for animations of more than 6 frames, I'd say that was easier.