Behavior of STL remove() function - only rearrange container elements? - c++

I've read here on StackOveflow and other sources that the behavior of the remove function is simply re-ordering the original container so that the elements that are TO BE REMOVED are moved to the end of the container and ARE NOT deleted. They remain part of the container and the remove() function simply returns an iterator that delimits the end of the range of elements to keep.
So if you never actually trim off the portion of the container that has the values that have been 'removed', they should still be present.
But when I run the code below there are no trailing spaces after the alphanumeric characters that were not 'removed'.
int main()
{
std::string test "this is a test string with a bunch of spaces to remove";
remove(test.begin(), test.end(), ' ');
std::cout << test << std::endl;
return 0;
}
What is going on here? Seeing as I never call test.erase() shouldn't I have a bunch of trailing spaces on my string? Is it guaranteed that the 'removed' items will still be present after calling remove()?
PS-I'm not looking for suggestions on how to best remove spaces from a string, the above is simply an illustrative example of the remove() behavior that is confusing me.

What's left at the end of your container after a call to remove is not necessarily the elements that were removed. It's just junk. Most likely it's "whatever was in those positions before the call to remove", but you can't rely on that either. Much like an uninitialized variable, it could be anything.
For example, the string:
"Hi I am Bob!\0"
after a call to remove to get rid of spaces probably looks like this
"HiIamBob!\0b!\0"
You won't see that on a cout, though, because it will stop printing once it hits the '\0'.

You might be willing to get Boost.String.
It's a collection of algorithms to act on strings.
boost::erase_all(test, " ");

Related

C++ string copy() gives me extra characters at the end, why is that?

I am studying c++, In a blog they introduced the concept of copy function. When I tried the same in my system, the result is not matching to what I expected. Please let me know what I did wrong here in the below code.
#include <iostream>
main(){
std::string statement = "I like to work in Google";
char compName[6];
statement.copy(compName, 6, 18);
std::cout<<compName;
}
I expected Google but actual output is Googlex
I am using windows - (MinGW.org GCC-6.3.0-1)
You are confusing a sequence of characters, C style string, and std::string. Let's break them down:
A sequence of characters is just that, one character after another in some container (in your case a C style array). To a human being several characters may look like a string, but there is nothing in your code to make it such.
C style string is an array of characters terminated by a symbol \0. It is a carry over from C, as such a compiler will assume that if even if you don't tell it otherwise the array of characters may potentially be such a string.
C++ string (std::string) is a template class that stores strings. There is no need to worry how it does so internally. Although there are functions for interoperability with the first two categories, it is a completely different thing.
Now, let's figure out how a compiler sees your code:
char compName[6];
This creates an array of characters with enough space to store 6 symbols. You can write C style strings into it as long as they are 5 symbols or less, since you will need to also write '\0' at the end. Since in C++ C style arrays are unsafe, they will allow you to write more characters into them, but you cannot predict in advance where those extra characters will be written into memory (or even if your program will continue to execute). You can also potentially read more characters from the array... But you cannot even ask the question where that data will be coming from, unless you are simply playing around with your compiler. Never do that in your code.
statement.copy(compName, 6, 18);
This line writes 6 characters. It does not make it into a C style string, it is simply 6 characters in an array.
std::cout<<compName;
You are trying to output to the console a C style string... which you have not provided to a compiler. So a an operator<< receives a char [], and it assumes that you knew what you were doing and works as if you gave it C string. It displays one character after another until it reaches '\0'. When will it get such a character? I have no idea, since you never gave it one. But due to C style arrays being unsafe, it will have no problem trying to read characters past the end of an array, reading some memory blocks and thinking that they are a continuation of your non-existent C style sting.
Here you got "lucky" and you only got a single byte that appeared as an 'x', and then you got a byte with 0 written in it, and the output stopped. If you run your program at a different time, with a different compiler, or compiled with different optimisations you might get a completely different data displayed.
So what should you have done?
You can try this:
#include <iostream>
#include <string>
int main()
{
std::string statement = "I like to work in Google";
char compName[7]{};
statement.copy(compName, 6, 18);
std::cout<<compName;
return 0;
}
What did i change? I made an array able to hold 7 characters (leaving enough space for a C style string of 6 characters) and i have provided an empty initialisation list {}, which will fill the array with \0 characters. This means that when you will replace the first 6 of them with your data, there will be a terminating character in the very end.
Another approach would be to do this:
#include <iostream>
#include <string>
int main()
{
std::string statement = "I like to work in Google";
char compName[7];
auto length = statement.copy(compName, 6, 18);
compName[length] = '\0';
std::cout<<compName;
return 0;
}
Here i do not initialise the array, but i get the length of the data that is written there with a .copy method and then add the needed terminator in the correct position.
What approach is best depends on your particular application.
When inserting pointer to a character into the stream insertion operator, the pointer is required to point to null terminated string.
compName does not contain the null terminator character. Therefore inserting inserting (a pointer to an element of) it into a character stream violates the requirement above.
Please let me know what I did wrong here
You violate the requirement above. As a consequence, the behaviour of your program is undefined.
I expected Google but actual output is Googlex
This is because the behaviour of the program is undefined.
How to terminate it?
Firstly, make sure that there is room in the array for the null terminator character:
char compName[7];
Then, assign the null terminator character:
compName[6] = '\0';

