Is there a way to erase two given values with loop? - c++

How do I erase if string sdl1 holds two values like "37". Considering that are two different values 3 and 7. Do I need some sort of list or only loop? Thank you
#include <iostream>
#include <string>
#include <algorithm>
#include <functional>
#include <vector>
#include <conio.h>
#include <sstream>
#include <stdio.h>
#include <iterator>
using namespace std;
void eraseAllSubStr(std::string & mainStr, const std::string & toErase)
{
size_t pos = std::string::npos;
while ((pos = mainStr.find(toErase)) != std::string::npos)
{
mainStr.erase(pos, toErase.length());
}
}
int main()
{
std::string str = "123456789";
//Let's Say I want to delete 5 and 8 and string sdl1 = "58".
string sdl1 = "5";
eraseAllSubStr(str, sdl1);
std::cout << str << std::endl;
return 0;
}
The output of the minimal reprod. example:
12346789
5 was erased.
But I would like to erase two values like 5 and 8 where string sdl1 = "58"

If I understand your actual question:
"What If I want to remove "58" like 5 and 8?"
and you want to provide std::string sdl1 = "58"; and have both 5 and 8 removed from std::string str = "123456789"; in a single loop, then you can simply use std::basic_string::find_first_of to locate the position of either 5 or 8 and then use std::basic_string::erase to remove the character. The only constraint is you only want to attempt the removal of characters while str.find_first_of (sdl1) != std::basic::npos).
A for loop is tailor made for the implementation, e.g.:
std::string str = "123456789";
//Let's Say I want to delete 5 and 8 and string sdl1 = "58".
std::string sdl1 = "58";
for (size_t pos = str.find_first_of (sdl1);
pos != std::string::npos;
pos = str.find_first_of (sdl1))
str.erase (pos, 1);
Putting it altogether in a short example, you could do:
#include <iostream>
#include <string>
int main (void) {
std::string str = "123456789";
//Let's Say I want to delete 5 and 8 and string sdl1 = "58".
std::string sdl1 = "58";
for (size_t pos = str.find_first_of (sdl1);
pos != std::string::npos;
pos = str.find_first_of (sdl1))
str.erase (pos, 1);
std::cout << "str: " << str << '\n';
}
Example Use/Output
$ ./bin/erasechars
str: 1234679

Erase as substring
If you want to erase as a substring, the code you wrote is solution to your problem. Run again your code by putting
std::string str = "123456789";
string sdl1 = "56";
You will get your ouput 1234789.
Once again set the inputs
std::string str = "12345678956123";
string sdl1 = "56";
You will get your output 1234789123
So you've successfully removed all the occurrences of 56 from your main string str.
The way std::string::find() works, it searches the string for the first occurrence of the sequence specified by its arguments. So it will work same for a substring as it works for a single character.
Similarly std::string::erase() erases a part of the string, reducing its length. All you need is to specify the starting index and the length of the substring (i.e. string ::erase (size_type idx, size_type len )) you would like to remove.
So, your code will work for removing all the occurrences of a given substring.
Erase as character
If you want to remove one or more characters from a string like you mentioned 5 and 8. You can use your code with simple modification to your eraseAllSubStr function.
void eraseAllSubStr(std::string & mainStr, const std::string & toErase)
{
size_t pos = std::string::npos;
// erase all the occurrences of the characters that are
// given through toErase string
// and obviously the length of a char is 1
for( int id = 0; id < toErase.size(); id++)
{
while ((pos = mainStr.find(toErase[id])) != std::string::npos)
{
mainStr.erase(pos,1);
}
}
}

Related

To replace one char by a higher amount of chars within a string, without deleting other letters in C++ [duplicate]

This question already has answers here:
Replace char in string with some string inplace
(4 answers)
Closed 4 years ago.
I want to replace character 'ü' in a string if found in it. My code replaces ü, but also deleting other letters in the string.
if (word.find('ü') != string::npos)
{
word.replace(word.find('ü'), 'ü', "ue");
}
Not a one-liner, but erase followed by insert is clear enough.
size_t x = word.find('ü');
while (x != string::npos)
{
word.erase(x, 1);
word.insert(x, "ue");
x = word.find('ü');
}
You can find the position of ü, starting from index 0 until end of the string and whenever you find, replace it with ue using the information of both position where you found and the length of the string you would like to find in the given string.
Something like follows: SEE LIVE HERE
#include <iostream>
#include <string>
#include <algorithm>
#include <cstddef>
int main()
{
std::string word("Herr Müller ist ein König.");
std::string findThis = "ü";
std::string replaceWith = "ue";
std::size_t pos = 0;
while ((pos = word.find(findThis, pos)) != std::string::npos)
{
word.replace(pos, findThis.length(), replaceWith);
pos += replaceWith.length();
}
std::cout << word << std::endl;
return 0;
}
Output:
Herr Mueller ist ein König.
If using boost is an option you can do something like this
#include <boost/algorithm/string.hpp>
int main()
{
std::string str("Herr Müller ist ein König.");
boost::replace_all(str, "ü", "ue");
std::cout << str << std::endl;
return 0
}

