Removing leading and trailing spaces from a string

C++String

C++ Problem Overview


How to remove spaces from a string object in C++.
For example, how to remove leading and trailing spaces from the below string object.

//Original string: "         This is a sample string                    "
//Desired string: "This is a sample string"

The string class, as far as I know, doesn't provide any methods to remove leading and trailing spaces.

To add to the problem, how to extend this formatting to process extra spaces between words of the string. For example,

// Original string: "          This       is         a sample   string    " 
// Desired string:  "This is a sample string"  

Using the string methods mentioned in the solution, I can think of doing these operations in two steps.

  1. Remove leading and trailing spaces.

  2. Use find_first_of, find_last_of, find_first_not_of, find_last_not_of and substr, repeatedly at word boundaries to get desired formatting.

C++ Solutions


Solution 1 - C++

This is called trimming. If you can use Boost, I'd recommend it.

Otherwise, use find_first_not_of to get the index of the first non-whitespace character, then find_last_not_of to get the index from the end that isn't whitespace. With these, use substr to get the sub-string with no surrounding whitespace.

In response to your edit, I don't know the term but I'd guess something along the lines of "reduce", so that's what I called it. :) (Note, I've changed the white-space to be a parameter, for flexibility)

#include <iostream>
#include <string>

std::string trim(const std::string& str,
                 const std::string& whitespace = " \t")
{
    const auto strBegin = str.find_first_not_of(whitespace);
    if (strBegin == std::string::npos)
        return ""; // no content

    const auto strEnd = str.find_last_not_of(whitespace);
    const auto strRange = strEnd - strBegin + 1;

    return str.substr(strBegin, strRange);
}

std::string reduce(const std::string& str,
                   const std::string& fill = " ",
                   const std::string& whitespace = " \t")
{
    // trim first
    auto result = trim(str, whitespace);

    // replace sub ranges
    auto beginSpace = result.find_first_of(whitespace);
    while (beginSpace != std::string::npos)
    {
        const auto endSpace = result.find_first_not_of(whitespace, beginSpace);
        const auto range = endSpace - beginSpace;

        result.replace(beginSpace, range, fill);

        const auto newStart = beginSpace + fill.length();
        beginSpace = result.find_first_of(whitespace, newStart);
    }

    return result;
}

int main(void)
{
    const std::string foo = "    too much\t   \tspace\t\t\t  ";
    const std::string bar = "one\ntwo";

    std::cout << "[" << trim(foo) << "]" << std::endl;
    std::cout << "[" << reduce(foo) << "]" << std::endl;
    std::cout << "[" << reduce(foo, "-") << "]" << std::endl;

    std::cout << "[" << trim(bar) << "]" << std::endl;
}

Result:

[too much               space]  
[too much space]  
[too-much-space]  
[one  
two]  

Solution 2 - C++

Easy removing leading, trailing and extra spaces from a std::string in one line

value = std::regex_replace(value, std::regex("^ +| +$|( ) +"), "$1");

removing only leading spaces

value.erase(value.begin(), std::find_if(value.begin(), value.end(), std::bind1st(std::not_equal_to<char>(), ' ')));

or

value = std::regex_replace(value, std::regex("^ +"), "");

removing only trailing spaces

value.erase(std::find_if(value.rbegin(), value.rend(), std::bind1st(std::not_equal_to<char>(), ' ')).base(), value.end());

or

value = std::regex_replace(value, std::regex(" +$"), "");

removing only extra spaces

value = regex_replace(value, std::regex(" +"), " ");

Solution 3 - C++

I am currently using these functions:

// trim from left
inline std::string& ltrim(std::string& s, const char* t = " \t\n\r\f\v")
{
	s.erase(0, s.find_first_not_of(t));
	return s;
}

// trim from right
inline std::string& rtrim(std::string& s, const char* t = " \t\n\r\f\v")
{
	s.erase(s.find_last_not_of(t) + 1);
	return s;
}

// trim from left & right
inline std::string& trim(std::string& s, const char* t = " \t\n\r\f\v")
{
	return ltrim(rtrim(s, t), t);
}

// copying versions

inline std::string ltrim_copy(std::string s, const char* t = " \t\n\r\f\v")
{
	return ltrim(s, t);
}

inline std::string rtrim_copy(std::string s, const char* t = " \t\n\r\f\v")
{
	return rtrim(s, t);
}

inline std::string trim_copy(std::string s, const char* t = " \t\n\r\f\v")
{
	return trim(s, t);
}

Solution 4 - C++

Boost string trim algorithm

