Parse String Between Brackets - c++

I have a string that goes like this:
Room -> Subdiv("X", 0.5, 0.5) { sleep | work } : 0.5
I need to somehow extract the 2 strings between {} , i.e. sleep and work. The format is strict, there can be just 2 words between the brackets, the words can change though. The text before and after the brackets can also change. My initial way of doing it was:
string split = line.substr(line.find("Subdiv(") + _count_of_fchars);
split = split.substr(4, axis.find(") { "));
split = split.erase(split.length() - _count_of_chars);
However, I do realised that this is no going to work if the strings in side the brackets are changed o anything with a different length.
How can this be done? Thanks!

Without hard-coding any numbers:
Find A as the index of the first "{" from the end of the string, search backward.
Find B as the index of the first "|" from the position of "{", search forward.
Find C as the index of the first "}" from the position of "|", search forward.
The substring between B and A gives you the first string. While the substring between C and B gives you the first string. You can include the spaces in your substring search, or take them out later.
std::pair<std::string, std::string> SplitMyCustomString(const std::string& str){
auto first = str.find_last_of('{');
if(first == std::string::npos) return {};
auto mid = str.find_first_of('|', first);
if(mid == std::string::npos) return {};
auto last = str.find_first_of('}', mid);
if(last == std::string::npos) return {};
return { str.substr(first+1, mid-first-1), str.substr(mid+1, last-mid-1) };
}
For Trimming the spaces:
std::string Trim(const std::string& str){
auto first = str.find_first_not_of(' ');
if(first == std::string::npos) first = 0;
auto last = str.find_last_not_of(' ');
if(last == std::string::npos) last = str.size();
return str.substr(first, last-first+1);
}
Demo

Something like:
unsigned open = str.find("{ ") + 2;
unsigned separator = str.find(" | ");
unsigned close = str.find(" }") - 2;
string strNew1 = str.substr (open, separator - open);
string strNew2 = str.substr (separator + 3, close - separator);

Even though you said that the amount of words to find is fixed I made a little more flexible example using a regular expression. However you could still achieve the same result using Мотяs answer.
std::string s = ("Room -> Subdiv(\"X\", 0.5, 0.5) { sleep | work } : 0.5")
std::regex rgx("\\{((?:\\s*\\w*\\s*\\|?)+)\\}");
std::smatch match;
if (std::regex_search(s, match, rgx) && match.size() == 2) {
// match[1] now contains "sleep | work"
std::istringstream iss(match[1]);
std::string token;
while (std::getline(iss, token, '|')) {
std::cout << trim(token) << std::endl;
}
}
trim removes leading and trailing spaces and the input string could easily be expanded to look like this: "...{ sleep | work | eat }...".
Here is the complete code.

Related

What is the most efficient way to replace \\n with \n

I ask this because I am using SFML strings. sf::String does not insert a new line in the presence of a \n.
I can't seem to figure out a way without using 3/4 STL algorithms.
std::replace_if(str.begin(), str.end(), [](const char&c](return c == '\\n'), '\n'});
does not work. The string remains the same.
I have also tried replacing the \\ occurrence with a temporary, say ~. This works, but when I go to replace the ~ with \, then it adds a \\ instead of a \
I have produced a solution by manually replacing and deleting duplicates after \n insertion :
for (auto it = str.begin(); it != str.end(); ++it) {
if (*it == '\\') {
if (it + 1 != str.end()) {
if (*(it + 1) != 'n') continue;
*it = '\n';
str.erase(it + 1);
}
}
}
You might do:
str = std::regex_replace(str, std::regex(R"(\\n)"), "\n")
Demo
The problem is that '\\n' is not a single character but two characters. So it needs to be stored in a string "\\n". But now std::replace_if doesn't work because it operates on elements and the elements of a std::string are single characters.
You can write a new function to replace sub-strings within a string and call that instead. For example:
std::string& replace_all(std::string& s, std::string const& from, std::string const& to)
{
if(!from.empty())
for(std::string::size_type pos = 0; (pos = s.find(from, pos) + 1); pos += to.size())
s.replace(--pos, from.size(), to);
return s;
}
// ...
std::string s = "a\\nb\\nc";
std::cout << s << '\n';
replace_all(s, "\\n", "\n");
std::cout << s << '\n';

