Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Not reading a string properly

Tags:

c++

string

I am practising user input handling. My goal is to have the user enter a line of integers separated by space (" "), read them as integers, store them and work on them later. I stumbled upon an interesting problem (Atleast in my oppinion) the way I am doing it, it seems that it is always not reading the last digit which was entered by the user. I will post the entire program here (since there are some extra libreries that are included). I have left some comments in the program

#include <iostream>
#include <string>
#include <vector>
#include <stdlib.h>

using namespace std;

int main()
{
    //this vector will store the integers
    vector<int> a;
    // this will store the user input
    string inp;
    getline(cin, inp);
    // this string will temporarily store the digits
    string tmp;
    //be sure that the reading part is okay
    cout << inp << endl;
     //until you meet something different than a digit, read char by char and add to string
     for(int i = 0; i < inp.length(); i++)
    {
        if(isdigit(inp[i]))
        {
            tmp +=inp[i];
        }
        else
        {
            // when it is not a character, turn to integer, empty string
            int value = atoi(tmp.c_str());
            a.push_back(value);
            tmp = "";
        }
    }
    // paste the entire vector of integers
    for(int i = 0; i < a.size(); i++)
    {
        cout << a[i] << endl;
    }
    return 0;
}
like image 525
Stanimirovv Avatar asked Apr 17 '13 13:04

Stanimirovv


2 Answers

Replace this line

for(int i = 0; i <inp.length(); i++)

by

for(int i = 0; i <= inp.length(); i++)

DEMO IDEONE

The problem with your code is: In example 25 30 46 whenever i=7, tmp=46. You are not pushing 46 in vector as inp[8] is a newline character, so your for loop terminates after i become 7.

Please Note: i <= inp.length() runs perfectly in most of the compilers as \0 is used/treated as sentinel.However, there are few compilers(like Microsoft Visual C++) that may show Assertion error: string subscript out of range.

like image 177
Ritesh Kumar Gupta Avatar answered Oct 05 '22 10:10

Ritesh Kumar Gupta


If the very end of the line is a digit, you don't hit the else on the last iteration, and that last number never gets pushed into the vector. The simplest solution would be to replicate the non-digit logic after the loop:

 if (!tmp.empty()) // If tmp has content, we need to put it in the vector.
 {
        int value = atoi(tmp.c_str());
        a.push_back(value);
        tmp = "";
 }

Although I'm sure you can think of a nicer way of structuring it.

Here's a version I came up with using std::stringstream, that also avoids atoi:

int main()
{
    std::vector<int> ints;
    std::string line;
    std::getline (std::cin, line);
    std::cout << "Read \"" << line << "\"\n";
    std::stringstream ss(line);

    int remaining = line.size();
    while (remaining)
    {
        if(std::isdigit(ss.peek())) // Read straight into an int
        {
            int tmp;
            ss >> tmp;
            ints.push_back(tmp);
        }
        else
        {
            ss.get(); // Eat useless characters
        }

        remaining = line.size()-ss.tellg();
    }

    for (auto i : ints)
        std::cout << i << '\n';

    return 0;
}

Running:

$ ./a.out <<< "12 34 56"
Read "12 34 56"
12
34
56

Note, this is specifically made to work with any old gibberish between the numbers:

$ ./a.out <<< "12-abc34-56"
Read "12-abc34-56"
12
34
56

If there will only be whitespace, this is even easier, as reading ints from a stringstream will ignore that automatically. In which case you just need:

int tmp;
while (ss >> tmp)
{
    ints.push_back(tmp);
}
like image 24
BoBTFish Avatar answered Oct 05 '22 10:10

BoBTFish