C++ STL splitting string at comma

I am aware of several related questions, such as Parsing a comma-delimited std::string one. However, I have created a code that fits my specific need - to split the string (read from a file) at comma stripping any whitespaces. Later I want to convert these substrings to double and store in std::vector. Not all operations are shown. Here is the code I am giving.
include "stdafx.h"
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
int main()
{
std::string str1 = " 0.2345, 7.9 \n", str2;
str1.erase(remove_if(str1.begin(), str1.end(), isspace), str1.end()); //remove whitespaces
std::string::size_type pos_begin = { 0 }, pos_end = { 0 };
while (str1.find_first_of(",", pos_end) != std::string::npos)
{
pos_end = str1.find_first_of(",", pos_begin);
str2 = str1.substr(pos_begin, pos_end- pos_begin);
std::cout << str2 << std::endl;
pos_begin = pos_end+1;
}
}
Output:
0.2345
7.9
So the program goes like this. While loop searches for occurrence of , pos_end will store first occurrence of ,, str2 will be a substring, pos_begin will go to one next to pos_end. First iteration will run fine.
In the next iteration, pos_end will be very large value and I am not sure what pos_end- pos_begin will be. Same goes with pos_begin (though it will be unused). Is making some checks, such as
if (pos_end == std::string::npos)
pos_end = str1.length();
a way to go?
The program works on though (g++ -Wall -Wextra prog.cpp -o prog -std=c++11). Is this approach correct?
Your erase idiom may fail to compile on more modern compilers because isspace is overloaded. At certain point removing whitespaces using range-for might be more effective.
Algorythm in question depends whether you need or not to store tokens and correct "syntax" errors in line and store or not empty token.
#include<iostream>
#include<string>
#include<list>
#include<algorithm>
typedef std::list<std::string> StrList;
void tokenize(const std::string& in, const std::string& delims, StrList& tokens)
{
tokens.clear();
std::string::size_type pos_begin , pos_end = 0;
std::string input = in;
input.erase(std::remove_if(input.begin(),
input.end(),
[](auto x){return std::isspace(x);}),input.end());
while ((pos_begin = input.find_first_not_of(delims,pos_end)) != std::string::npos)
{
pos_end = input.find_first_of(delims,pos_begin);
if (pos_end == std::string::npos) pos_end = input.length();
tokens.push_back( input.substr(pos_begin,pos_end-pos_begin) );
}
}
int main()
{
std::string str = ",\t, 0.2345,, , , 7.9 \n";
StrList vtrToken;
tokenize( str, "," , vtrToken);
int i = 1;
for (auto &s : vtrToken)
std::cout << i++ << ".) " << s << std::endl;
return 0;
}
Output:
1.) 0.2345
2.) 7.9
This variant strips all empty token. Whether is right or not is unknown in your context, so there is no correct answer. If you have to check if string was correct, or if you have replace empty tokens with default values, you have to add additional checks
I use ranges library in c++20 and implement like bellow:
#include <iostream>
#include <ranges>
#include <algorithm>
#include <vector>
auto join_character_in_each_subranges = [](auto &&rng) {
return std::string(&*rng.begin(), std::ranges::distance(rng)); };
auto trimming = std::ranges::views::filter([](auto character){
return !std::isspace(character);});
int main()
{
std::string myline = " 0.2345, 7.9 ";
std::vector<double> line_list;
for (std::string&& words : myline
| std::ranges::views::split(',')
| std::ranges::views::transform(join_character_in_each_subranges))
{
auto words_trimming = words | trimming;
std::string clean_number;
std::ranges::for_each(words_trimming,
[&](auto character){ clean_number += character;});
line_list.push_back(atof(clean_number.c_str()));
}
}
First, iterate on myline sentences and splits the view into subranges on the delimiter
myline | std::ranges::views::split(',')
get each subrange and append each character to each other and view into the std::string with transform function
std::transform applies the given function to a range and stores the result in another range.
std::ranges::views::transform(join_character_in_each_subranges)
also, remove any prefix and suffix from view ranges
auto words_trimming = words | trimming;
and convert view ranges to std::string with
std::ranges::for_each(words_trimming, [&](auto character){ clean_number += character;});
finally, convert each clean_number to double and push_back into the list.
line_list.push_back(atof(clean_words.c_str()));

How can I extract a string sperated by at most one space from an array of chars? (C++)

