views:

7432

answers:

10

I'm currently using the following code to right-trim all the std::strings in my programs:

std::string s;
s.erase(s.find_last_not_of(" \n\r\t")+1);

It works fine, but I wonder if there are some end-cases where it might fail?

Of course, answers with elegant alternatives and also left-trim solution are welcome.

+4  A: 

In the case of an empty string, your code assumes that adding 1 to string::npos gives 0. string::npos is of type string::size_type, which is unsigned. Thus, you are relying on the overflow behaviour of addition.

Greg Hewgill
You're phrasing that as if it's bad. *Signed* integer overflow behavior is bad.
MSalters
+29  A: 

Using Boost's string algorithms would be easiest

#include <boost/algorithm/string.hpp>
using namespace std;
using namespace boost;

string str1(" hello world! ");
trim(str1);
Leon Timmermans
What does boost use to determine if a character is whitespace?
Tom
It depends on the locale. My default locale (VS2005, en) means tabs, spaces, carriage returns, newlines, vertical tabs and form feeds are trimmed.
MattyT
+3  A: 

I'm not sure if your environment is the same, but in mine, the empty string case will cause the program to abort. I would either wrap that erase call with an if(!s.empty()) or use Boost as already mentioned.

Steve
+6  A: 

Hacked off of Cplusplus.com

string choppa(const string &t, const string &ws)
{
    string str = t;
    size_t found;
    found = str.find_last_not_of(ws);
    if (found != string::npos)
     str.erase(found+1);
    else
     str.clear();            // str is all whitespace

    return str;
}

This works for the null case as well. :-)

Paul Nathan
+25  A: 

I tend to use one of these 3 for my trimming needs:

// trim from both ends
static inline std::string &trim(std::string &s) {
        return ltrim(rtrim(s));
}

// trim from start
static inline std::string &ltrim(std::string &s) {
        s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
        return s;
}

// trim from end
static inline std::string &rtrim(std::string &s) {
        s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
        return s;
}

They are fairly self explanatory and work very well.

EDIT: btw, I have std::ptr_fun in there to help disambiguate std::isspace because there is actually a second definition which supports locales. This could have been a cast just the same, but I tend to like this better.

Evan Teran
+2  A: 

I've been using the following code to right trim std::strings:

// trim trailing spaces
size_t endpos = str.find_last_not_of(" \t");
if( string::npos != endpos )
{
    str = str.substr( 0, endpos+1 );
}

And just to balance things out, I'll include the left trim code too.

// trim leading spaces
size_t startpos = str.find_first_not_of(" \t");
if( string::npos != startpos )
{
    str = str.substr( startpos );
}
Bill the Lizard
This won't detect other forms of whitespace... newline, line feed, carriage return in particular.
Tom
Right. You have to customize it for the whitespace you're looking to trim. My particular application was only expecting spaces and tabs, but you can add \n\r to catch the others.
Bill the Lizard
+1  A: 

The above methods are great, but sometimes you want to use a combination of functions for what your routine considers to be whitespace. In this case, using functors to combine operations can get messy so I prefer a simple loop I can modify for the trim. Here is a slightly modified trim function copied from the C version here on SO. In this example, I am trimming non alphanumeric characters.

string trim(char const *str)
{
  // Trim leading non-letters
  while(!isalnum(*str)) str++;

  // Trim trailing non-letters
  end = str + strlen(str) - 1;
  while(end > str && !isalnum(*end)) end--;

  return string(str, end+1);
}
Corwin Joy
+1  A: 

Here's what I came up with:

std::stringstream trimmer;
trimmer << str;
trimmer >> str;

Stream extraction eliminates whitespace automatically, so this works like a charm.
Pretty clean and elegant too, if I do say so myself. ;)

tzaman
+5  A: 

I like tzaman's solution, the only problem with it is that it doesn't trim a string containing only spaces.

To correct that 1 flaw, add a str.clear() in between the 2 trimmer lines

std::stringstream trimmer;
trimmer << str;
str.clear();
trimmer >> str;
Michaël Schoonbrood
+1: Good point.
ereOn
Nice :) the problem with both our solutions, though, is that they'll trim both ends; can't make an `ltrim` or `rtrim` like this.
tzaman
A: 

This version trims interal whitespace also

static inline std::string &trimAll(std::string &s)
{

if(s.size() == 0)
{
return s;
}
int val = 0;
for (int cur = 0; cur < s.size(); cur++)
{
if(s[cur] != ' ')
{
s[val] = s[cur];
val++;
}
}
s.resize(val);
return s;
}

Brian