This question already has answers here:
How do you reverse a string in place in C or C++?
(21 answers)
Closed 6 years ago.
Im trying to figure out how to reverse the string temp when I have the string read in binary numbers
istream& operator >>(istream& dat1d, binary& b1)
{
string temp;
dat1d >> temp;
}
I'm not sure what you mean by a string that contains binary numbers. But for reversing a string (or any STL-compatible container), you can use std::reverse(). std::reverse() operates in place, so you may want to make a copy of the string first:
#include <algorithm>
#include <iostream>
#include <string>
int main()
{
std::string foo("foo");
std::string copy(foo);
std::cout << foo << '\n' << copy << '\n';
std::reverse(copy.begin(), copy.end());
std::cout << foo << '\n' << copy << '\n';
}
Try
string reversed(temp.rbegin(), temp.rend());
EDIT: Elaborating as requested.
string::rbegin() and string::rend(), which stand for "reverse begin" and "reverse end" respectively, return reverse iterators into the string. These are objects supporting the standard iterator interface (operator* to dereference to an element, i.e. a character of the string, and operator++ to advance to the "next" element), such that rbegin() points to the last character of the string, rend() points to the first one, and advancing the iterator moves it to the previous character (this is what makes it a reverse iterator).
Finally, the constructor we are passing these iterators into is a string constructor of the form:
template <typename Iterator>
string(Iterator first, Iterator last);
which accepts a pair of iterators of any type denoting a range of characters, and initializes the string to that range of characters.
Related
I was trying to transform a string into lowercase and store it in another variable using std::transform and std::tolower. I first tried:
string str1("Hello");
string lowerStr1;
transform(str1.begin(), str1.end(), lowerStr1.begin(), ::tolower);
cout << lowerStr1 << endl;
But, lowerStr1 contained nothing. After initializing lowerStr1 with str1, I got the desired result. I want to know the intuition behind this. Could someone explain why lowerStr1 should be initialized in this case?
lowerStr1 is empty, and std::transform won't insert elements into it.
std::transform applies the given function to a range and stores the result in another range, beginning at d_first.
You can use std::back_inserter, which constructs a std::back_insert_iterator, which would call push_back() on the container to insert elements.
transform(str1.begin(), str1.end(), back_inserter(lowerStr1), ::tolower);
Or make lowerStr1 containing 5 elements in advance.
string lowerStr1(5, '\0');
transform(str1.begin(), str1.end(), lowerStr1.begin(), ::tolower);
or
string lowerStr1;
lowerStr1.resize(5);
transform(str1.begin(), str1.end(), lowerStr1.begin(), ::tolower);
Could someone explain why lowerStr1 should be initialized in this case?
That's because you initialize lowerStr1 containing 5 elements in advance as above. What's the value of the initialized elements doens't matter in fact.
This is because your call to std::transform is logically equivalent to the following code:
auto b=str1.begin();
auto e=str1.end();
auto p=lowerStr1.begin();
while (b != e)
{
*p=tolower(*b);
++b;
++e;
}
But lowerStr1, is a completely empty string. lowerStr1.begin() gives you, loosely speaking, a pointer to an empty string. So writing to that pointer and, adding insult to injury, incrementing it and continuing to write to it, result in undefined behavior, memory corruption, and a non-trivial possibility of a crash.
You do not add content to an empty string by grabbing a pointer to it, and then scribbling into that pointer. There are several ways of doing that correctly, with push_back() or insert() methods. You can also use an iterator that does that, like a std::back_insert_iterator, which can use with std::transform.
Generic algorithms won't change the size of the containers.
You need to use an iterator adapter which implements operator= in a special way so that it actually insert elements.
Therefore you can use back_inserter(lowerStr1) to make sure that lowerStr1 gets extended as trasform() does assignments.
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
string str1("Hello");
string lowerStr1;
transform(str1.begin(), str1.end(), std::back_inserter(lowerStr1), ::tolower);
cout << lowerStr1 << endl;
}
I need to insert a character into a string of letters that are in alphabetical order, and this character has to be placed where it belongs alphabetically.
For example I have the string string myString("afgjz"); and the input code
cout << "Input your character" << endl;
char ch;
cin >> ch;
but how can I make it so that after inputting the char(say b) it is then added to the string on the proper position resulting in the string becoming "abfgjz".
You can use std::lower_bound to find the position to insert.
myString.insert(std::lower_bound(myString.begin(), myString.end(), ch), ch);
A more generic solution would be having a function like
namespace sorted
{
template<class Container, class T>
void insert(Container & object, T const & value)
{
using std::begin;
using std::end;
object.insert(std::lower_bound(begin(object),
end(object), value), value);
}
}
And then use
sorted::insert(myString, ch);
Class std::string has the following insert method (apart from other its insert methods):
iterator insert(const_iterator p, charT c);
So all what you need is to find the position where the new character has to be inserted. If the string has already the same character then there are two approaches: either the new character is inserted before the existent character in the string and in this case you should use standard algorithm std::lower_bound or the new character is inserted after the existent character in the string and in this case you should use standard algorithm std::upper_bound.
Here is a demonstrative program that shows how this can be done using standard algorithm std::upper_bound. You may substitute it for std::lower_bound if you like. Though in my opinion it is better to insert the new character after existent one because in some situation you can avoid moving characters after the target position that to insert the new character.
#include <iostream>
#include <algorithm>
#include <string>
int main()
{
std::string myString( "afgjz" );
char c = 'b';
myString.insert( std::upper_bound( myString.begin(), myString.end(), c ), c );
std::cout << myString << std::endl;
return 0;
}
The program output is
abfgjz
I've been reading text material to hone my C++ skills and am having trouble with my iterator-string experiment. What I want to do is take a string, iterate through each element in the string, and output each element in the string. However, since I am learning how to use the arrow operator, the compiler is telling me I cannot access the member string::empty. Doesn't the variable aIter have type string::iterator?
#include <iostream>
#include <string>
using namespace std;
int main()
{
string sString("some string!");
for(auto aIter = sString.cbegin();
aIter != sString.cend() && !aIter->empty();
aIter++)
cout << *aIter << endl;
cout << endl;
return 0;
}
Error:
request for member 'empty' in '* aIter.__gnu_cxx::__normal_iterator<_Iterator, _Container>::operator-><const char*, std::basic_string<char> >()', which is of non-class type 'const char'|
You're iterating over the characters of a string. Characters don't have member functions, and have no concept of being "empty".
You probably want to simply remove the !aIter->empty() test, to iterate until you reach the end of the string. This will work even if the string is empty: the cbegin() iterator will equal the cend() iterator, so the loop will end immediately.
If you wanted to check whether the string is empty (which you don't need to do here), that would be sString.empty().
I try to generalize for stream objects with following code:
#include <iostream>
#include <vector>
#include <sstream>
#include <iterator>
using namespace std;
template<class T, class U>
T& operator<<(T& os, vector<U> vec)
{
vector<string>::iterator begin = vec.begin();
vector<string>::iterator end = vec.end();
for (; begin != end; ++begin)
os << *begin << " ";
return os;
}
int main()
{
vector<string> things({
"car", "truck", "rabbit"
});
ostringstream oss;
oss << things << endl;
copy(oss.str().begin(), oss.str().end(), ostream_iterator<char>(cout, ""));
}
Now it work with cout << things and string str = oss.str(); copy(str.begin() ... but not with oss.str().begin(). From what I understand, str() returns a string object with a copy of the current contents of the stream. So why do we need to copy it twice, once from str() and once in initialization of string object? This question is not same as using c_str().
Following also works:
string::iterator begin = oss.str().begin();
string::iterator end = oss.str().end();
copy(begin, end, ostream_iterator<char>(cout, ""));
From what I understand, str() returns a string object with a copy of
the current contents of the stream.
That is correct. And it is the answer to your question. Because str() returns a copy, two calls to oss.str() result in two completely different copies (i.e. two different string objects). The iterators of the two respective string objects are not compatible, since they are not from the same string. It is undefined behavior to pass two iterators from two completely different strings to an algorithm (such as std::copy) which is expecting them to be from the same range.
You wouldn't expect this to work, would you?
std::string str1 = oss.str();
std::string str2 = oss.str();
std::copy(str1.begin(), str2.end(), ostream_iterator<char>(cout, ""));
In response to your edit, "Following also works:". If that works, it is purely by coincidence. Those iterators are both invalid at the end of the statement in which they were created, because the string into which they point no longer exists. Your use of them in the call to std::copy is undefined behavior.
This question already has an answer here:
How do I erase elements from STL containers?
(1 answer)
Closed 8 years ago.
I've this code:
#include <algorithm>
#include <iostream>
#include <list>
using namespace std;
struct P
{
bool operator()(const int &n) const
{
return n % 3 == 0;
}
};
int main()
{
std::list<int> l({ 5, 2, 6, 1, 13, 9, 19 });
std::cout << l.size();
std::remove_if(l.begin(), l.end(), P());
std::cout << l.size() << std::endl;
return 0;
}
prints out "77". I expected it would have printed out "75", because the operator () of the P struct, returns true when its argument has not remainder of the division by 3. And that's the case for '6' and '9' (two elements out of seven).
Am I missing something ?
thanks.
To quote from http://www.cplusplus.com/reference/algorithm/remove_if/
The function cannot alter the properties of the object containing the range of elements (i.e., it cannot alter the size of an array or a container): The removal is done by replacing the elements for which pred returns true by the next element for which it does not, and signaling the new size of the shortened range by returning an iterator to the element that should be considered its new past-the-end element.
In other words, it rearranges the elements in the given range so that all the non-removed ones are at the beginning, then returns an iterator just past the end of the non-removed part. But it can't delete any elements, because it doesn't know anything about the underlying container.
Is it possible std::remove_if returns the resulting list?
remove/remove_if only reorders a sequence, it doesn't modify it. Iterators have no access to or knowledge of the container from which they come. You need to pass the result to a suitable erase container member:
l.erase(std::remove_if(l.begin(), l.end(), P()), l.end());
Don't forget the second l.end() so that you get the two-iterator overload of erase that erases a whole range. If you forget it, you end up with the one-iterator overload that only erases a single element.