I have an array of characters that I pass to this function that I read in from a file. They look like this:
7270706PRRío Cañas Abajo 1185 0.238885 18.037675 -66.46701029126 2.6966
I want to extract the chars signifying the name of a place from the 9th position of this char array up until the stream of spaces (so in this case "Canas Abajo"). To do this, I tried, calling with a = 9 and stop = 40:
string getPlaceName(const char (&in) [110], int a, int stop){
char ext[48];
int p =0; int sig = 0;
string toret;
for(a, p; a < stop; a+=1, p+=1)
ext[p] = in[a];
char * v = strtok(ext, " ");
while (v) {
if(sig == 0)
{string q = (v); toret = q; sig= 1;}
else
{string l = (v); toret = toret +" " +l;}
v = strtok(NULL, " ");
}
cout<<"|"<<toret<<"|"<<endl;
return toret;
}
This works for files where the last letter of the string I'm trying to extract is at the end of the line, yet in the example string given, it is returning a jumble of random characters after the name and implies there is no proper null termination. This happens with and without the string q = (v) and string l = (v). What is wrong with my attempt at combining the string and returning?
Tokenize the string, use strtok(). You can also use std::string::find() method. You can also use std::copy() algorithm. stream iterators are used to copy the stream to the vector as whitespace separated strings.
Use string::substr() to extract a substring from - to position.
char myString[] = "7270706PRRío Cañas Abajo 1185 0.238885 18.037675 -66.46701029126 2.6966";
char *p = strtok(myString, " ");
while (p) {
printf ("Token: %s\n", p);
p = strtok(NULL, " ");
}
OR
#include <string>
#include <vector>
#include <iostream>
#include <istream>
#include <ostream>
#include <iterator>
#include <sstream>
#include <algorithm>
int main()
{
std::string str = "7270706PRRío Cañas Abajo 1185 0.238885 18.037675 -66.46701029126 2.6966";
std::stringstream strstr(str);
std::istream_iterator<std::string> it(strstr);
std::istream_iterator<std::string> end;
std::vector<std::string> results(it, end);
std::ostream_iterator<std::string> oit(std::cout);
std::copy(results.begin(), results.end(), oit);
}
I've used a method for this before:
std::vector<std::string> Split(const std::string& text, const char separator)
{
std::vector<std::string> tokens;
int start = 0;
int end = 0;
while ((end = text.find(separator, start)) != std::string::npos)
{
tokens.push_back(text.substr(start, end - start));
start = end + 1;
}
tokens.push_back(text.substr(start));
return tokens;
}
Use this to get a list of tokens and then select the 1st and 2nd index.

Splitting a string into multiple strings with multiple delimiters without removing?