How to remove an entire word in a sentence c++

I want to remove specific word in a sentences and I have tried to breakdown the sentences into word and compare the word but when i call the erase function , the index will update. I have tried another method while it will delete substring in a word which i dont want to. Can anyone help me out a bit? what approach i should use.
input
The house whirled around two or three times and rose slowly through the air.
output
The house whirled around two or three times and rose slowly through air.
here is my prototype of the function
int RemoveWordFromLine(string line, string word)
{
// ==========================
string tmp_str="",spacebar=" ";
int start=0,end=-1;
for(int i=0;i<line.length();i++)
{
if(isspace(line[i])||int(line[i])==44||int(line[i])==46)
{
cout<<tmp_str<<" "<<start<<" "<<end<<endl; // compare
if(tmp_str==word)
{
line.erase(start,end);
}
tmp_str="";
start=i+1;
end=i;
} else
{
tmp_str+=line[i];
end++;
}
}
if(tmp_str==word)
{
line.erase(start,end);
}
cout<<tmp_str<<" "<<start<<" "<<end<<endl; // compare
cout<<line<<endl;
// ==========================
}
ideone's link
You can write your function in the following way:
void RemoveWordFromLine(std::string &line, const std::string &word)
{
auto n = line.find(word);
if (n != std::string::npos)
{
line.erase(n, word.length());
}
}
And use it like:
std::string line("This is a wrong line");
RemoveWordFromLine(line, "wrong");
printf("line is: '%s'\n", line.c_str());
Prints out:
line is: 'This is a line'
You are passing end position instead of length of string to be deleted.
You just need to replace
if(tmp_str==word)
{
line.erase(start,end);
}
with
if(tmp_str==word)
{
line.erase(start,word.length()+1); //+1 to prevent 2 spaces
}
You could also do line.erase(start-1,word.length()+1); if you would like to keep '.' or ',' after deleted word.
With <regex>, you might do:
std::string RemoveWordFromLine(const std::string& line, std::string word)
{
// \s* : extra optional spaces at the start
// \b : word boundary
// Ideally, we should check that `word` doesn't break regex too:
std::regex reg("\\s*\\b" + word + "\\b");
return std::regex_replace(line, reg, "");
}
Demo
Remove multiple occurrences as well with this function
void removeWords(string& s, const string& p) {
size_t n = p.length();
for (size_t i = s.find(p);
i != string::npos;
i = s.find(p))
// take care of subword
if((i==0 && (i+n <= s.length()) && (s[i+n+1] == ' ' || s[i+ n+1] == '.'))|| //first word
(i + n <= s.length() && s[i - 1] == ' ' && (s[i+n]=='\0' ||s[i + n] == '.' || s[i+ n+1] == ' '))) //other word
s.erase(i, n);
}

string::replace not working correctly 100% of the time?

I'm trying to replace every space character with '%20' in a string, and I'm thinking of using the built in replace function for the string class.
Currently, I have:
void replaceSpace(string& s)
{
int len = s.length();
string str = "%20";
for(int i = 0; i < len; i++) {
if(s[i] == ' ') {
s.replace(i, 1, str);
}
}
}
When I pass in the string "_a_b_c_e_f_g__", where the underscores represent space, my output is "%20a%20b%20c%20e_f_g__". Again, underscores represent space.
Why is that the spaces near the beginning of the string are replaced, but the spaces towards the end aren't?
You are making s longer with each replacement, but you are not updating len which is used in the loop condition.
Modifying the string that you are just scanning is like cutting the branch under your feet. It may work if you are careful, but in this case you aren't.
Namely, you take the string len at the beginning but with each replacement your string gets longer and you are pushing the replacement places further away (so you never reach all of them).
The correct way to cut this branch is from its end (tip) towards the trunk - this way you always have a safe footing:
void replaceSpace(string& s)
{
int len = s.length();
string str = "%20";
for(int i = len - 1; i >= 0; i--) {
if(s[i] == ' ') {
s.replace(i, 1, str);
}
}
}
You're growing the string but only looping to its initial size.
Looping over a collection while modifying it is very prone to error.
Here's a solution that doesn't:
void replace(string& s)
{
string s1;
std::for_each(s.begin(),
s.end(),
[&](char c) {
if (c == ' ') s1 += "%20";
else s1 += c;
});
s.swap(s1);
}
As others have already mentioned, the problem is you're using the initial string length in your loop, but the string gets bigger along the way. Your loop never reaches the end of the string.
You have a number of ways to fix this. You can correct your solution and make sure you go to the end of the string as it is now, not as it was before you started looping.
Or you can use #molbdnilo 's way, which creates a copy of the string along the way.
Or you can use something like this:
std::string input = " a b c e f g ";
std::string::size_type pos = 0;
while ((pos = input.find(' ', pos)) != std::string::npos)
{
input.replace(pos, 1, "%20");
}
Here's a function that can make it easier for you:
string replace_char_str(string str, string find_str, string replace_str)
{
size_t pos = 0;
for ( pos = str.find(find_str); pos != std::string::npos; pos = str.find(find_str,pos) )
{
str.replace(pos ,1, replace_str);
}
return str;
}
So if when you want to replace the spaces, try it like this:
string new_str = replace_char_str(yourstring, " ", "%20");
Hope this helps you ! :)

