This question already has answers here:
why does my std::transform retuns nothing/empty string?
(2 answers)
Closed 3 months ago.
Why std::transform doesn't work this way:
std::string tmp = "WELCOME";
std::string out = "";
std::transform(tmp.begin(), tmp.end(), out.begin(), ::tolower);
out is empty!
But this works:
std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
I don't want the transformation to happen in-place.
You are writing in out-of-bounds memory, since the range of out is smaller than that of tmp. You can store the result in out by applying std::back_inserter.
As user17732522 pointed out, since it's not legal to take the adress of a standard libary function, it's better to pass over a lamda object that calls std::tolower on the character when needed.
std::transform(tmp.begin(), tmp.end(), std::back_inserter(out), [](auto c) {
return std::tolower(static_cast<unsigned char>(c));
});
One way is to resize the string to allocate sufficient memory for std::transform to populate it.
out.resize(tmp.size()); // <---
std::transform(tmp.begin(), tmp.end(), out.begin(), ::tolower);
Alternatively, as others have mentioned you could use std::back_inserter, which handles inserting for you. We can also call out.reserve to save time on allocations (especially if your string is large).
out.reserve(tmp.size()); // (optional) Smol optimisation.
std::transform(tmp.begin(), tmp.end(), std::back_inserter(out), ::tolower);
Related
I am using one vector of strings and the strings are not in lower case. I want them to covert to lower case.
For converting string to lower case, I am using following method.
std::transform(strCmdLower.begin(), strCmdLower.end(), strCmdLower.begin(), ::tolower);
I can iterate vector and convert each string but I would like to know is there any library functions available for this purpose like above. Copying to new vector also fine.
std::vector<std::string> v1;
v1.push_back("ABC");
v1.push_back("DCE");
std::vector<std::string> v2(v1.begin(), v1.end());
You can still use std::transform with a lambda, e.g.
std::vector<std::string> v1;
v1.push_back("ABC");
v1.push_back("DCE");
std::vector<std::string> v2;
v2.reserve(v1.size());
std::transform(
v1.begin(),
v1.end(),
std::back_inserter(v2),
[](const std::string& in) {
std::string out;
out.reserve(in.size());
std::transform(in.begin(), in.end(), std::back_inserter(out), ::tolower);
return out;
}
);
LIVE
Just a quick question - Which is better to use to add a string to the end of a vector<string>, back_inserter or push_back? mainly, which works faster(I'm working with a huge data, so the marginal difference is actually important) and what are the main differences?
The two are not equivalent. You use std::back_inserter for example when you need to pass an input iterator to an algorithm. std::vector<std::string>::push_back would not be an option in this case. For example
std::vector<std::string> a(100, "Hello, World");
std::vector<std::string> b;
std::copy(a.begin(), a.end(), std::back_inserter(b));
Short of (the obvious) building a C style string first then using that to create a std::string, is there a quicker/alternative/"better" way to initialize a string from a vector of chars?
Well, the best way is to use the following constructor:
template<class InputIterator> string (InputIterator begin, InputIterator end);
which would lead to something like:
std::vector<char> v;
std::string str(v.begin(), v.end());
I think you can just do
std::string s( MyVector.begin(), MyVector.end() );
where MyVector is your std::vector.
With C++11, you can do std::string(v.data()) or, if your vector does not contain a '\0' at the end, std::string(v.data(), v.size()).
std::string s(v.begin(), v.end());
Where v is pretty much anything iterable. (Specifically begin() and end() must return InputIterators.)
I like Stefan’s answer (Sep 11 ’13) but would like to make it a bit stronger:
If the vector ends with a null terminator, you should not use (v.begin(), v.end()): you should use v.data() (or &v[0] for those prior to C++17).
If v does not have a null terminator, you should use (v.begin(), v.end()).
If you use begin() and end() and the vector does have a terminating zero, you’ll end up with a string "abc\0" for example, that is of length 4, but should really be only "abc".
Just for completeness, another way is std::string(&v[0]) (although you need to ensure your string is null-terminated and std::string(v.data()) is generally to be preferred.
The difference is that you can use the former technique to pass the vector to functions that want to modify the buffer, which you cannot do with .data().
vector<char> vec;
//fill the vector;
std::string s(vec.begin(), vec.end());
I have 2 std::string. I just want to, given the input string:
capitalize every letter
assign the capitalized letter to the output string.
How come this works:
std::string s="hello";
std::string out;
std::transform(s.begin(), s.end(), std::back_inserter(out), std::toupper);
but this doesn't (results in a program crash)?
std::string s="hello";
std::string out;
std::transform(s.begin(), s.end(), out.begin(), std::toupper);
because this works (at least on the same string:
std::string s="hello";
std::string out;
std::transform(s.begin(), s.end(), s.begin(), std::toupper);
There is no space in out. C++ algorithms do not grow their target containers automatically. You must either make the space yourself, or use a inserter adaptor.
To make space in out, do this:
out.resize(s.length());
[edit] Another option is to create the output string with correct size with this constructor.
std::string out(s.length(), 'X');
I'd say that the iterator returned by out.begin() is not valid after a couple of increments for the empty string. After the first ++ it's ==out.end(), then the behavior after the next increment is undefined.
After all this exactly what insert iterator is for.
Thats the sense of a backinserter: It inserts elements to a container. using begin(), you pass a iterator to a empty container and modify invalid iterators.
I am sorry - my edits interfered with your comments. I first posted something wrong accidentally.
I guess this is a simple question. I need to do something like this:
std::set<int> s1, s2;
s1 = getAnExcitingSet();
std::transform(s1.begin(), s1.end(), std::back_inserter(s2), ExcitingUnaryFunctor());
Of course, std::back_inserter doesn't work since there's no push_back.
std::inserter also needs an iterator? I haven't used std::inserter so I'm not sure what to do.
Does anyone have an idea?
Of course, my other option is to use a vector for s2, and then just sort it later. Maybe that's better?
set doesn't have push_back because the position of an element is determined by the comparator of the set. Use std::inserter and pass it .begin():
std::set<int> s1, s2;
s1 = getAnExcitingSet();
transform(s1.begin(), s1.end(),
std::inserter(s2, s2.begin()), ExcitingUnaryFunctor());
The insert iterator will then call s2.insert(s2.begin(), x) where x is the value passed to the iterator when written to it. The set uses the iterator as a hint where to insert. You could as-well use s2.end().
In 2016 there was a proposal to have a "single argument inserter iterator".
https://isocpp.org/files/papers/p0471r0.html . I couldn't find if it the proposal advanced. I think it makes sense.
For now you can have this behavior defining the maker function:
template<class Container>
auto sinserter(Container& c){
using std::end;
return std::inserter(c, end(c));
}
Used as:
std::transform(begin(my_vec), end(my_vec), sinserter(my_set), [](auto& e){return e.member;});