I use boost framework, so it could be helpful, but I haven't found a necessary function.
For usual fast splitting I can use:
string str = ...;
vector<string> strs;
boost::split(strs, str, boost::is_any_of("mM"));
but it removes m and M characters.
I also can't siply use regexp because it searches the string for the longest value which meets a defined pattern.
P.S. There are a lot of similar questions, but they describe this implementation in other programming languages only.
Untested, but rather than using vector<string>, you could try a vector<boost::iterator_range<std::string::iterator>> (so you get a pair of iterators to the main string for each token. Then iterate from (start of range -1 [as long as start of range is not begin() of main string], to end of range)
EDIT: Here is an example:
#include <iostream>
#include <string>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/range/iterator_range.hpp>
int main(void)
{
std::string str = "FooMBarMSFM";
std::vector<boost::iterator_range<std::string::iterator>> tokens;
boost::split(tokens, str, boost::is_any_of("mM"));
for(auto r : tokens)
{
std::string b(r.begin(), r.end());
std::cout << b << std::endl;
if (r.begin() != str.begin())
{
std::string bm(std::prev(r.begin()), r.end());
std::cout << "With token: [" << bm << "]" << std::endl;
}
}
}
Your need is beyond the conception of split. If you want to keep 'm or M', you could write a special split by strstr, strchr,strtok or find function. You could change some code to produce a flexible split function.
Here is an example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void split(char *src, const char *separator, char **dest, int *num)
{
char *pNext;
int count = 0;
if (src == NULL || strlen(src) == 0) return;
if (separator == NULL || strlen(separator) == 0) return;
pNext = strtok(src,separator);
while(pNext != NULL)
{
*dest++ = pNext;
++count;
pNext = strtok(NULL,separator);
}
*num = count;
}
Besides, you could try boost::regex.
My current solution is the following (but it is not universal and looks like too complex).
I choose one character which couldn't appear in this string. In my case it is '|'.
string str = ...;
vector<string> strs;
boost::split(strs, str, boost::is_any_of("m"));
str = boost::join(strs, "|m");
boost::split(strs, str, boost::is_any_of("M"));
str = boost::join(strs, "|M");
if (boost::iequals(str.substr(0, 1), "|") {
str = str.substr(1);
}
boost::split(strs, str, boost::is_any_of("|"));
I add "|" before each of symbols m/M, except of the very first position in string. Then I split the string into substrings with deleting of this extra character

set<string>: how to list not strings starting with given string and ending with `/`?

for example we have in our set:
bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog
bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog
bin/obj/Debug/vc100.idb
bin/obj/Debug/vc100.pdb
So this is what I tried based on this grate answer:
#include <iostream>
#include <algorithm>
#include <set>
#include <string>
#include <iterator>
using namespace std;
struct get_pertinent_part
{
const std::string given_string;
get_pertinent_part(const std::string& s)
:given_string(s)
{
}
std::string operator()(const std::string& s)
{
std::string::size_type first = 0;
if (s.find(given_string) == 0)
{
first = given_string.length() + 1;
}
std::string::size_type count = std::string::npos;
std::string::size_type pos = s.find_last_of("/");
if (pos != std::string::npos && pos > first)
{
count = pos + 1 - first;
}
return s.substr(first, count);
}
};
void directory_listning_without_directories_demo()
{
set<string> output;
set<string> demo_set;
demo_set.insert("file1");
demo_set.insert("file2");
demo_set.insert("folder/file1");
demo_set.insert("folder/file2");
demo_set.insert("folder/folder/file1");
demo_set.insert("folder/folder/file2");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
demo_set.insert("bin/obj/Debug/vc100.idb");
demo_set.insert("bin/obj/Debug/vc100.pdb");
std::transform(demo_set.begin(),
demo_set.end(),
std::inserter(output, output.end()),
get_pertinent_part("bin/obj/Debug/"));
std::copy(output.begin(),
output.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
int main()
{
directory_listning_without_directories_demo();
cin.get();
return 0;
}
This outputs:
CloudServerPrototype/
file1
file2
folder/
folder/folder/
vc100.idb
vc100.pdb
and we are given with bin/obj/Debug/string. We want to cout:
vc100.idb
vc100.pdb
CloudServerPrototype/
How to do such thing?
Quick example of what you want to do.
String.find(): http://www.cplusplus.com/reference/string/string/find/
String.subStr(): http://www.cplusplus.com/reference/string/string/substr/
string str = "bin/obj/Debug/vc100.pdb";
string checkString ("bin/obj/Debug");
// Check if string starts with the check string
if (str.find(checkString) == 0){
// Check if last letter if a "/"
if(str.substr(str.length()-1,1) == "/"){
// Output strating at the end of the check string and for
// the differnce in the strings.
cout << str.substr(checkString.length(), (str.length() - checkString.length()) ) << endl;
}
}
It's not clear with which part of the problem you are stuck, so here is a starter for you.
To get the parts of the strings between "given string" and the final '/' (where present):
std::string get_pertinent_part(const std::string& s)
{
std::string::size_type first = 0;
if (s.find(given_string) == 0)
{
first = given_string.length() + 1;
}
std::string::size_type count = std::string::npos;
std::string::size_type pos = s.find_last_of("/");
if (pos != std::string::npos && pos > first)
{
count = pos + 1 - first;
}
return s.substr(first, count);
}
To insert these parts into a new set (output) to guarantee uniqueness you can use the following:
std::transform(your_set.begin(),
your_set.end(),
std::inserter(output, output.end()),
get_pertinent_part);
You may wish to pass given_string into get_pertinent_part(), in which case you'll need to convert it to a functor:
struct get_pertinent_part
{
const std::string given_string;
get_pertinent_part(const std::string& s)
:given_string(s)
{
}
std::string operator()(const std::string& s)
{
std::string::size_type first = 0;
//
// ...same code as before...
//
return s.substr(first, count);
}
};
You can then call it this way:
std::transform(your_set.begin(),
your_set.end(),
std::inserter(output, output.end()),
get_pertinent_part("bin/obj/Debug"));
To output the new set:
std::copy(output.begin(),
output.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
Sorting the results is left as an exercise.
The easiest way I can think of, using the standard C functions, would be:
char * string1 = "bin/obj/Debug"
char * string2 = "bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog"
char result[64];
// the above code is just to bring the strings into this example
char * position = strstr(string1, string2);
int substringLength;
if(position != NULL){
position += strlen(string2);
substringLength = strchr(position, '/') - position;
strncpy(result, position, substringLength);
}else{
strcpy(result, string1); // this case is for when your first string is not found
}
cout << result;
The first thing that occurs, is finding the substring, string1, in the string we are analyzing, being string2. Once we found the starting point, and assuming it was there at all, we add the length of that substring to that starting point using pointer arithmatic, and then find the resulting string's length by subtracting the starting position from the ending position, which is found with strchr(position, '/'). Then we simply copy that substring into a buffer and it's there to print with cout.
I am sure there is a fancy way of doing this with std::string, but I'll leave that to anyone who can better explain c++ strings, I never did manage to get comfortable with them, haha