split a string into sequences of consecutive non-whitespace characters

I want to split a string into sequences of consecutive non-whitespace characters. For example, given
std::string test(" 35.3881 12.3637 39.3485");,
I want to obtain an iterator which points to "35.3881", its increment points to "12.3637" and the second increment points to "39.3485".
As in the given example, it's possible (but not guaranteed) that the string starts with an unknown number of whitespaces. Moreover, the number of whitespaces between sequences of non-whitespace characters is also unknown. Trailing whitespaces are possible too.
The following code almost solves my problem:
std::regex regex("\\s+");
std::sregex_token_iterator it(test.cbegin(), test.cend(), regex, -1);
The problem, even with the given example, is that here the iterator it initially points to an empty string; which is not the behavior I desire. How can we fix that?
I'd just use a normal stream_iterator:
std::istringstream test(" 35.3881 12.3637 39.3485");
std::copy(std::istream_iterator<std::string>(test),
{},
std::ostream_iterator<std::string>(std::cout, "\n"));
Result:
35.3881
12.3637
39.3485
If, as noted in the comments, you find it important to avoid copying the data, you can use an istrstream instead of an instringstream, something like this:
#include <strstream>
// ...
std::string test(" 35.3881 12.3637 39.3485");
std::istrstream buffer(test.c_str());
std::copy(std::istream_iterator<std::string>(buffer),
{},
std::ostream_iterator<std::string>(std::cout, "\n"));
Note: <strstream> and everything contains is officially deprecated, so in theory, it could disappear in some future version of the standard. I'd eventually expect to see something based on a string_view, which would also avoid copying the data, but I'm not sure it actually exists yet (it certainly doesn't in the compiler I'm using at the moment).

Can't figure out pointers in C++! Need info about basic strings and pointers

