views:

900

answers:

5

In a C++ MD2 file loader, I have a lot of frames, each with a name that ends with a number, such as

  • stand0
  • stand1
  • stand2
  • stand3
  • stand4
  • ...
  • stand10
  • stand11
  • run0
  • run1
  • run2

etc.

How do I get what the string is without the number behind? e.g. a function that changed "stand10" to just "stand"

A: 

Quick and dirty and not too elegant:

for (int i = str.GetLength()-1; i >= 0; i--)
    {
    if (!isdigit(str.GetAt(i)) break;

    str.SetAt(i,'\0');
    }
Steve
I would up-vote if you did a substring instead of inserting a null :(
crashmstr
+4  A: 

Just to show another way, reverse iterators:

string::reverse_iterator rit = str.rbegin();
while(isdigit(*rit)) ++rit;
std::string new_str(str.begin(), rit.base());

If you have boost::bind, you can make your life easier

std::string new_str(str.begin(),
    std::find_if(str.rbegin(), str.rend(),
                 !boost::bind(::isdigit, _1)).base());
Johannes Schaub - litb
it's just one of many ways, of which find_last_not_of possibly would be quite cute. i just wanted to show one of these ways :)
Johannes Schaub - litb
sure +1...
Tim
+3  A: 

string::find_last_not_of("0123456789") and then string::substr()

that gives you the position of the last non digit/number. Just take all the preceding characters and that is the base name.

Increment by one to get the start of the number sequence at the end of the string.

Note: no error checking or other tests.

#include <string>

using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
   string test = "hellothere4";

   size_t last_char_pos = test.find_last_not_of("0123456789");
   string base = test.substr(0, last_char_pos + 1);

EDIT

there is a problem with ALL the solutions when your "base name" has a number at the end.

for example, if the base string is "base1" then you can never get the proper base name. I assume you already are aware of this.

Or am I missing something? As long as the base name can't have a number at the end just before the postfix number it will work fine.

Tim
Finish the parameter list to find_last_not_of and this is the right answer.
jmucchiello
+1  A: 

C-style way of doing it:

Iterate through your string character-by-character, starting from the left. When you read a number, stop, and mark it as the end of your string.

char *curChar = myString;   // Temporary for quicker iteration.

while(*curChar != '\0') {   // Loop through all characters in the string.
    if(isdigit(*curChar)) { // Is the current character a digit?
        *curChar = '\0';    // End the string.
        break;              // No need to loop any more.
    }

    ++curChar;              // Move onto the next character.
}
strager
This would stop at the first digit in the string, though. If I read the requirements right, only trailing digits need to be truncated. Plan9FromOuterSpace23 should be stripped to Plan9FromOuterSpace, not Plan9.
Steve
@Steve, Ah, didn't think that would be an issue. He didn't state it in his question, and all the examples he gave were a-z strings (with the numbers appenended of course).
strager
+1  A: 

Just to complete it, one with find_first_of:

string new_string = str.substr(0, str.find_first_of("0123456789"));

just one line :)

Also, for these things, I like to use regular expressions (althought this case is very simple):

string new_string = boost::regex_replace(str, boost::regex("[0-9]+$"), "");
Diego Sevilla
this won't work - what if the base name has a digit in it? The other two best choices allow for that...
Tim
the regex will work, but not the find first of()
Tim
Well, from the question it is not obvious to me that the base string may have numbers.In fact, what I understand is that he wants to remove all the trailing numbers from the string.Anyway, in the regex case, you can include the base, whatever it is, as literal chars, and substitute only the digits
Diego Sevilla
yep, the regex one works.
Tim
Tim, sorry to insist, but both expressions, as written, will work exactly the same way.
Diego Sevilla
The first line was what I was looking for. Some of the others answers could also work just at good, but I like the simple solutions :)And about the base name, it shouldn't be hard to name them something without numbers.
Xunil
It is my turn to insist: It will only work if the base name has no digits in it. Apparently this is acceptable.
Tim