C++ string equivalent for strrchr - c++

Using C strings I would write the following code to get the file name from a file path:
#include <string.h>
const char* filePath = "dir1\\dir2\\filename"; // example
// extract file name (including extension)
const char* fileName = strrchr(progPath, '\\');
if (fileName)
++fileName;
else
fileName = filePath;
How to do the same with C++ strings? (i.e. using std::string from #include <string>)

The closest equivalent is rfind:
#include <string>
std::string filePath = "dir1\\dir2\\filename"; // example
// extract file name (including extension)
std::string::size_type filePos = filePath.rfind('\\');
if (filePos != std::string::npos)
++filePos;
else
filePos = 0;
std::string fileName = filePath.substr(filePos);
Note that rfind returns an index into the string (or npos), not a pointer.

To find the last occurence of a symbol in a string use std::string::rfind
std::string filename = "dir1\\dir2\\filename";
std::size_t pos = filename.rfind( "\\" );
However, if you're handling filenames and pathes more often, have a look at boost::filesystem
boost::filesystem::path p("dir1\\dir2\\filename");
std::string filename = p.filename().generic_string(); //or maybe p.filename().native();

Either call string::rfind(), or call std::find using reverse iterators (which are returned from string::rbegin() and string::rend()).
find might be a little bit more efficient since it explicitly says that you're looking for a matching character. rfind() looks for a substring and you'd give it a length 1 string, so it finds the same thing.

Apart from rfind(), you can also use find_last_of()
You have an example too written in cplusplus.com which is same as your requirement.

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.

Concatenation of strings in C++ (Linux)

I want to concatenate three string in C++.
I have a vector std::vector<std::string> my_list where the filenames are stored. I want to add the directory and filename extension for each of the filenames in order to read binary the information from the file, so i do it like that:
for (int i = 0; i < my_list.size(); i++) {
std::string tmp = prefix + my_list[i] + suffix;
std::ifstream file(tmp.c_str(), std::ifstream::binary);
}
where prefix ist std::string prefix = "directory/" and suffix ist std::string suffix = ".foo".
And it works in Windows. However it doesn't work in Linux.
Suffix overwrites "tmp"-string. It looks like foo/y_file_timestamp instead of out/my_file_timestamp.foo.
What should I do to prevent this overwriting?
The bug is not in the code you showed us.
The problem is that your strings have unexpected characters in them, specifically carriage returns ('\r') that cause the caret to return to the beginning of the line during output of your concatenated string to the terminal window.
Presumably this is a problem caused by careless parsing of input data with Windows-style line endings. You should normalise your data and be sure to strip all line-ending variants during parsing.
Always be sure to check the contents of your strings at the character-level when encountering a problem with string operations.
Thank you #BarryTheHatchet. I forgot to mention that vector my_list was filled this way:
std::string LIST_WITH_DATA = "data/list.scp"
const char* my_data = LIST_WITH_DATA.c_str();
std::ifstream my_file(my_data);
std::string my_line;
while (std::getline(my_file, my_line)) {
my_list.push_back(my_line);
}
data/list.scp looks like:
file1/00001-a
file2/00001-b
file3/00001-c
file4/00001-d
std::getline was my problem.
The solution I found here: Getting std :: ifstream to handle LF, CR, and CRLF?

C++ - grep path in linux

I am new to linux and I want to know how to get path which is in the following format - /home/linux/sample/?
I want to write a c++ function which takes the path as input and returns true as if the path has /home/linux/sample/.
Example:
If the path is /home/linux/sample/test.txt should return true
If the path is /home/linux/sample/dir should return true
If the path is /home/linux/user/test.txt should return false
Can some one please help me?
Thanks in advance.
To write such a function you need only std::string:
string str ("There are two needles in this haystack.");
string str2 ("needle");
if (str.find(str2) != string::npos) {
//.. found.
}
If the algorithm will get more sophisticated than I would move to regular expressions.
Looks like you're trying to check if a string is the prefix of another one. In that case, you can rely on std::equal from <algorithm>:
std::string prefix = "/home/linux/sample/";
std::string path0 = "/home/linux/sample/test.txt";
if(std::equal(prefix.begin(), prefix.end(), path0.begin())) {
...
}
The / at the end of prefix is important!

Find only file name from full path of the file in vc++

Suppose there is a CString variable which store the full path of the file.Now I hava to find only file name from if.How to do it in vc++.
CString FileName = "c:\Users\Acer\Desktop\FolderName\abc.dll";
Now I want only abc.dll.
You can use PathFindFileName.
Remember that you have to escape the \ character in your path string!
Same as already stated above, but as u are using MFC framework, this would be the way to do it. Though this does not check files existence.
CString path= "c:\\Users\\Acer\\Desktop\\FolderName\\abc.dll";
CString fileName= path.Mid(path.ReverseFind('\\')+1);
std::string str = "c:\\Users\\Acer\\Desktop\\FolderName\\abc.dll";
std::string res = str.substr( str.find_last_of("\\") + 1 );
Will get you "abs.dll".
I would use Boost::FileSystem for filename manipulation as it understands what the parts of a name would be. The function you want here would be filename()
If you are just getting the filename you can do this using CString functions. First find the ast backslash using ReverseFind and then Right to get the string wanted.
The code below demonstrate extracting a file name from a full path
#include <iostream>
#include <cstdlib>
#include <string>
#include <algorithm>
std::string get_file_name_from_full_path(const std::string& file_path)
{
std::string file_name;
std::string::const_reverse_iterator it = std::find(file_path.rbegin(), file_path.rend(), '\\');
if (it != file_path.rend())
{
file_name.assign(file_path.rbegin(), it);
std::reverse(file_name.begin(), file_name.end());
return file_name;
}
else
return file_name;
}
int main()
{
std::string file_path = "c:\\Users\\Acer\\Desktop\\FolderName\\abc.dll";
std::cout << get_file_name_from_full_path(file_path) << std::endl;
return EXIT_SUCCESS;
}

Parsing a string by a delimeter in C++

Ok, so I need some info parsed and I would like to know what would be the best way to do it.
Ok so here is the string that I need to parse. The delimeter is the "^"
John Doe^Male^20
I need to parse the string into name, gender, and age variables. What would be the best way to do it in C++? I was thinking about looping and set the condition to while(!string.empty()
and then assign all characters up until the '^' to a string, and then erase what I have already assigned. Is there a better way of doing this?
You can use getline in C++ stream.
istream& getline(istream& is,string& str,char delimiter=ā€™\nā€™)
change delimiter to '^'
You have a few options. One good option you have, if you can use boost, is the split algorithm they provide in their string library. You can check out this so question to see the boost answer in action: How to split a string in c
If you cannot use boost, you can use string::find to get the index of a character:
string str = "John Doe^Male^20";
int last = 0;
int cPos = -1;
while ((cPos = str.find('^', cPos + 1)) != string::npos)
{
string sub = str.substr(last, cPos - last);
// Do something with the string
last = cPos + 1;
}
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "This is a sample string";
char * pch;
printf ("Looking for the 's' character in \"%s\"...\n",str);
pch=strchr(str,'s');
while (pch!=NULL)
{
printf ("found at %d\n",pch-str+1);
pch=strchr(pch+1,'s');
}
return 0;
}
Do something like this in an array.
You have a number of choices but I would use strtok(), myself. It would make short work of this.