2

I have a c_str which contains [51,53]. I want to split these pairs in two integers. They are in a c_str because I read them from an input file.

There must be an easy way to parse them. I was thinking of using the .at function:. But I am sure I made it way to complicated. Plus, it does not work, since it outputs:

pair: 0x7ffffcfc2998
pair: 0x7ffffcfc2998 
etc
string pairstring = buffertm.c_str();
stringstream pair1, pair2;
int pairint1, pairint2;
pair1 << pairstring.at(1) << pairstring.at(2);
cout << "pair: " << pair1;
pair1 >> pairint1;
pair2 << pairstring.at(4) << pairstring.at(5);
//cout << "pair: " << pair2;
pair2 >> pairint2;

Any better ways to do this?

zakinster
  • 10,508
  • 1
  • 41
  • 52
dorien
  • 5,265
  • 10
  • 57
  • 116
  • 4
    "they are in a c_str". That does not make much sense. If you are assigning a std::string to another std::string, the call to c_str is useless. – SirDarius Nov 06 '13 at 15:05
  • Do note that `c_str()` is just a member function of `std::string` that returns a char* that contains the characters of the string. – JBL Nov 06 '13 at 15:06
  • Yes, you are right. Because I read them from a file I already had that line there string buffertm; tmfile >> buffertm;string pairstring = buffertm.c_str(); – dorien Nov 06 '13 at 15:10
  • You get the wrong output since you're printing the stringstreams themselves, not the integers you extract from them. Older implementations have a nasty `operator void*()` to allow safe-ish conversion to `bool`, so trying to print a stream will print some pointer value. – Mike Seymour Nov 06 '13 at 15:19
  • Also, the input format is valid JSON. Are you by any chance reading JSON data, and are trying to avoid using a parser ? – SirDarius Nov 06 '13 at 15:20

6 Answers6

5

Something like this:

char c1, c2, c3;
int first, second;

std::istringstream iss(str);

if (iss >> c1 >> first >> c2 >> second >> c3
    && c1 == '['  && c2 == ',' && c3 == ']' )
{
    // success
}

You might want to throw in an additional check to see if there are more characters after the closing bracket:

if ((iss >> std::ws).peek() != EOF) {
   //^^^^^^^^^^^^^^
   // eats whitespace chars and returns reference to iss
   /* there are redundant charactes */
}
jrok
  • 54,456
  • 9
  • 109
  • 141
4

Try this using C++11 std::stoi:

char c_str[] = "[51,53]";
std::string s(c_str);
int num1 = std::stoi(s.substr(1, 2));
int num2 = std::stoi(s.substr(4, 2));

If you know the numbers will be outside the range 10-99 then use this instead:

char c_str[] = "[5156789,5]";
std::string s(c_str);
s.assign(s.substr(1, s.size() - 2));         // Trim away '[' and ']'
std::string::size_type middle = s.find(','); // Find position of ','
int num1 = std::stoi(s.substr(0, middle));
int num2 = std::stoi(s.substr(middle + 1, s.size() - (middle + 1)));

The function stoi will throw std::invalid_argument if number can't be parsed.

Edit:

For a more rubust solution that will only parse base-10 numbers, you should use something like this:

char c_str[] = "[51,0324]";

int num1, num2;
try {
    std::string s(c_str);
    s.assign(s.substr(1, s.size() - 2));
    std::string::size_type middle = s.find(',');

    std::unique_ptr<std::size_t> pos{new std::size_t};

    std::string numText1 = s.substr(0, middle);
    num1 = std::stoi(numText1, pos.get()); // Try parsing first number.
    if (*pos < numText1.size()) {
        throw std::invalid_argument{{numText1.at(*pos)}};
    }

    std::string numText2 = s.substr(middle + 1, s.size() - (middle + 1));
    num2 = std::stoi(numText2, pos.get()); // Try parsing second number.
    if (*pos < numText2.size()) {
        throw std::invalid_argument{{numText2.at(*pos)}};
    }
} catch (const std::invalid_argument& e) {
    std::cerr << "Could not parse number" << std::endl;
    std::exit(EXIT_FAILURE);
}

It will throw std::invalid_argument when trying to parse strings as "1337h4x0r" and such, unlike when using std::istringstream. Se this for more info

Community
  • 1
  • 1
Felix Glas
  • 15,065
  • 7
  • 53
  • 82
  • 2
    This solution assumes that all integers will be in the [10,99] interval. I'm not sure this is robust enough. – SirDarius Nov 06 '13 at 15:16
3

For a number of reasons, I'd recommend a two-step process rather than trying to do it all at once.

Also, some of this depends heavily on what kinds of assumptions you can safely make.

1) Just tokenize. If you know that it will start with a "[" and you know there's going to be a comma, and you know you'll only get two numbers, search the string for a "[," then use substr() to get everything between it and the comma. That's one token. Then do something similar from the comma to the "]" to get the second token.

2) Once you have two strings, it's relatively trivial to convert them to integers, and there are a number of ways to do it.

A few answers have been added while I've been typing, but I still recommend a two-step approach.

Matt
  • 775
  • 7
  • 24
1

If you're reasonably confident about the format of the string, you could put the whole thing into a stringstream, and then use the extraction operator; e.g.:

stringstream strm(pairstring);    
int pairint1 = 0, pairint2 = 0;
char c = 0;

strm >> c >> pairint1 >> c >> pairint2 >> c;

In reality you'd want some error checking in there too, but hopefully that's enough of an idea to get you started.

Peter Bloomfield
  • 5,578
  • 26
  • 37
0

Try to split your string using strtok function. For convertation use atoi.

Necronomicron
  • 1,150
  • 4
  • 24
  • 47
  • Even in C, that's a bad idea: `strtok` isn't reentrant and modifies the string it's parsing; and `atoi` doesn't detect errors. In C++, there are much better alternatives. – Mike Seymour Nov 06 '13 at 15:32
0

I have a c_str which contains [51,53]

No, c_str() is not a data structure, it's a method of std::basic_string that's returning a constant C string with data equivalent to those stored in the std::string.

string pairstring = buffertm.c_str(); is cumbersome, just do string pairstring = buffertm; or use directly buffertm.

Secondly, you're using your stringstream in the wrong way, you should use an istringstream (here you are using it as an ostringstream :

int i, j;
istringstream iss(buffertm);
iss.get()
iss >> i;
iss.get()
iss >> j;
iss.get().
zakinster
  • 10,508
  • 1
  • 41
  • 52