Hello fellow programmers, my university teacher sent me some solved problems in c++, my friend and I can't figure them out. I will write down the parts that we can't understand.
Problem 1:
You must enter a string, which includes spaces ' ' and the code counts the spaces in the string.
Example:
asd123 count = 0
a sd123 count = 1
a s d count = 2
Here is the solution:
typedef char strType[100];
strType str;
cout << "string: ";
cin.getline(str, 100);
cout << "\"" << str << "\"\n";
short count = 0;
char *adr = strchr(str, ' '); //value might be NULL
while (adr)
{
++count;
adr = strchr(adr + 1, ' ');
}
cout << "count: " << count << endl;
We need explanation after short count =0; We can't understand the value of *adr(is it an exact number, or true or false, or whatever type). How can you add +1 to adr, how the while cycle ends. Because if the *adr is something complex (consisting of many data) how could you increase it's value by one(it stores the spaces ' ' occurring in str) and become something which stops the while.
I'm guessing this is the first time you've come across pointers. If you don't quite understand them, I'd highly recommend going and learning about them as they are pretty critical to C/C++ (among other languages).
For the benefit of this question, though, I'll give a very rough definition. A pointer "points" to a specific memory location. Its "type" is used to define how much memory it expects to use at that point. That is, a char* pointer will only expect to handle a char's worth of data, and will only reserve that much memory. A long* will reserve and handle a long's worth of memory (significantly larger, how much more depends on the system, but that's another subject).
so when the code snippet you have says:
char *adr = strchr(str, ' '); //value might be NULL
what is means is: that value of "adr" is a memory location that is the size of type char, which we are setting equal to the result of the library function strchr. We'll get back to strchr in a second.
Now, "str" is what is called a C-Style string. This is because it uses the C method for strings, rather than the newer C++ library string type, which works slightly differently. A C-Style string is literally a chunk of contiguous (no gaps) memory - in this case, 100 chars all laid out one after another in memory. If I want to read the string, what I do is read each part of it one after another, so I go to the start, read a char, then I want to jump a char's worth of memory forward, to the next char. this is what adr++ does - it says "jump me one step forward", one step in this case being a char, because that is the "type" of adr. That may or may not make sense, Mohamad Elghawi posted a link to a tutorial on pointer arithmetic that's also worth reading.
Back to strchr. strchr is a function that searches for a particular character value within a string. In this case, find me ' ' (space) within str. So it goes to the memory location at the start of str, and jumps through it till if finds a space. If it doesn't find a space, it returns NULL - hence the comment in the code.
If it DOES find a space, it will assign the memory location of that space to adr. Then we jump into the while loop. If adr is SOMETHING, then we've found at least one space, so iterate the count. Then we say "ok, the space as adr was the first space, so if there are any more spaces they must be after it, so we do another search for spaces from the memory location one after adr's current value, otherwise we would just pick up the same space again and our count would be wrong. Keep doing this until we get NULL as an answer, adding 1 to our count every time adr is not null. when adr is null, there are no more spaces in the string, so our count is finished. Make sense?
Last question you might have: how does strchr know where the end of the string is? Well, this works because C-Style strings are also called "Null-terminated strings" - they automatically have a \0 (Null) value entry at the end. strchr knows that if it hits this value, it has come to the end of the string.
Lesson for next time: pointers are a pretty basic concept that will be covered thoroughly in tutorials and books all over the internet. The chances are that you could have figured out the answer to this in reasonable time had you looked through them, and looked up what strchr does. However, we were all beginners once and I realise that it's often a bit daunting. Try a little harder to find an existing answer first next time :)
In your example strchr returns the character pointer pointing to the location which has value space in string. If it is a valid pointer while loop allow it to enter loop and increment the value of variable count. add+1 is the pointer arithmetic to reach the next character pointer in location in string after the space character in string.

String::push_back() does not push back

I am currently writing a infix to postfix converter. It works pretty well except I have problems adding the rest of the Stack to the postfix. The stack is a
vector<char>
and the postfix is a String. After I'm done reading everything I try the following:
while (!stack.empty()) {
postfix.push_back(stack.back());
stack.pop_back();
}
But there is nothing appended. I even tried it with Debug-mode and it seems fine, but nothing gets appended.
std::cout << postfix.c_str();
Leaves out the last operator from the stack. I even tried to save it temporary, but it does not get pushed.
I can not post all four files in pastebin because of the limit. I use Visual Studio 2010 Ultimate and there are no errors, just no character appended.
The way you're printing out that string is needlessly complicated - and possibly wrong. If you replace
std::cout << postfix.c_str();
with
std::cout << postfix;
You will then see what's really in the string, even if it contains embedded null characters.
Since you are using '\0' as an error indicator but not checking for it you are likely to have embedded nulls. And by using c_str() you are explicitly asking for the string to be truncated at the first null.

printing using one '\n'

I am pretty sure all of you are familiar with the concept of the Big4, and I have several stuffs to do print in each of the constructor, assignment, destructor, and copy constructor.
The restriction is this:
I CAN'T use more than one newline (e.g., ƒn or std::endl) in any method
I can have a method called print, so I am guessing print is where I will put that precious one and only '\n', my problem is that how can the method print which prints different things on each of the element I want to print in each of the Big4? Any idea? Maybe overloading the Big4?
Maybe I don't understand the question completely because it is asked rather awkwardly, but can't you just have a function called newline that receives an ostream as an argument, and then simply prints '/n' to that output stream? Then you can just call that infinitely many times, while still abiding the arbitrary "one newline" rule.
e.g.
(edit: code removed, "smells like homework")
print should take a parameter containing the information to output to the screen (sans '\n') and then call the c++ output method with in-line appending the '\n' to the passed in information.
note: no code 'cause this smells like homework to me...
I'm not sure I completely understand what you're trying to accomplish. Why is it that you can only use one newline? What makes it difficult to just write your code with only one newline in it? For example, I've done stuff like this before.
for(int i = 0; i < 10; i++) {
cout << i << " ";
}
cout << std::endl;
If you need something more complicated, you might want to make some sort of print tracker object that keeps a flag for whether a newline has been printed, and adjusts its behavior accordingly. This seems like it might be a little overly complicated though.