#include <boost/algorithm/string/trim.hpp>

[...]

std::string msg = "   some text  with spaces  ";
boost::algorithm::trim(msg);

Solution 5 - C++

This is my solution for stripping the leading and trailing spaces ...

std::string stripString = "  Plamen     ";
while(!stripString.empty() && std::isspace(*stripString.begin()))
	stripString.erase(stripString.begin());

while(!stripString.empty() && std::isspace(*stripString.rbegin()))
	stripString.erase(stripString.length()-1);

The result is "Plamen"

Solution 6 - C++

Here is how you can do it:

std::string & trim(std::string & str)
{
   return ltrim(rtrim(str));
}

And the supportive functions are implemeted as:

std::string & ltrim(std::string & str)
{
  auto it2 =  std::find_if( str.begin() , str.end() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
  str.erase( str.begin() , it2);
  return str;	
}

std::string & rtrim(std::string & str)
{
  auto it1 =  std::find_if( str.rbegin() , str.rend() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
  str.erase( it1.base() , str.end() );
  return str;	
}

And once you've all these in place, you can write this as well:

std::string trim_copy(std::string const & str)
{
   auto s = str;
   return ltrim(rtrim(s));
}

Solution 7 - C++

Example for trim leading and trailing spaces following jon-hanson's suggestion to use boost (only removes trailing and pending spaces):

#include <boost/algorithm/string/trim.hpp>

std::string str = "   t e s t    ";
 
boost::algorithm::trim ( str );

Results in "t e s t"

There is also

  • trim_left results in "t e s t "
  • trim_right results in " t e s t"

Solution 8 - C++

C++17 introduced std::basic_string_view, a class template that refers to a constant contiguous sequence of char-like objects, i.e. a view of the string. Apart from having a very similar interface to std::basic_string, it has two additional functions: remove_prefix(), which shrinks the view by moving its start forward; and remove_suffix(), which shrinks the view by moving its end backward. These can be used to trim leading and trailing space:

#include <string_view>
#include <string>

std::string_view ltrim(std::string_view str)
{
    const auto pos(str.find_first_not_of(" \t\n\r\f\v"));
    str.remove_prefix(std::min(pos, str.length()));
    return str;
}

std::string_view rtrim(std::string_view str)
{
    const auto pos(str.find_last_not_of(" \t\n\r\f\v"));
    str.remove_suffix(std::min(str.length() - pos - 1, str.length()));
    return str;
}

std::string_view trim(std::string_view str)
{
    str = ltrim(str);
    str = rtrim(str);
    return str;
}

int main()
{
    std::string str = "   hello world   ";
    auto sv1{ ltrim(str) };  // "hello world   "
    auto sv2{ rtrim(str) };  // "   hello world"
    auto sv3{ trim(str) };   // "hello world"

    //If you want, you can create std::string objects from std::string_view objects
    std::string s1{ sv1 };
    std::string s2{ sv2 };
    std::string s3{ sv3 };
}

Note: the use of std::min to ensure pos is not greater than size(), which happens when all characters in the string are whitespace and find_first_not_of returns npos. Also, std::string_view is a non-owning reference, so it's only valid as long as the original string still exists. Trimming the string view has no effect on the string it is based on.

Solution 9 - C++

/// strip a string, remove leading and trailing spaces
void strip(const string& in, string& out)
{
    string::const_iterator b = in.begin(), e = in.end();
    
    // skipping leading spaces
    while (isSpace(*b)){
        ++b;
    }
    
    if (b != e){
        // skipping trailing spaces
        while (isSpace(*(e-1))){
            --e;
        }
    }

    out.assign(b, e);
}

In the above code, the isSpace() function is a boolean function that tells whether a character is a white space, you can implement this function to reflect your needs, or just call the isspace() from "ctype.h" if you want.

Solution 10 - C++

Example for trimming leading and trailing spaces

std::string aString("    This is a string to be trimmed   ");
auto start = aString.find_first_not_of(' ');
auto end = aString.find_last_not_of(' ');
std::string trimmedString;
trimmedString = aString.substr(start, (end - start) + 1);

OR

trimmedSring = aString.substr(aString.find_first_not_of(' '), (aString.find_last_not_of(' ') - aString.find_first_not_of(' ')) + 1);

Solution 11 - C++

Using the standard library has many benefits, but one must be aware of some special cases that cause exceptions. For example, none of the answers covered the case where a C++ string has some Unicode characters. In this case, if you use the function isspace, an exception will be thrown.

I have been using the following code for trimming the strings and some other operations that might come in handy. The major benefits of this code are: it is really fast (faster than any code I have ever tested), it only uses the standard library, and it never causes an exception:

#include <string>
#include <algorithm>
#include <functional>
#include <locale>
#include <iostream>

typedef unsigned char BYTE;

std::string strTrim(std::string s, char option = 0)
{
	// convert all whitespace characters to a standard space
	std::replace_if(s.begin(), s.end(), (std::function<int(BYTE)>)::isspace, ' ');

	// remove leading and trailing spaces
	size_t f = s.find_first_not_of(' ');
	if (f == std::string::npos)	return "";
	s = s.substr(f, s.find_last_not_of(' ') - f + 1);

	// remove consecutive spaces
	s = std::string(s.begin(), std::unique(s.begin(), s.end(),
		[](BYTE l, BYTE r){ return l == ' ' && r == ' '; }));

	switch (option)
	{
	case 'l':  // convert to lowercase
		std::transform(s.begin(), s.end(), s.begin(), ::tolower);
		return s;
	case 'U':  // convert to uppercase
		std::transform(s.begin(), s.end(), s.begin(), ::toupper);
		return s;
	case 'n':  // remove all spaces
		s.erase(std::remove(s.begin(), s.end(), ' '), s.end());
		return s;
	default: // just trim
		return s;
	}
}

Solution 12 - C++

This might be the simplest of all.

You can use string::find and string::rfind to find whitespace from both sides and reduce the string.

void TrimWord(std::string& word)
{
    if (word.empty()) return;

    // Trim spaces from left side
    while (word.find(" ") == 0)
    {
        word.erase(0, 1);
    }

    // Trim spaces from right side
    size_t len = word.size();
    while (word.rfind(" ") == --len)
    {
        word.erase(len, len + 1);
    }
}

Solution 13 - C++

> To add to the problem, how to extend this formatting to process extra spaces between words of the string.

Actually, this is a simpler case than accounting for multiple leading and trailing white-space characters. All you need to do is remove duplicate adjacent white-space characters from the entire string.

The predicate for adjacent white space would simply be:

auto by_space = [](unsigned char a, unsigned char b) {
    return std::isspace(a) and std::isspace(b);
};

and then you can get rid of those duplicate adjacent white-space characters with std::unique, and the erase-remove idiom:

// s = "       This       is       a sample   string     "  
s.erase(std::unique(std::begin(s), std::end(s), by_space), 
        std::end(s));
// s = " This is a sample string "  

This does potentially leave an extra white-space character at the front and/or the back. This can be removed quite easily:

if (std::size(s) && std::isspace(s.back()))
    s.pop_back();

if (std::size(s) && std::isspace(s.front()))
    s.erase(0, 1);

Here's a demo.

Solution 14 - C++

I've tested this, it all works. So this method processInput will just ask the user to type something in. it will return a string that has no extra spaces internally, nor extra spaces at the begining or the end. Hope this helps. (also put a heap of commenting in to make it simple to understand).

you can see how to implement it in the main() at the bottom

#include <string>
#include <iostream>

string processInput() {
  char inputChar[256];
  string output = "";
  int outputLength = 0;
  bool space = false;
  // user inputs a string.. well a char array
  cin.getline(inputChar,256);
  output = inputChar;
       string outputToLower = "";
  // put characters to lower and reduce spaces
  for(int i = 0; i < output.length(); i++){
    // if it's caps put it to lowercase
    output[i] = tolower(output[i]);
    // make sure we do not include tabs or line returns or weird symbol for null entry array thingy
    if (output[i] != '\t' && output[i] != '\n' && output[i] != 'Ì') {
      if (space) {
        // if the previous space was a space but this one is not, then space now is false and add char
        if (output[i] != ' ') {
          space = false;
          // add the char
          outputToLower+=output[i];
        }
      } else {
        // if space is false, make it true if the char is a space
        if (output[i] == ' ') {
          space = true;
        }
        // add the char
        outputToLower+=output[i];
      }
    }
  }
  // trim leading and tailing space
  string trimmedOutput = "";
  for(int i = 0; i < outputToLower.length(); i++){
    // if it's the last character and it's not a space, then add it
    // if it's the first character and it's not a space, then add it
    // if it's not the first or the last then add it
    if (i == outputToLower.length() - 1 && outputToLower[i] != ' ' || 
      i == 0 && outputToLower[i] != ' ' || 
      i > 0 && i < outputToLower.length() - 1) {
      trimmedOutput += outputToLower[i];
    } 
  }
  // return
  output = trimmedOutput;
  return output;
}

int main() {
  cout << "Username: ";
  string userName = processInput();
  cout << "\nModified Input = " << userName << endl;
}

Solution 15 - C++

Why complicate?

std::string removeSpaces(std::string x){
    if(x[0] == ' ') { x.erase(0, 1); return removeSpaces(x); }
    if(x[x.length() - 1] == ' ') { x.erase(x.length() - 1, x.length()); return removeSpaces(x); }
    else return x;
}

This works even if boost was to fail, no regex, no weird stuff nor libraries.

EDIT: Fix for M.M.'s comment.

Solution 16 - C++

No boost, no regex, just the string library. It's that simple.

string trim(const string s) { // removes whitespace characters from beginnig and end of string s
	const int l = (int)s.length();
	int a=0, b=l-1;
	char c;
	while(a<l && ((c=s.at(a))==' '||c=='\t'||c=='\n'||c=='\v'||c=='\f'||c=='\r'||c=='\0')) a++;
	while(b>a && ((c=s.at(b))==' '||c=='\t'||c=='\n'||c=='\v'||c=='\f'||c=='\r'||c=='\0')) b--;
	return s.substr(a, 1+b-a);
}

Solution 17 - C++

The constant time and space complexity for removing leading and trailing spaces can be achieved by using pop_back() function in the string. Code looks as follows:

void trimTrailingSpaces(string& s) {
    while (s.size() > 0 && s.back() == ' ') {
        s.pop_back();
    }
}

void trimSpaces(string& s) {
    //trim trailing spaces.
    trimTrailingSpaces(s);
    //trim leading spaces
    //To reduce complexity, reversing and removing trailing spaces 
    //and again reversing back
    reverse(s.begin(), s.end());
    trimTrailingSpaces(s);
    reverse(s.begin(), s.end());
}

Solution 18 - C++

    char *str = (char*) malloc(50 * sizeof(char));
    strcpy(str, "    some random string (<50 chars)  ");

    while(*str == ' ' || *str == '\t' || *str == '\n')
            str++;

    int len = strlen(str);

    while(len >= 0 && 
            (str[len - 1] == ' ' || str[len - 1] == '\t' || *str == '\n')
    {
            *(str + len - 1) = '\0';
            len--;
    }

    printf(":%s:\n", str);

Solution 19 - C++

void removeSpaces(string& str)
{
	/* remove multiple spaces */
    int k=0;
    for (int j=0; j<str.size(); ++j)
    {
            if ( (str[j] != ' ') || (str[j] == ' ' && str[j+1] != ' ' ))
            {
                    str [k] = str [j];
                    ++k;
            }

    }
    str.resize(k);

    /* remove space at the end */   
    if (str [k-1] == ' ')
            str.erase(str.end()-1);
    /* remove space at the begin */
    if (str [0] == ' ')
            str.erase(str.begin());
}

Solution 20 - C++

string trim(const string & sStr)
{
    int nSize = sStr.size();
    int nSPos = 0, nEPos = 1, i;
    for(i = 0; i< nSize; ++i) {
        if( !isspace( sStr[i] ) ) {
            nSPos = i ;
            break;
        }
    }
    for(i = nSize -1 ; i >= 0 ; --i) {
        if( !isspace( sStr[i] ) ) {
            nEPos = i;
            break;
        }
    }
    return string(sStr, nSPos, nEPos - nSPos + 1);
}

Solution 21 - C++

For leading- and trailing spaces, how about:

string string_trim(const string& in) {

    stringstream ss;
    string out;
    ss << in;
    ss >> out;
    return out;

}

Or for a sentence:

string trim_words(const string& sentence) {
	stringstream ss;
	ss << sentence;
	string s;
	string out;

	while(ss >> s) {
		
		out+=(s+' ');
	}
	return out.substr(0, out.length()-1);
}

Solution 22 - C++

> neat and clean

 void trimLeftTrailingSpaces(string &input) {
    	input.erase(input.begin(), find_if(input.begin(), input.end(), [](int ch) {
    		return !isspace(ch);
    	}));
    }
    
    void trimRightTrailingSpaces(string &input) {
    	input.erase(find_if(input.rbegin(), input.rend(), [](int ch) {
    		return !isspace(ch);
    	}).base(), input.end());
    }

Solution 23 - C++

My Solution for this problem not using any STL methods but only C++ string's own methods is as following:

void processString(string &s) {
    if ( s.empty() ) return;

    //delete leading and trailing spaces of the input string
    int notSpaceStartPos = 0, notSpaceEndPos = s.length() - 1;
    while ( s[notSpaceStartPos] == ' ' ) ++notSpaceStartPos;
    while ( s[notSpaceEndPos] == ' ' ) --notSpaceEndPos;
    if ( notSpaceStartPos > notSpaceEndPos ) { s = ""; return; }
    s = s.substr(notSpaceStartPos, notSpaceEndPos - notSpaceStartPos + 1);

    //reduce multiple spaces between two words to a single space 
    string temp;
    for ( int i = 0; i < s.length(); i++ ) {
    	if ( i > 0 && s[i] == ' ' && s[i-1] == ' ' ) continue;
    	temp.push_back(s[i]);
    }
    s = temp;
}

I have used this method to pass a LeetCode problem Reverse Words in a String

Solution 24 - C++

void TrimWhitespaces(std::wstring& str)
{
	if (str.empty())
		return;

	const std::wstring& whitespace = L" \t";
	std::wstring::size_type strBegin = str.find_first_not_of(whitespace);
	std::wstring::size_type strEnd = str.find_last_not_of(whitespace);

	if (strBegin != std::wstring::npos || strEnd != std::wstring::npos)
	{
		strBegin == std::wstring::npos ? 0 : strBegin;
		strEnd == std::wstring::npos ? str.size() : 0;

		const auto strRange = strEnd - strBegin + 1;
		str.substr(strBegin, strRange).swap(str);
	}
	else if (str[0] == ' ' || str[0] == '\t')	// handles non-empty spaces-only or tabs-only
	{
		str = L"";
	}
}

void TrimWhitespacesTest()
{
	std::wstring EmptyStr = L"";
	std::wstring SpacesOnlyStr = L"    ";
	std::wstring TabsOnlyStr = L"			";
	std::wstring RightSpacesStr = L"12345     ";
	std::wstring LeftSpacesStr = L"     12345";
	std::wstring NoSpacesStr = L"12345";

	TrimWhitespaces(EmptyStr);
	TrimWhitespaces(SpacesOnlyStr);
	TrimWhitespaces(TabsOnlyStr);
	TrimWhitespaces(RightSpacesStr);
	TrimWhitespaces(LeftSpacesStr);
	TrimWhitespaces(NoSpacesStr);

	assert(EmptyStr == L"");
	assert(SpacesOnlyStr == L"");
	assert(TabsOnlyStr == L"");
	assert(RightSpacesStr == L"12345");
	assert(LeftSpacesStr == L"12345");
	assert(NoSpacesStr == L"12345");
}

Solution 25 - C++

What about the erase-remove idiom?

std::string s("...");
s.erase( std::remove(s.begin(), s.end(), ' '), s.end() );

Sorry. I saw too late that you don't want to remove all whitespace.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTL36View Question on Stackoverflow
Solution 1 - C++GManNickGView Answer on Stackoverflow
Solution 2 - C++Evgeny KarpovView Answer on Stackoverflow
Solution 3 - C++GalikView Answer on Stackoverflow
Solution 4 - C++jon-hansonView Answer on Stackoverflow
Solution 5 - C++Plamen StoyanovView Answer on Stackoverflow
Solution 6 - C++jha-GView Answer on Stackoverflow
Solution 7 - C++Semjon MössingerView Answer on Stackoverflow
Solution 8 - C++jignatiusView Answer on Stackoverflow
Solution 9 - C++Murphy78View Answer on Stackoverflow
Solution 10 - C++Thinkal VBView Answer on Stackoverflow
Solution 11 - C++polfosol ఠ_ఠView Answer on Stackoverflow
Solution 12 - C++user2983960View Answer on Stackoverflow
Solution 13 - C++cigienView Answer on Stackoverflow
Solution 14 - C++ElipsisView Answer on Stackoverflow
Solution 15 - C++Jack Of BladesView Answer on Stackoverflow
Solution 16 - C++ProjectPhysXView Answer on Stackoverflow
Solution 17 - C++BishalGView Answer on Stackoverflow
Solution 18 - C++AmarghoshView Answer on Stackoverflow
Solution 19 - C++Devesh AgrawalView Answer on Stackoverflow
Solution 20 - C++kjkView Answer on Stackoverflow
Solution 21 - C++IderwokView Answer on Stackoverflow
Solution 22 - C++user1856722View Answer on Stackoverflow
Solution 23 - C++Charles WangView Answer on Stackoverflow
Solution 24 - C++Ivan StreletsView Answer on Stackoverflow
Solution 25 - C++vt.View Answer on Stackoverflow