Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to "verify" your details, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ Programming ➜ General ➜ std::string::erase

std::string::erase

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by Terry   USA  (87 posts)  Bio
Date Wed 02 Dec 2009 04:43 PM (UTC)

Amended on Wed 02 Dec 2009 04:45 PM (UTC) by Terry

Message
On a more serious note than my last post... :P I'm trying to delete leading spaces using .erase(). However, I'm getting some weird bug that I can't understand. Maybe you guys can shed some light on the matter. :)

darkness@linux:~$ cat eval.cpp
#include <cstdlib>
#include <iostream>
#include <string>


int main()
{
    const std::string str = "    foo   ";

    size_t spaces;
    for(spaces = 0; str[spaces] == ' '; ++spaces);

    str.erase(0, spaces - 1);

    std::cout << "Number of leading spaces: " << spaces << std::endl;
    std::cout << "New string: \"" << str << "\"" << std::endl;

    return EXIT_SUCCESS;
}

darkness@linux:~$ g++ -o eval eval.cpp
eval.cpp: In function âint main()â:
eval.cpp:13: error: passing âconst std::stringâ as âthisâ argument of âstd::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::erase(typename _Alloc::rebind<_CharT>::other::size_type, typename _Alloc::rebind<_CharT>::other::size_type) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]â discards qualifiers



Obviously, I was expecting the output to be:
Number of leading spaces: 4
New string: "foo   "
Top

Posted by Nick Cash   USA  (626 posts)  Bio
Date Reply #1 on Wed 02 Dec 2009 04:49 PM (UTC)

Amended on Wed 02 Dec 2009 04:52 PM (UTC) by Nick Cash

Message

    const std::string str = "    foo   ";


The erase method modifies the string, so C++ is protecting you since you declared it as const. Try removing const.

Also,


    str.erase(0, spaces - 1);


You don't need to subtract one. I'm sure you would find that in your testing, but for completeness my OCD demands I point it out. :)

~Nick Cash
http://www.nick-cash.com
Top

Posted by Terry   USA  (87 posts)  Bio
Date Reply #2 on Wed 02 Dec 2009 04:51 PM (UTC)
Message
*facepalm* CRAIIIIII *sigh* i r idjit... :P thanks though...
Top

Posted by Nick Cash   USA  (626 posts)  Bio
Date Reply #3 on Wed 02 Dec 2009 04:54 PM (UTC)
Message
Programmers make errors; it is a fact of life. No reason to beat yourself up! I've certainly made sillier mistakes. Sometimes you just need another pair of eyes to point out the obvious things you are missing :)

~Nick Cash
http://www.nick-cash.com
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #4 on Wed 02 Dec 2009 05:26 PM (UTC)
Message
One thing to keep in mind is that you said this:
Quote:
Obviously, I was expecting the output to be:

but, your program hadn't compiled yet. The error message is admittedly rather cryptic, but any message of the form:

error: passing const XXX as argument to YYY discards qualifies


means that you are passing a const object to something that expects a non-const object.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Wjmurdick   (13 posts)  Bio
Date Reply #5 on Thu 03 Dec 2009 02:50 PM (UTC)

Amended on Fri 04 Dec 2009 09:43 PM (UTC) by Nick Gammon

Message
I wrote a trim function for a project a while back that used std::string. You could always use it if you wish. I've also got a char* version.


void str_ltrim (string& str)
{
	string mstr;
	int i=0, j, len;

	if (str.empty()) return;

	mstr = str;

	while (mstr[i] == ' ') {
		j = i;
		i++;
	};

	if (j == 0) return;

	len = mstr.length() - j;
	str = mstr.substr(j+1, len);
}

void str_rtrim (string& str)
{
	string mstr;
	int i=0, j, len;

	if (str.empty()) return;

	mstr = str;
	len = mstr.length();
	i = len-1;
	
	while (mstr[i] == ' ') {
		j = i;
		i--;
	};

	if (j == len) return;

	str = mstr.substr(0, j);
}
Top

Posted by Samson   USA  (683 posts)  Bio
Date Reply #6 on Fri 04 Dec 2009 01:21 AM (UTC)

Amended on Fri 04 Dec 2009 09:43 PM (UTC) by Nick Gammon

Message
I've been using these for awhile now and they work perfectly.

void strip_lspace( string & line )
{
   string::size_type space;

   space = line.find_first_not_of( ' ' );
   if( space == string::npos )
      space = 0;
   line = line.substr( space, line.length(  ) );

   space = line.find_first_not_of( '\t' );
   if( space == string::npos )
      space = 0;
   line = line.substr( space, line.length(  ) );
}

void strip_tspace( string & line )
{
   string::size_type space;

   space = line.find_last_not_of( ' ' );
   if( space != string::npos )
      line = line.substr( 0, space + 1 );
}

void strip_spaces( string & line )
{
   strip_lspace( line );
   strip_tspace( line );
}
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #7 on Fri 04 Dec 2009 01:36 AM (UTC)
Message
Two suggestions:
  • first_first_not_of allows you to specify several characters at once, so you can scan for spaces and tabs at the same time. This is (in almost all cases) more efficient than scanning the string twice.

  • I would make const versions that return a string, so that you can easily apply the functions to const strings. As a convenience to maintain interface, you might consider having the non-const version return a reference to the argument as well.


