Reputation: 135
I am trying to write a program that eliminates blank spaces using a range based for loop in C++. For eg, if the input is, "what is your name?" , the output should be "Whatisyourname?" however when i run the code below, the output it gives is "Whatisyourname?me?", why is that?
int main()
{
string s = "What is your name?";
int write_index = 0;
for (const char &c : s)
{
if (c != ' ')
{
s[write_index++] = c;
}
}
cout << s << endl;
system("pause");
}
Upvotes: 0
Views: 2045
Reputation: 2253
This is a basic application of the copy_if
algorithm from the standard library.
#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
#include <string>
int main()
{
std::string s = "What is your name?";
std::copy_if(s.begin(), s.end(), std::ostream_iterator<char>(std::cout),
[](char c){ return !std::isspace(c); });
return 0;
}
outputs:
Whatisyourname?
If you actually need to remove them from the original string, then use the algorithm remove_if
followed by erase
.
Upvotes: 0
Reputation: 12104
Now, I don't personally code C++, but this looks eerily similar to a for-each loop
in C#, Java, and JavaScript; so I'll give it a go.
Let's first break down your code to see what's going on
int main() {
// creates a string object which is essentially a glorified array of chars
string s = "What is your name?";
int write_index = 0;
// for evry char "c" in the char-array "s"
for (const char &c : s) {
// if c isn't a space
if (c != ' ') {
// write c to s at index "write_index" then increment "write_index"
s[write_index++] = c;
}
}
std::cout << s << std::endl;
system("pause");
}
The logic seems good, so why does "what is your name?" turn into "whatisyourname?me?"? Simple. Because you're overwriting the existing array.
"what is your name?" is 18 characters long, and since you're only writing a non-space character to the array if it's not a space you're essentially copying characters one space left for every space in your text.
For example here's what happens after you run this code over the first 7 characters: "whatiss your name?", and after the first 12: "whatisyourur name?", and finally after all 18: "whatisyourname?me?". The length of the string never really changes.
So you got a number of options to solve this issue:
Upvotes: 0
Reputation: 275405
Here are two useful little functions:
template<class C, class F>
bool remove_erase_if( C& c, F&& f ) {
using std::begin; using std::end;
auto it = std::remove_if( begin(c), end(c), std::forward<F>(f) );
if ( it == c.end())
return false;
c.erase( it, c.end() );
return true;
}
template<class C, class T>
bool remove_erase( C& c, T&& t ) {
using std::begin; using std::end;
auto it = std::remove( begin(c), end(c), std::forward<T>(t) );
if ( it == c.end())
return false;
c.erase( it, c.end() );
return true;
}
these both take a container, and either a test or an element.
They then remove and erase any elements that pass the test, or equal the element.
Your code emulated the remove
part of the above code, and did not do the erase
part. So the characters remaining at the end ... remained.
remove
(or your code) simply moves all the "kept" data to the front of the container. The stuff left over at the end ... stays there. The erase
step then tells the container that the stuff after the stuff you kept should be discarded. If you don't discard it, it ... remains ... and you get your bug.
With the above two functions, you can do this:
int main() {
std::string s = "What is your name?";
remove_erase( s, ' ' );
std::cout << s << '\n';
}
and you are done.
As an aside, using namespace std;
is often a bad idea. And std::endl
forces a buffer-flush, so I prefer '\n'
. Finally, system("pause")
can be emulated by running your IDE in a mode that leaves you your command window open, instead of adding it to your code Ctrl-F5.
Upvotes: 1
Reputation:
Try this:
#include <string.h>
#include <iostream>
using namespace std;
int main()
{
string s = "What is your name?";
std::string aux(s.size(),' ');
int write_index = 0;
for (const char &c : s)
{
if (c != ' ')
{
aux[write_index++] = c;
}
}
cout << s << endl;
cout << aux << endl;
system("pause");
}
Upvotes: 0
Reputation: 310990
Add after the loop the following statement
s.erase( write_index );
or
s.resize( write_index );
to remove redundant characters from the string.
The general approach to such tasks is the following
#include <algorithm>
#include <string>
//...
s.erase( std::remove( s.begin(), s.end(), ' ' ), s.end() );
Upvotes: 2
Reputation: 1243
The reason for this is because string s
is still as long as the original string, "What is your name?"
. You wrote over top of every character in the string except for the last three. What you could do is erase the last three characters from the string after you're done removing the spaces. This is untested but something like this should work:
s.erase(write_index, s.length() - write_index)
Your range based for loop usage is correct. Just keep in mind that you're looping over all the input characters (as though you were looping with for (int i = 0; i < s.length(); i++)
, but you're not outputting as many characters as you're reading.
So the equivalent for loop would be like this:
for (int i = 0; i < s.length(); i++) {
const char& c = s[i];
if (c != ' ') {
s[write_index++] = c;
}
}
Upvotes: 1
Reputation: 574
You can keep track of the number of spaces you have and resize the string at the end.
int main()
{
string s = "What is your name?";
int length = s.length();
int write_index = 0;
for (const char &c : s)
{
if (c != ' ')
{
s[write_index++] = c;
}
else
{
length -= 1;
}
}
s.resize(length);
cout << s << endl;
}
Upvotes: 0