CString extract file path - c++

Hey I'm trying to extract the file path but the problem is that I'm stuck in an infinite loop don't understand why. Please have a look at my code.
CString myString(_T("C:\\Documents and Settings\\admin\\Desktop\\Elite\\Elite\\IvrEngine\\dxxxB1C1.log"));
int pos = myString.Find(_T("\\"));
while (pos != -1)
{
pos = myString.Find(_T("\\"), pos); // it keeps returning 2
}
CString folderPath = myString.Mid(pos);
Now the problem is that, Find() returns 2 the first time I run, but then in the while loop it keeps returning 2, why is the function unable to find the rest '\' ? So now I'm in an infinite loop :(.

It sounds like Find includes the character at the position you give it when searching. So if you give it the position of a character that matches the search, then it will return that same position.
You probably need to change it to:
pos = myString.Find(_T("\\"), pos + 1);

your code will never work! When the while loop finished, the contend of pos can not be used.
Here is a solution which will work:
CString folderPath;
int pos = myString.ReverseFind('\\');
if (pos != -1)
{
folderPath = myString.Left(pos);
}

You can fix the code (see the pos + 1 answers) but I think that you should use _splitpath_s instead which was intended for this kind of operations.

CString::Find always returns the first occurence of the character you're searching for. So it keeps finding the the first "\\" which is at index 2 infinitely since you're searching from 2 which includes that "\\"

I can understand your initial implementation, as the behaviour of CString::Find() seem to have changed over time.
Take a look at the MSDN docs for MFC implementation shipped with VC6 here and at the current implementation here. Especially look at the differences of the description of the 2nd offset parameter.
The solution to your problem is, as already stated above, to add 1 to the search offset of the successive Find() calls. You can also search for single chars (or wchar_ts) like that:
myString.Find(_T('\\'), pos+1);
EDIT:
BTW, take a look at the Path* familly of functions exposed by the shlwapi.dll, declared in shlwapi.h. Especially the PathRemoveFileSpec function might be of interest to you.

in MFC, example to get folder which including executable file:
char ownPth[MAX_PATH];
// Will contain exe path
HMODULE hModule = GetModuleHandle(NULL);
if(NULL == hModule){
return __LINE__;
}
// When passing NULL to GetModuleHandle, it returns handle of exe itself
GetModuleFileName(hModule,ownPth, (sizeof(ownPth)));
modulePath = (LPCSTR)ownPth;
modulePath = modulePath.Left(modulePath.ReverseFind(_T('\\')));
return 0;

Related

Check string for recurrence of specific character

How can I search inside of a string for more than one occurrence of a specific character (in this case a period .)?
I have already tried adapting the answer from this question, but I think I am doing it wrong.
std::string periodCheck = i.convert_to<std::string>();
char subString = '.';
std::size_t pos = periodCheck.find(subString, 0);
int counter;
while(pos != std::string::npos){
counter++;
if(counter > 1){
std::cout << "\nError: Multiple periods\n";
return false;
}
}
The first line simply converts from a Boost multi-precision cpp_dec_float (named i) to a string. I know that this part of the code works, because I use it effectively elsewhere in the program.
I am trying to check if a string contains more than one period. If the string has more than one period in it, the function returns false.
How can I achieve this?
If you find a period, then your next logical step would also be to search again, starting with the next character position.
However, if you review your code, you will not be able to find any place where it is actually searching again. There's no call to find() inside the while loop.
A while loop is not required at all. All you need to do is to call find() a second time, specifying pos+1 as the starting position for the second search, and check the results again. If you find another period, you can call it a wrap. Nothing is to be gained by searching for any remaining periods in the string. You have your answer.
std::size_t pos = periodCheck.find(subString, 0);
if (pos != std::string::npos)
{
pos=periodCheck.find(subString, pos+1);
if (pos != std::string::npos)
return false;
}

CString::find... iterative use issue?

I'm using a CString to search a text block... here's my code:
// locate file name in dir listing
in = *buf;
i = in.Find("DOWNLD .DAT ");// find start of name, two spaces (0x20) as delim
// size of search text here is 14
if (i == -1) return 0;
j = in.Find(' ',i);// now find next space char *after* file size...
// why don't I have to add to i here? There are spaces in my search string.
if (j == -1) return 0;
fileSize = in.Mid((i+14),j-i);// extract file size string, note indexing past found string
return atoi(fileSize.GetBuffer());
Here's what MSDN has to say about the return value of find:
" Return Value
The zero-based index of the first character in this CString object that matches the requested substring or characters; -1 if the substring or character is not found."
Now the way I read this, I have to index past the string I found before doing another find... but the way it actually works, I use the 'i' returned before as the start position for a new search. I'm using this in other places in my program, and I definitely have to index past it (when using ::mid(), for instance)... I'd like to know why this is happening, if by design or bug. The original string can be large; I've seen it near 300chars... is this the problem?
Your second Find call finds the space after "DOWNLD", not the one after ".DAT ". You want to increment i before the second Find call, so that it refers to the first character past the string your first call searched for.
So I didn't find a bug in CString... my code was in error. Here's the code changed that works:
j = in.Find(' ',i+14);// index past searched string
if (j == -1) return 0;
fileSize = in.Mid((i+14),j-i-14);// note -14 added
return atoi(fileSize.GetBuffer());
It was the missing -14 in the mid that was confusing me... the resulting string was 14 past where it should have been, and was missing the portion of interest. Why my original fix worked? Just a coincidence I guess.
Thanks for helping!

string.find() doesn't return -1

The code below is simple. As I know, if string::find() didn't find matches it returns -1. But for some reasons the code below doesn't work. Everytime I run this code I get endless loop. Thank you for help!
#include <string>
#include <iostream>
using namespace std;
int main()
{
string text;
text = "asdasd ijk asdasd";
string toReplace = "ijk";
cout<<text<<endl;
int counter = 0;
while ( text.find(toReplace) != -1)
counter++;
cout<<counter<<endl;
system("pause");
}
Aside from the other answers which are completely correct, I just wanted to add that your while loop would have produced an endless loop anyway. For example:
while(text.find(toReplace) != std::string::npos)
counter++;
will be an endless loop because it will keep trying to find the toReplace string in text and it will always find it (that's because find starts from the beginning of the string each time). This is probably not what you intended.
std::string::find returns std::string::npos if the searched substring is not found, not -1. The exact value of npos is implementation-defined, so use npos, as in
while ( text.find(toReplace) != std::string::npos)
Come to think of it, find couldn't return -1 even if it wanted to because the return type of find is specified to be std::size_t which is an unsigned type.
Additionally, find will always search for the first occurrence of the substring, no matter how many times you call it. If you want to iterate through all the occurrences you should use the overload of find which takes a second parameter - the position from which to start searching.
Whoever told you this or wherever you read it, it lied to you.
If std::string::find fails, it returns std::string::npos, which is not -1.
You should check the documentation about such things, when you're not sure.
So, your while will be something like:
while ( std::string::npos != text.find(toReplace) )
Regarding your comment:
UPDATE: I tried to use while ( text.find(toReplace) != string::npos ) but I still get endless loop :( – user2167403 10 secs ago
You should really learn to read the documentation. Use a variable to store the last result of std::string::find (different from std::string::npos) and use std::string::find's second parameter - pos ( by passing value - last_match_position + 1).
Omitting the second parameter, std::string::find always starts from the beginning of the string, which causes the endless loop.
In the code snippet you provided text variable contains substring "ijk" which is stored in the toReplace variable. As long as in while cycle neither text or toReplace variables are changed, find method will always return not a -1 value which is the condition for while cycle to continue.
As already metioned in other comments you should check not for -1 but for std::string::npos.
It does help to read the manual page (string::npos is the answer).
See http://www.cplusplus.com/reference/string/string/find/

How do I return the end of a directory

I have a function that is supposed to find the last bit of a directory in a string. eg:
"C:\Lolcats\pie\ambulance\" should return "ambulance". However it returns some strange characters ive never seen, like the male arrow-point symbol and some other weird stuff.
string App::getlastName(string cakes){
//finds the name of the last folder in a directory
string name;
string temp;//popback all of temp into name to invert it
cakes.pop_back();
char i = cakes[cakes.length()-1];
while (i != '\\'){
temp.push_back(cakes[i]);
cakes.pop_back();
i = cakes[cakes.length()-1];
} //-1?
for (int j = 0; j<temp.length(); ++j){
name.push_back(temp.back());
temp.pop_back();
}
return name;
}
This is probably one of the worst functions i've ever written, but I can't think of how else to wrangle the end off :( Can someone help me please? :D
Note that the function doesnt need to find the name of a file, it'll just be folders.
Two steps:
if it ends with a backslash character remove it:
if (!cakes.empty() && '\\' == *(cakes.end() - 1))
{
cakes.erase(cakes.end() - 1);
}
use std::string::find_last_of() to locate last backslash and std::string::substr() to extract the last part:
std::string last_part;
const size_t slash_idx = cakes.find_last_of("\\");
if (std::string::npos != slash_idx)
{
last_part = cakes.substr(slash_idx + 1);
}
If it is possible that the directory name could contain forward slashes add the additional check for the last character and just add it to the argument to find_last_of("\\/"), as it can search for more than one character.
If you remove the trailing \ off the string you can use a simple combination of rfind and substr to get the data you want.
string substring = cakes.substr(cakes.rfind("\\") + 1);
#Joel Rondeau's comment where he says that the line temp.push_back(cakes[i]) is a problem is correct, but I thought I would elaborate.
The reason is that the variable i is defined as a char not an int, but it is possible for the two to be cast implicitly. So the reason why you are getting strange characters returned is because casting a char to an int has resulted in an index value that probably does not exist in your string.
Reading your code sample it looks like you should be doing temp.push_back(cakes[cakes.length()-1]) instead (or better, store this index in a temp variable so you don't have to keep writing it every time).
Your method depends on the string already being in the correct format with no assertions or error checking--not a good idea.
I would just do something like:
char* directoryName = strrchr(fullPath, '\\') + 1;
after you have trimmed off the trailing '\'.

C++: how to judge if the path of the file start with a given path

I have a path, for example, named
/my/path/test/mytestpath
, and I want to judge if it start with a given path, for example
/my/path
The C++17 filesystem library is probably the most robust solution. If C++17 is not available to you, Boost.Filesystem provides an implementation for earlier C++ versions. Try something like:
bool isSubDir(path p, path root)
{
while(p != path()) {
if(p == root) {
return true;
}
p = p.parent_path();
}
return false;
}
Substring the length of the string ( /my/path ) of the original (/my/path/test/mytestpath ) from the beginning.
Check whether two strings are equal.
You can do a string compare of the number of characters in the shorter string.
The fact that the characters match of itself won't mean it is a sub-path because you need to check that the next character in the longer string is a '/'
In C you can use strncmp() which takes a length of characters.
In C++ you can use the same or string compare functions. The find() function will work for this but remember to also check that the next character in the main path is a directory separator.
You could "tokenize" your path but that is likely to not be worth it.
std::string::find() returns the index at which a string was found, with an index of 0 being the start of the string:
std::string path("/my/path/test/mytestpath");
// This will check if 'path' begins with "/my/path/".
//
if (0 == path.find("/my/path/"))
{
// 'path' starts with "/my/path".
}