1

I have a string in form "blah-blah..obj_xx..blah-blah" where xx are digits. E.g. the string may be "root75/obj_43.dat".

I want to read "xx" (or 43 from the sample above) as an integer. How do I do it?

I tried to find "obj_" first:

std::string::size_type const cpos = name.find("obj_");
assert(std::string::npos != cpos);

but what's next?

4
  • Is xx guaranteed to be only two characters? Or can it be any number of characters? Commented Sep 24, 2014 at 12:14
  • @Cyber Let's assume it is always 2 chars long. Commented Sep 24, 2014 at 12:15
  • I would match string with regex and then use std::atoi on the result. Commented Sep 24, 2014 at 12:16
  • 4
    What about using a std::regex to parse the string, and extract the number part? Otherwise a simple std::string::substr() might be sufficient. Commented Sep 24, 2014 at 12:16

5 Answers 5

3

My GCC doesn't support regexes fully, but I think this should work:

#include <iostream>
#include <string>
#include <regex>
#include <iterator>

int main ()
{
  std::string input ("blah-blah..obj_42..blah-blah");
  std::regex expr ("obj_([0-9]+)");

  std::sregex_iterator i = std::sregex_iterator(input.begin(), input.end(), expr);
  std::smatch match = *i;
  int number = std::stoi(match.str());
  std::cout << number << '\n';
}
Sign up to request clarification or add additional context in comments.

Comments

1

With something this simple you can do

auto b = name.find_first_of("0123456789", cpos);
auto e = name.find_first_not_of("0123456789", b);
if (b != std::string::npos)
{
    auto digits = name.substr(b, e);
    int n = std::stoi(digits);
}
else
{
    // Error handling
}

For anything more complicated I would use regex.

7 Comments

e is always going to be the same as b. Also, you can't validate b against npos after you already used it in a way that requires it to be valid.
@R.MartinhoFernandes Copy and paste error. Fixed now. Thanks.
Still haven't fixed the validation of b after its use.
@R.MartinhoFernandes Nothing wrong with using std::string::npos to find e. It is valid. Hence we can wait with the validation.
Hmm, I think you're right. I got confused by the way the standard formulates this.
|
0

How about:

#include <iostream>
#include <string>

int main()
{
  const std::string test("root75/obj_43.dat");
  int number;
  // validate input:
  const auto index = test.find("obj_");
  if(index != std::string::npos)
  {
    number = std::stoi(test.substr(index+4));
    std::cout << "number: " << number << ".\n";
  }
  else
    std::cout << "Input validation failed.\n";
}

Live demo here. Includes (very) basic input validation (e.g. it will fail if the string contains multiple obj_), variable length numbers at the end, or even more stuff following it (adjust the substr call accordingly) and you can add a second argument to std::stoi to make sure it didn't fail for some reason.

3 Comments

Disgracefully fragile and ugly.
It does not handle the blah-blah.
@JBL Updated to be more resilient and less ugly.
0

Here's another option

//your code:
std::string::size_type const cpos = name.find("obj_");
assert(std::string::npos != cpos);

//my code starts here:
int n;
std::stringstream sin(name.substr(cpos+4));
sin>>n;

Comments

0

Dirt simple method, though probably pretty inefficient, and doesn't take advantage of the STL:

(Note that I didn't try to compile this)

unsigned GetFileNumber(std::string &s)
{
    const std::string extension = ".dat";

    /// get starting position - first character to the left of the file extension
    /// in a real implementation, you'd want to verify that the string actually contains
    /// the correct extension.
    int i = (int)(s.size() - extension.size() - 1);

    unsigned sum = 0;

    int tensMultiplier = 1;

    while (i >= 0)
    {
        /// get the integer value of this digit - subtract (int)'0' rather than
        /// using the ASCII code of `0` directly for clarity. Optimizer converts
        /// it to a literal immediate at compile time, anyway.
        int digit = s[i] - (int)'0';

        /// if this is a valid numeric character
        if (digit >= 0 && digit <= 9)
        {
            /// add the digit's value, adjusted for it's place within the numeric
            /// substring, to the accumulator
            sum += digit * tensMultiplier;

            /// set the tens place multiplier for the next digit to the left.
            tensMultiplier *= 10;
        }
        else
        {
            break;
        }

        i--;
    }

    return sum;
}

If you need it as a string, just append the found digits to a result string rather than accumulating their values in sum.

This also assumes that .dat is the last part of your string. If not, I'd start at the end, count left until you find a numeric character, and then start the above loop. This is nice because it's O(n), but may not be as clear as the regex or find approaches.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.