string from after the second space character

I have a string
string str= "Jhon 12345 R333445 3434";
string str1= "Mike 00987 #F54543";
So from str i want "R333445 3434" only because after second space character whatever appear i want all, similarly form str1 "#F54543"
I used stringstream and extract the next word after the space but it wont give correct result for
str ="Jhon 12345 R333445 3434";
it gives R333445 only it should give "R333445 3434"
Please suggest some better logic of my problem.
How about
#include <string>
#include <iostream>
int main()
{
const std::string str = "Jhon 12345 R333445 3434";
size_t pos = str.find(" ");
if (pos == std::string::npos)
return -1;
pos = str.find(" ", pos + 1);
if (pos == std::string::npos)
return -1;
std::cout << str.substr(pos, std::string::npos);
}
Output
R333445 3434
According to http://ideone.com/P1Knbe.
It seems you want to skip the first two words and read the rest, if that is correct you can do something like this.
std::string str("Jhon 12345 R333445 3434"");
std::string tmp, rest;
std::istringstream iss(str);
// Read the first two words.
iss >> tmp >> tmp;
// Read the rest of the line to 'rest'
std::getline(iss,rest);
std::cout << rest;
You could find the index of the second space and then take the substring from one position past it to the end.
int index = 0;
for (int i = 0; i < 2; ++i){
index = (str.find(" ", index)) + 1;
}
ans = str.substr(index);
reference on std::string::find
reference on std::string::substr

Replace 3 or more occurrences of character within string

I would like to find 3 or more occurrences of a within a std::string in order to replace.
For example:
std::string foo = "This is a\n\n\n test";
std::string bar = "This is a\n\n\n\n test";
std::string baz = "This is a\n\n\n\n\n test";
std::string boo = "This is a\n\n\n\n\n\n test";
// ... etc.
Should all be converted to:
std::string expectedResult = "This is a\n\n test";
Vanilla stl would be appreciated (no regexp libs or boost) if possible.
This should find consecutive \n and replace them:
size_type i = foo.find("\n\n\n");
if (i != string::npos) {
size_type j = foo.find_first_not_of('\n', i);
foo.replace(i, j - i, "\n\n");
}
Write a function to process each string you are interested in modifying:
Read each string one character a time. Keep track of 2 char variables: a and b. For each character c you read, do:
if ( a != b ) {
a = b;
b = c;
} else if ( a == b ) {
if ( a == c ) {
// Put code here to remove c from your string at this index
}
}
I am not 100% sure if you can use something from STL directly to accomplish what you are asking, but as you can see this logic isn't much code to implement.
You can use find and replace. (this will replace "\n\n\n..." -> "\n\n"). You can pass position to string::find so that you don't have to search the start of the string again (optimization)
int pos = 0;
while ((pos = s.find ("\n\n\n", pos)) != s.npos)
s.replace (pos, 3, "\n\n", 2);
And this will replace "\n\n\n\n.." -> "\n"
int pos = 0;
while ((pos = s.find ("\n\n", pos)) != s.npos)
s.replace (pos, 2, "\n", 1);