Note that strip_lspace trims spaces and tabs, but strip_tspace only removes spaces.

Finally, if efficiency matters (and it probably doesn't) the two strip phases can be combined into a single phase, so that you only have one substring operation.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Nick Gammon   Australia  (23,070 posts)  Bio   Forum Administrator
Date Reply #8 on Fri 04 Dec 2009 09:46 PM (UTC)

Amended on Fri 04 Dec 2009 09:47 PM (UTC) by Nick Gammon

Message
Here is my version:



#define SPACES " \t\r\n"


inline string trim_right (const string & s, const string & t = SPACES)
  { 
  string d (s); 
  string::size_type i (d.find_last_not_of (t));
  if (i == string::npos)
    return "";
  else
   return d.erase (d.find_last_not_of (t) + 1) ; 
  }  // end of trim_right

inline string trim_left (const string & s, const string & t = SPACES) 
  { 
  string d (s); 
  return d.erase (0, s.find_first_not_of (t)) ; 
  }  // end of trim_left

inline string trim (const string & s, const string & t = SPACES)
  { 
  string d (s); 
  return trim_left (trim_right (d, t), t) ; 
  }  // end of trim


This trims whitespace (which you can define exactly what it is) from the left, right, or both sides of a string. This is used in MUSHclient.

The trimmed string is returned, the source string is unchanged.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #9 on Sat 05 Dec 2009 03:36 AM (UTC)
Message
Those could probably be made ever so slightly more efficient by using a substr on the const std::string argument, instead of copying into a modifiable string first. The 'erase' and 'substr' both require what is basically a string copy, but you can avoid the extra copy into a modifiable string.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Nick Cash   USA  (626 posts)  Bio
Date Reply #10 on Sat 05 Dec 2009 05:45 AM (UTC)
Message
It would seem David is a performance nazi :P

I would post some of the C-string functions I wrote to win one of the MudMagic coding competitions, but I think we would all be horrified!

~Nick Cash
http://www.nick-cash.com
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #11 on Sat 05 Dec 2009 07:46 AM (UTC)
Message
Heh, sorry, some habits die hard. :-) When I see "library functions" my brain goes into code-review mode and my instinct is to improve them where easily possible. I won't go to the extremes of squeezing out every last assembly cycle, but when improvements are easy I think it can't hurt to mention them.

If you will indulge an apparent self-contradiction, I will note that improving performance isn't always a worthwhile endeavor; it is only important that things be "fast enough", not "as fast as possible". If you're going to spend time on improving performance -- as opposed to making small, easy changes -- you should make sure that you're gaining real benefit for your time invested.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Nick Gammon   Australia  (23,070 posts)  Bio   Forum Administrator
Date Reply #12 on Sat 05 Dec 2009 09:56 AM (UTC)
Message
It might also be more efficient to do a test first to see if the (probably frequent) case of no spaces is there, to save more work. For example, if there is no space in the first position, when trimming leading spaces, simply return the source string. However this would only be more efficient if the source string usually did not have spaces. If it always did, then that would be slower.

However there is also something to be said for the function to be readable, and, as you say, if it is fast enough, then that may be very adequate.


- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Cash   USA  (626 posts)  Bio
Date Reply #13 on Sun 06 Dec 2009 12:03 AM (UTC)

Amended on Sun 06 Dec 2009 12:04 AM (UTC) by Nick Cash

Message
Quote:

Heh, sorry, some habits die hard.


No worries. Critical reviewing of code is not a habit you should be killing. I am a performance nazi as well, but it is generally required for embedded systems!

Quote:

... you should make sure that you're gaining real benefit for your time invested.


This is something that is very hard to get across to the interns that I work with. Most of them see no benefit in refactoring/optimization, and others take it too far! It is an important lesson to learn though, as your time is precious :)

~Nick Cash
http://www.nick-cash.com
Top

Posted by Samson   USA  (683 posts)  Bio
Date Reply #14 on Sun 06 Dec 2009 11:07 AM (UTC)
Message
David Haley said:
*first_first_not_of allows you to specify several characters at once, so you can scan for spaces and tabs at the same time. This is (in almost all cases) more efficient than scanning the string twice.


I wasn't aware of that. Good to know.

Also not terribly worried about using it on const strings.

Quote:
Note that strip_lspace trims spaces and tabs, but strip_tspace only removes spaces.


That's probably only because the use case for the functions even existing never had a situation where tabs would show up after the string, but they sometimes did before. I used them for cleaning up file input after converting most of them to read using the filestream libraries.

Quote:
Finally, if efficiency matters (and it probably doesn't) the two strip phases can be combined into a single phase, so that you only have one substring operation.


Not sure there's a point in combining. Most of the usage only calls on strip_lspace. strip_tspace came later, and strip_spaces was just a convenient way to do both in situations that needed it. I supposed if they were going to get used for general purpose functions then combining them into a single one would be more sensible.
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


41,088 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.