Stanimirovv
Stanimirovv

Reputation: 3172

Not reading a string properly

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;
}

Upvotes: 7

Views: 1291

Answers (9)

BoBTFish
BoBTFish

Reputation: 19767

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);
}

Upvotes: 3

Ritesh Kumar Gupta
Ritesh Kumar Gupta

Reputation: 5191

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.

Upvotes: 6

4pie0
4pie0

Reputation: 29724

you ommit input. change your code to reflrct this:

   //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];
            int value = atoi(tmp.c_str());
            a.push_back(value);
        }
        else
        {
            tmp = "";
        }
    }
    // paste the entire vector of integers
    for(int i = 0; i < a.size(); i++)
    {
        cout << a[i] << endl;
    }
    return 0;

or replace in loop: for(int i = 0; i <inp.length(); i++)

by

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

Upvotes: 0

Pruthviraj
Pruthviraj

Reputation: 131

When the last digit of the last number is stored in tmp, after that the loop ends because you have read the last character of the entire string. When the loop ends tmp still contains the last number.

1) You can convert and add the last number to vector after the loop. The last number still available in tmp.

2) Or you can explicitly add non-digit character in the end of the string before the loop.

Upvotes: 0

johnchen902
johnchen902

Reputation: 9599

Your loop is finished after last digit is read, so the last digit is never turned to integer. Just add some code after original for loop.

for(int i = 0; i < inp.length(); i++)
{
    /* ...... */
}
// add this to read the last digit
if(tmp.length() > 0){
    int value = atoi(tmp.c_str());
    a.push_back(value);
    tmp = "";
}

Upvotes: 1

Loghorn
Loghorn

Reputation: 2807

Before starting the loop, add a space to the string to be sure to push the last number: inp.push_back(' ')

Upvotes: 1

RageD
RageD

Reputation: 6823

You never push back your last value. For instance, consider this input

40 36

Then while you are reading, you push back at the first space. But you never push 36 since there are no more characters.

After the end of your for() loop you can try this:

if(!tmp.empty()) {
  a.push_back(tmp);
}

Upvotes: 0

alexrider
alexrider

Reputation: 4463

You update a with new value only when when non digit is found. Thus if you have string ending with digits, tmp will contain digital string but you will never get to else that should perform push_back. You may fix this by adding following code after for loop

if(!tmp.empty()){
    // when it is not a character, turn to integer, empty string
    int value = atoi(tmp.c_str());
    a.push_back(value);
    tmp = "";
}

Upvotes: 1

BlackMario
BlackMario

Reputation: 55

Your program need a string which is ended with a non-digit character to work correctly. Try this string "1 12 14587 15 " because in your algorithm when your forgot the last space, your program store the number into the tmp string but don't save it into the vector. To correct that you need to add a last push_back just after your first loop.

Upvotes: 1

Related Questions