Find a char or substring with specifying start pos - c++

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.

Related

Use C++, How to change the above multiple \n into only one \n?

CString str = _T("111\n\n\n222");
How to change the above multiple \n into only one \n?
Cannot use Replace directly, because the number of \n is not fixed
while (str.Replace("\n\n", "\n") > 0)
;
You can use CString::GetBuffer to obtain a buffer that you can modify. The corresponding CString::ReleaseBuffer allows you to specify a new length for the string.
If you want to remove consecutive characters, you can do this easily by simply walking through the string and rewriting its characters. Any time you see a character that you wish to remove, simply don't write it and don't update the end-position of the string.
Here's a general-purpose function to remove some number of consecutive characters from a CString:
void LimitConsecutiveCharacters(CString& str, TCHAR ch, int maxConsecutive = 1)
{
LPTSTR *begin = str.GetBuffer(0);
LPTSTR *end = begin;
int consecutive = 0;
for (LPTSTR *pos = begin; *pos != _T('\0'); ++pos)
{
if (*pos == ch)
{
if (consecutive >= maxConsecutive)
continue;
++consecutive;
}
else
{
consecutive = 0;
}
*end++ = *pos;
}
int newLength = end - begin;
str.ReleaseBuffer(newLength);
}
As you can see above, it keeps a count of how many consecutive values it has seen for the target character. If the maximum number of consecutive characters is reached, then it simply moves to the next loop iteration. Any time it sees some other character, the "consecutive" count resets.
The end tracks the position that is being written to, which might even be the same position you're reading from, if you've not removed any characters. At the end, some simple pointer arithmetic calculates the new string length and calls CString::ReleaseBuffer.
An example invocation would be:
CString str = _T("111\n\n\n222");
LimitConsecutiveCharacters(str, _T('\n'));
You can convert your CString into a std::wstring, use regex_replace and then convert back to CString.
The patterns for the regular expression would be something like:
find what: L"\n+"
replace by: L"\n"

Splitting a char array by delimeter, then saving the result?

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

CString find the last entry

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++.

Get file name without extension?

I'm new to C++ world, I stuck with a very trivial problem i.e. to get file name without extension.
I have TCHAR variable containing sample.txt, and need to extract only sample, I used PathFindFileName function it just return same value what I passed.
I tried googling for solution but still no luck?!
EDIT: I always get three letter file extension, I have added the following code,
but at the end I get something like Montage (2)««þîþ how do I avoid junk chars at the end?
TCHAR* FileHandler::GetFileNameWithoutExtension(TCHAR* fileName)
{
int fileLength = _tcslen(fileName) - 4;
TCHAR* value1 = new TCHAR;
_tcsncpy(value1, fileName, fileLength);
return value1;
}
Here's how it's done.
#ifdef UNICODE //Test to see if we're using wchar_ts or not.
typedef std::wstring StringType;
#else
typedef std::string StringType;
#endif
StringType GetBaseFilename(const TCHAR *filename)
{
StringType fName(filename);
size_t pos = fName.rfind(T("."));
if(pos == StringType::npos) //No extension.
return fName;
if(pos == 0) //. is at the front. Not an extension.
return fName;
return fName.substr(0, pos);
}
This returns a std::string or a std::wstring, as appropriate to the UNICODE setting. To get back to a TCHAR*, you need to use StringType::c_str(); This is a const pointer, so you can't modify it, and it is not valid after the string object that produced it is destroyed.
You can use PathRemoveExtension function to remove extension from filename.
To get only the file name (with extension), you may have first to use PathStripPath, followed by PathRemoveExtension.
Try below solution,
string fileName = "sample.txt";
size_t position = fileName.find(".");
string extractName = (string::npos == position)? fileName : fileName.substr(0, position);
TCHAR* FileHandler::GetFileNameWithoutExtension(TCHAR* fileName)
{
int fileLength = _tcslen(fileName) - 4;
TCHAR* value1 = new TCHAR[fileLength+1];
_tcsncpy(value1, fileName, fileLength);
return value1;
}
Try this:
Assuming the file name is in a string.
string fileName = your file.
string newFileName;
for (int count = 0;
fileName[count] != '.';
count++)
{
newFileName.push_back(fileName[count]);
}
This will count up the letters in your original file name and add them one by one to the new file name string.
There are several ways to do this, but this is one basic way to do it.

Splitting the string at Enter key

I'm getting the text from editbox and I'd want to get each name separated by enter key like the character string below with NULL characters.
char *names = "Name1\0Name2\0Name3\0Name4\0Name5";
while(*names)
{
names += strlen(names)+1;
}
how would you do the same for enter key (i.e separated by /r/n) ? can you do that without using the std::string class?
Use strstr:
while (*names)
{
char *next = strstr(names, "\r\n");
if (next != NULL)
{
// If you want to use the key, the length is
size_t len = next - names;
// do something with a string here. The string is not 0 terminated
// so you need to use only 'len' bytes. How you do this depends on
// your need.
// Have names point to the first character after the \r\n
names = next + 2;
}
else
{
// do something with name here. This version is 0 terminated
// so it's easy to use
// Have names point to the terminating \0
names += strlen(names);
}
}
One thing to note is that this code also fixes an error in your code. Your string is terminated by a single \0, so the last iteration will have names point to the first byte after your string. To fix your existing code, you need to change the value of names to:
// The algorithm needs two \0's at the end (one so the final
// strlen will work and the second so that the while loop will
// terminate). Add one explicitly and allow the compiler to
// add a second one.
char *names = "Name1\0Name2\0Name3\0Name4\0Name5\0";
If you want to start and finish with a C string, it's not really C++.
This is a job for strsep.
#include <stdlib.h>
void split_string( char *multiline ) {
do strsep( &multiline, "\r\n" );
while ( multiline );
}
Each call to strsep zeroes out either a \r or a \n. Since only the string \r\n appears, every other call will return an argument. If you wanted, you could build an array of char*s by recording multiline as it advances or the return value of strsep.
void split_string( char *multiline ) {
vector< char* > args;
do {
char *arg = strsep( &multiline, "\r\n" );
if ( * arg != 0 ) {
args.push_back( arg );
}
} while ( multiline );
}
This second example is at least not specific to Windows.
Here's a pure pointer solution
char * names = "name1\r\nName2\r\nName3";
char * plast = names;
while (*names)
{
if (names[0] == '\r' && names[1] == '\n')
{
if (plast < names)
{
size_t cch = names - plast;
// plast points to a name of length cch, not null terminated.
// to extract the name use
// strncpy(pout, plast, cch);
// pout[cch] = '\0';
}
plast = names+2;
}
++names;
}
// plast now points to the start of the last name, it is null terminated.
// extract it with
// strcpy(pout, plast);
Since this has the C++ tag, the easiest would probably using the C++ standard library, especially strings and string streams. Why do you want to avoid std::string when you're doing C++?
std::istringstream iss(names);
std::string line;
while( std::getline(iss,line) )
process(line); // do process(line.c_str()) instead if you need to