I have two CString s1 and CString s2. I need find the last entry s2 in s1.
I can find any metod in CString like in C# LastIndexOf.
I am nooby in c++. Thanks in advance.
CString has no such function. You have to write it yourself, e.g.
int LastIndexOf(const CString& s1, const CString& s2)
{
int found = -1;
int next_pos = 0;
for (;;)
{
next_pos = s1.Find(s2, next_pos);
if (next_pos == -1)
return found;
found = next_pos;
};
}
A more optimal algorithm would reverse the strings first, I'm leaving that as an exercise.
i adopted the answer from Andrey (to increment the next_pos, seems to cause an endless loop without it). aaand, since i don't have enough repu points yet (can't comment) i'll post it as a separate answer:
int LastIndexOf(const CString& s1, const CString& s2)
{
int start = s1.Find(s2, 0);
if (start >= 0)
{
while (start < s1.GetLength())
{
int idx = s1.Find(s2, start+1);
if (idx >= 0)
start = idx;
else
break;
}
}
return start;
}
There is no CString method to directly address your question. However, you can use a combination of CString::ReverseFind + _tcsncmp to first locate next occurrence of last character of substring and, if found, compare the whole substring from there.
CString is I think part of the "Microsoft Foundation Class Library" and is not Standard C++.
There is a reference including methods here:
http://msdn.microsoft.com/en-us/library/aa315043%28v=vs.60%29.aspx
I don't see anything to turn that directly into a std::string (which has a lot more methods), but it probably is not so hard, either (search for "CString to std::string" and you'll find some stuff).
Although they are presumably related, don't confuse this with a c-string, which is an array of chars from Standard C included in Standard C++.
Related
I am using a std::string to hold binary data read from a socket.
The data consists of messages beginning with a '$' and ending with a '#'. Each message may contain '\0' characters.
I use std::string::find() to find the location of the first message and extract it from the string using std::string::substr():
class MessageSplitter {
public:
MessageSplitter() { m_data.reserve(1'000'000); }
void appendBinaryData(const std::string& binaryData) {
m_data.append(bytes);
}
bool popMessage(std::string& msg) {
size_t beg_index = m_data.find("$");
if (beg_index == std::string::npos) {
return false;
}
size_t end_index = m_data.find("#", beg_index);
if (end_index == std::string::npos) {
return false;
}
size_t count = end_index - beg_index + end.size();
msg = m_data.substr(beg_index, count);
m_data = m_data.substr(end_index + end.size());
return true;
}
private:
std::string m_data;
};
I read from socket this way (error checking on recv omitted):
char buffer[4096];
int ret = ::recv(m_socket, buffer, 4096, 0);
std::string binaryData = std::string(buffer, ret);
This approach seems to work fine on Windows.
However is it guaranteed to work on other platforms according to the C++ standard?
This is perfectly safe from a language level. std::string is guaranteed to be able to handle non-printable characters including embedded nul characters just fine.
From a programmer's prospective though it's somewhat unsafe because it's surprising. When I see std::string I generally expect it to be printable text. It has an operator<< for example to make it easy to print to output streams, and I have to remember never to use that.
For the second reason, I would tend to prefer something more explicit. std::vector<std::byte> or std::vector<unsigned char> or similar. Something that doesn't act like text is much more difficult to accidentally treat as text.
The best way to illustrate my question is with this example (that doesn't work if I use the strstr CRT function):
const wchar_t* s1 = L"Hauptstraße ist die längste";
const wchar_t* s2 = L"Hauptstrasse";
bool b_s1_starts_with_s2 = !!wcsstr(s1, s2);
_ASSERT(b_s1_starts_with_s2); //Should be true
So far the only WinAPI that seems to recognize linguistic string equivalency is CompareStringEx when used with the LINGUISTIC_IGNORECASE flag, but it is somewhat tricky & inefficient to use for this purpose as I will have to call it on s2 repeatedly until I reach its end.
So I was wondering if there's a better approach to doing this (under Windows)?
EDIT: Here's what I mean:
bool b_s1_starts_with_s2 = false;
int ln1 = (int)wcslen(s1);
int ln2 = (int)wcslen(s2);
for(int p = 1; p <= ln1; p++)
{
if(::CompareString(LOCALE_USER_DEFAULT, LINGUISTIC_IGNORECASE,
s1, p,
s2, ln2) == CSTR_EQUAL)
{
//Match
b_s1_starts_with_s2 = true;
break;
}
}
You can use FindNLSString, check if the return value is zero.
Evidently it matches ß with ss
const wchar_t *s1 = L"Hauptstraße ist die längste";
const wchar_t *s2 = L"Hauptstrasse";
INT found = 0;
int start = FindNLSString(0, LINGUISTIC_IGNORECASE, s1, -1, s2, -1, &found);
wprintf(L"start = %d\n", start);
s1 = L"δεθ Testing Greek";
s2 = L"ΔΕΘ";
start = FindNLSString(0, LINGUISTIC_IGNORECASE, s1, -1, s2, -1, &found);
wprintf(L"start = %d\n", start);
I have not tried it, but I think you probably could use LCMapStringEx to transform all strings to lowercase appropriately for the locale, and then do a normal string prefix match with wcsncmp.
(As noted in comments, it makes no sense that you used wcsstr in your example since wcsstr determines if one string contains another string. To determine if one string starts with another string, it's more efficient to use wcsncmp with the length of the prefix string.)
I need to be able to parse the following two strings in my program:
cat myfile || sort
more myfile || grep DeKalb
The string is being saved in char buffer[1024]. What I need to end up with is a pointer to a char array for the left side, and a pointer to a char array for the right side so that I can use these to call the following for each side:
int execvp(const char *file, char *const argv[]);
Anyone have any ideas as to how I can get the right arguments for the execvp command if the two strings above are saved in a character buffer char buffer[1024]; ?
I need char *left to hold the first word of the left side, then char *const leftArgv[] to hold both words on the left side. Then I need the same thing for the right. I have been messing around with strtok for like two hours now and I am hitting a wall. Anyone have any ideas?
I recommend you to learn more about regular expressions. And in order to solve your problem painlessly, you could utilize the Boost.Regex library which provides a powerful regular expression engine. The solution would be just several lines of code, but I encourage you to do it yourself - that would be a good exercise. If you still have problems, come back with some results and clearly state where you were stuck.
You could use std::getline(stream, stringToReadInto, delimeter).
I personally use my own function, which has some addition features baked into it, that looks like this:
StringList Seperate(const std::string &str, char divider, SeperationFlags seperationFlags, CharValidatorFunc whitespaceFunc)
{
return Seperate(str, CV_IS(divider), seperationFlags, whitespaceFunc);
}
StringList Seperate(const std::string &str, CharValidatorFunc isDividerFunc, SeperationFlags seperationFlags, CharValidatorFunc whitespaceFunc)
{
bool keepEmptySegments = (seperationFlags & String::KeepEmptySegments);
bool keepWhitespacePadding = (seperationFlags & String::KeepWhitespacePadding);
StringList stringList;
size_t startOfSegment = 0;
for(size_t pos = 0; pos < str.size(); pos++)
{
if(isDividerFunc(str[pos]))
{
//Grab the past segment.
std::string segment = str.substr(startOfSegment, (pos - startOfSegment));
if(!keepWhitespacePadding)
{
segment = String::RemovePadding(segment);
}
if(keepEmptySegments || !segment.empty())
{
stringList.push_back(segment);
}
//If we aren't keeping empty segments, speedily check for multiple seperators in a row.
if(!keepEmptySegments)
{
//Keep looping until we don't find a divider.
do
{
//Increment and mark this as the (potential) beginning of a new segment.
startOfSegment = ++pos;
//Check if we've reached the end of the string.
if(pos >= str.size())
{
break;
}
}
while(isDividerFunc(str[pos]));
}
else
{
//Mark the beginning of a new segment.
startOfSegment = (pos + 1);
}
}
}
//The final segment.
std::string lastSegment = str.substr(startOfSegment, (str.size() - startOfSegment));
if(keepEmptySegments || !lastSegment.empty())
{
stringList.push_back(lastSegment);
}
return stringList;
}
Where 'StringList' is a typedef of std::vector, and CharValidatorFunc is a function pointer (actually, std::function to allow functor and lambda support) for a function taking one char, and returning a bool. it can be used like so:
StringList results = String::Seperate(" Meow meow , Green, \t\t\nblue\n \n, Kitties!", ',' /* delimeter */, DefaultFlags, is_whitespace);
And would return the results:
{"Meow meow", "Green", "blue", "Kitties!"}
Preserving the internal whitespace of 'Meow meow', but removing the spaces and tabs and newlines surrounding the variables, and splitting upon commas.
(CV_IS is a functor object for matching a specific char or a specific collection of chars taken as a string-literal. I also have CV_AND and CV_OR for combining char validator functions)
For a string literal, I'd just toss it into a std::string() and then pass it to the function, unless extreme performance is required. Breaking on delimeters is fairly easy to roll your own - the above function is just customized to my projects' typical usage and requirements, but feel free to modify it and claim it for yourself.
In case this gives anyone else grief, this is how I solved the problem:
//variables for the input and arguments
char *command[2];
char *ptr;
char *LeftArg[3];
char *RightArg[3];
char buf[1024]; //input buffer
//parse left and right of the ||
number = 0;
command[0] = strtok(buf, "||");
//split left and right
while((ptr=strtok(NULL, "||")) != NULL)
{
number++;
command[number]=ptr;
}
//parse the spaces out of the left side
number = 0;
LeftArg[0] = strtok(command[0], " ");
//split the arguments
while((ptr=strtok(NULL, " ")) != NULL)
{
number++;
LeftArg[number]=ptr;
}
//put null at the end of the array
number++;
LeftArg[number] = NULL;
//parse the spaces out of the right side
number = 0;
RightArg[0] = strtok(command[1], " ");
//split the arguments
while((ptr=strtok(NULL, " ")) != NULL)
{
number++;
RightArg[number]=ptr;
}
//put null at the end of the array
number++;
RightArg[number] = NULL;
Now you can use LeftArg and RightArg in the command, after you get the piping right
execvp(LeftArg[0], LeftArg);//execute left side of the command
Then pipe to the right side of the command and do
execvp(RightArg[0], RightArg);//execute right side of command
I think I need a reverse version of strtok, like:
char* p = rstrtok(str, delimeters);
For example, sequentially get the position of '-', '_' and '+' in the string "hello+stack_over-flow" using a delimeter set of "+_-"
I only care about the delimeters, and their position, (not the content between), so I guess the boost::split_iterator is not appropriate here.
Is there any existing utility function I can leverage? or any solution to deal with this kind of situation?
Furthermore, since I am doing C++, is there any convenient approach to avoid this old fashion C?
(I searched "reverse strtok" but merely get "stack over flow" to "flow over stack" stuff...)
You could roll your own using strrchr.
If you use C++ style std::string you can leverage string::find_last_of.
You could do this with strpbrk:
char string[] = "hello+stack_over-flow";
char *pos = string;
while (*pos != '\0' && (pos = strpbrk(pos, "+-_")) != NULL)
{
/* Do something with `pos` */
pos++; /* To skip over the found character */
}
strtok is pretty simple in the most basic versions - a couple hundred lines at most.
If you google "strtok filetype:c" you can see how it's implemented in the various libraries.
A very naive solution would be to reverses the string first, and then do the strtok().
This is bad for long strings though, but if you need performance, go roll your own strtok().
Something like this:
void reverse(char* dest, const char* src)
{
int len = strlen(src);
int i;
for(i = 0; i < len; ++i)
dest[len-i-1] = src[i];
}
EDIT:
Coincidentally I have this Dr Dobbs page open in a tab from a google search yesterday: http://www.drdobbs.com/conversations-al-go-rithms/184403801
I couldn't find a function which would let me specify the start pos for beginning a char or substring search.
I have, for example:
char *c = "S1S2*S3*S4";
I'd like search for 'S3' by searching the first '*' asterisk and then the second asterisk following it and finally getting the substring 'S3' enclosed by those asterisks.
The string class has a large find family of functions that take an index as a second argument. Repeated applications of find('*', index) should get you what you need.
std::string s(c);
std::string::size_type star1 = s.find('*');
std::string::size_type star2 = s.find('*', star1 + 1);
std::string last_part = s.substr(star2 + 1);
One solution would be to find the location of the first asterisk, then the location of the second asterisk. Then use those positions as the start and end locations to search for S3.
Use
char *strchr( const char *str, int ch );
See here for reference
#include <string>
std::string between_asterisks( const std::string& s ) {
std::string::size_type ast1 = s.find('*');
if (ast1 == std::string::npos) {
throw some_exception();
}
std::string::size_type sub_start = ast1+1;
std::string::size_type ast2 = s.find('*', sub_start);
if (ast2 == std::string::npos) {
throw some_exception();
}
return s.substr(sub_start, ast2-sub_start);
}
You can use strchr(). Simply save the returned pointer and pass it to the next call. As this pointer points to the occurence of your search, the search will start from there.
well one possibility - if you are to use c-style char* arrays for strings - is to use strchr to search for the occurrences of the asterisks, e.g., (and with NO error checking, mind)
char c []= "S1S2*S3*S4";
char* first = strchr(c,'*');
if (first) {
char* start = ++first;
char* nextast = strchr(start,'*');
char* s3str = new char[nextast-start+1];
strncpy(s3str,start,nextast-start);
s3str[next-start] = '\0';
}
But it would be easier to use the C++ string class to do this.