As I thought about this question on SO, I realized that I wanted to implement either a string trimmer or a trimmed string inputter.
This is what I came up with, but I have a nagging feeling I could have done better.
Would you critique this, please?
#include <string>
#include <iostream>
#include <sstream>
#include <cassert>
template <typename Ch, typename Tr, typename Alloc>
void
trim(std::basic_string<Ch, Tr, Alloc>& str, const std::locale& loc)
{
// (b,e)=ltrim(str)
typename std::basic_string<Ch, Tr, Alloc>::const_iterator b = str.begin();
typename std::basic_string<Ch, Tr, Alloc>::const_iterator e = str.end();
while( b != e && std::isspace(*b, loc) )
b++;
// (b,e)=rtrim(b,e)
// Query: this loop looks u-g-l-y. Can it be less ugly?
while( b != e ) {
e--;
if(!std::isspace(*e, loc)) {
e++;
break;
}
}
// str=(b,e)
// Query: this string self-assigns (sort of). Is this UB?
str.assign(b, e);
}
template <typename Ch, typename Tr, typename Alloc>
std::basic_istream<Ch,Tr>&
getTrimmedLine(std::basic_istream<Ch,Tr>& is,
std::basic_string<Ch, Tr, Alloc>& str)
{
// Get raw data from input
std::getline(is, str);
// And trim it
trim(str, is.getloc());
return is;
}
#define TEST
#ifdef TEST
template<typename T, std::size_t N>
std::size_t countof( T (& a)[N] ) { return N; }
int main () {
const char * const inputs[] = {
"simple", " front", "back ", " both ",
" front2", "back2 ", " both2 ",
"\n", "", "\n\n\n",
" \n", " ", " \n\n\n"
};
const char * const outputs[] = {
"simple", "front", "back", "both",
"front2", "back2", "both2",
"", "", "",
"", "", ""
};
assert(countof(inputs) == countof(outputs));
for(std::size_t i = 0; i < countof(inputs); i++) {
std::istringstream is(inputs[i]);
std::string result;
getTrimmedLine(is, result);
assert(result == outputs[i]);
}
}
#endif