1

Would there be a better way to do this using alogrithm functions like std::find()?

std::string s = "012X012Y012";

//After erasing the numbers from beginning and end; the end result should be X012Y

size_t index = s.find_first_not_of("0123456789");
string::iterator iter_end = s.begin();
advance(iter_end, index);
s.erase(s.begin(), iter_end);

//Now std::string should be X012Y012

size_t index = s.find_last_not_of("0123456789")+1;
string::iterator iter_start = s.begin();
advance(iter_start, index);
s.erase(iter_start, s.end());

//Finally, std::string should be X012Y
NullUserException
  • 83,810
  • 28
  • 209
  • 234
cpx
  • 17,009
  • 20
  • 87
  • 142
  • You've always got regex :) (Off-the-cuff: `/[0-9]*?(.+?)[0-9]*?/`, capture 1 is your output) – Brian S Jul 24 '10 at 06:49
  • 1
    Relevant: http://stackoverflow.com/questions/479080/trim-is-not-part-of-the-standard-c-c-library. – kennytm Jul 24 '10 at 06:51
  • I'm not sure if it matters, but you're not actually doing what you say (trimming numerics from the beginning and end). So if your input was `0123X123Y456Z123` you'll get `X123Y` instead of `X123Y456Z`. – egrunin Jul 24 '10 at 06:52
  • To trim characters off the end you could use the `string::resize` method, which takes the new size as a parameter, avoiding some of the iterator hassles in your example. Makes the two cases asymmetric, though. Similar code for nearby similar work is helpful for readability, even if that sometimes means one block is slightly more complex than it could be. Of course too much repetition means you should have wrote a function - and if you do a lot of e.g. `erase_from_first_not_of_to_end`... –  Jul 24 '10 at 07:32

2 Answers2

5

Seems fine enough. I'd factor out your constant:

const std::string Numbers = "0123456789";

And re-use that. erase does allow you to use indices instead of iterators, so you can trim all that iterator stuff from your code. You should also check the results of your search, otherwise you'll do weird things to your string:

std::size_t start = s.find_first_not_of(Numbers);
if (start != std::string::npos)
    s.erase(0, start);

size_t end = s.find_last_not_of(Numbers);
if (end != std::string::npos)
    s.erase(end + 1, std::string::npos);

Lastly, I think you should trim the right before the left. If you trim the left first, during erase the remainder of the string needs to be copied and moved down. By trimming the right first, you eliminate some things to copy. Nothing major, I suspect, but might as well.

(This is true of most sequence containers: erasing from some point to the end simply means getting rid of objects, while erasing from the start to some point requires moving the remaining items into their new spot.)


Of course, you can generalize this and re-use it:

// typically you trim whitespace, so for the sake of
// a generic utility, default to that
const std::string whitespace = " \f\n\r\t\v";

void trim_left(std::string& pStr, const std::string& pExcess = whitespace)
{
    std::size_t start = s.find_first_not_of(pExcess);
    if (start != std::string::npos)
        s.erase(0, start);
}

void trim_right(std::string& pStr, const std::string& pExcess = whitespace)
{
    size_t end = s.find_last_not_of(pExcess);
    if (end != std::string::npos)
        s.erase(end + 1, std::string::npos);
}

void trim(std::string& pStr, const std::string& pExcess = whitespace)
{
    // right then left, for reasons said above
    trim_right(pStr, pExcess);
    trim_left(pStr, pExcess);
}
GManNickG
  • 494,350
  • 52
  • 494
  • 543
0

Not a pure STL solution, but the Boost String libraries make this trivial:

std::string s = "012X012Y012";
boost::trim_if(s, boost::is_digit());
std::cout << s << std::endl; // Outputs: "X012Y"
ollb
  • 1,453
  • 1
  • 11
  • 17