I want to write a method that should trim a std::string's ongoing and trailing white spaces. For example, if the method gets " HELLO WORLD ", it should return "HELLO WORLD". Note, that between the return string is a white space, that is important.
This is the signature of my method:
std::string MyHandler::Trim(const std::string& toTrim)
My approach was to copy the 'toTrim' parameter into a non constant copy.
std::string toTrimCopy = toTrim;
Now I want to get a non constant iterator and erase within a for loop any whitespaces from beginning and the end, while the iterators value is a white space.
for (std::string::iterator it = toTrim.begin(); *it == ' '; it++)
{
toTrimCopy.erase(it);
}
for (std::string::iterator it = toTrim.end();
*it == ' ';
it--)
{
toTrimCopy.erase(it);
}
This results in the compiler error:
StringHandling.C:60:49: error: conversion from ‘std::basic_string<char>::const_iterator {aka __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >}’ to non-scalar type ‘std::basic_string<char>::iterator {aka __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >}’ requested
I come from Java and I learn C++ for 3 weeks now. So dont judge me. I suspect, that the =
assignment assigns constant char pointer to my new string, so that my copy is implicit a constant value. But I do not exactly know.
By the way, this approach throws an exception as well:
std::string toTrimCopy = "";
std::strcpy(toTrimCopy, toTrim);
He says, that he cant convert a string to a char pointer.
It's undefined behaviour to pass an iterator from toTrim
to methods of toTrimCopy
, so you are lucky that the type mismatch caught it.
std::string::begin
has two overloads:
std::string::iterator std::string::begin();
std::string::const_iterator std::string::begin() const;
The const
ness of the string participates in overload resolution. You can't initialise an iterator
from a const_iterator
, because that would allow you to modify the underlying object through that iterator.
I would change your function to
namespace MyHandlerNS { // optional, could be in global namespace
std::string Trim(std::string toTrim) {
for (auto it = toTrim.begin(); it != toTrim.end() && *it == ' ';) {
it = toTrim.erase(it);
}
for (auto it = toTrim.rbegin(); it != toTrim.rend() && *it == ' ';) {
it = toTrim.erase(it.base());
}
return toTrim;
}
}
Or get rid of the loops entirely with standard algorithms
#include <algorithm>
namespace MyHandlerNS { // optional, could be in global namespace
std::string Trim(std::string toTrim) {
auto isSpace = [](char c){ return c == ' '; };
auto it = std::find_if_not(toTrim.begin(), toTrim.end(), isSpace);
toTrim.erase(toTrim.begin(), it);
it = std::find_if_not(toTrim.rbegin(), toTrim.rend(), isSpace).base();
toTrim.erase(it, toTrim.end());
return toTrim;
}
}