00:00
00:00
Newgrounds Background Image Theme

KiichigoInu just joined the crew!

We need you on the team, too.

Support Newgrounds and get tons of perks for just $2.99!

Create a Free Account and then..

Become a Supporter!

C++ cin input

1,240 Views | 9 Replies
New Topic Respond to this Topic

C++ cin input 2014-01-31 19:59:57


So I've been messing around with C++ input from the console for awhile now, and I've come across a few different methods that include error checking and fail preventing/resetting. I'm currently using this method:

while(true){
  int input;
  string temp;
  getline(cin, temp);
  if(stringstream(temp) >> input){
    // user input successful
    break;
  }else{
    // fail, try again, etc...
  }
}

But I'm also familiar with

int input;
while(true){
  cin >> input;
  if (!cin){
    cin.clear();
    cin.ignore(256, '\n');
  }else if(input == 1){
    ...
  }
}

I'm under the impression that the widely used (and easier in my opinion) method is the first one. Are there any other methods and are they better?


BBS Signature

Response to C++ cin input 2014-01-31 22:43:23


If you want to stick to C++ functions then those are your only options that exist in the standard. The alternatives would be to use functions from the C standard. Your first example could be done like this in C:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int num;
    char line[256];

    while (1) {
        fgets(line, 256, stdin);
        if (num=atoi(line)) {
            break;
        }
    }

    return 0;
}

You could also use the atoi() function in C++, which is what I would prefer using over a stringstream since it's more succinct. You could also use the C functions for capturing input if you prefer, but the fact that you need to use C-strings makes it slightly limited compared to the C++ version (the 256 in my example code is the maximum number of bytes that it will read, limiting it to being able to read 255 characters, regardless of how much input is given to stdin).

Personally this is how I would do it:

#include <iostream>
#include <string>
#include <stdlib.h> // This is for atoi(), but might not be required depending on which compiler you use.

int main() {
    int num;
    std::string line;

    while (true) {
        getline(std::cin, line);
        if (num=atoi(&line[0])) {
            break;
        }
    }
}

The if statement could also be written like this:

if (num=atoi(line.c_str()))

But all those round brackets are ugly, in my opinion. Both do the same thing.

Response to C++ cin input 2014-02-01 11:09:01


At 1/31/14 10:43 PM, Diki wrote: If you want to stick to C++ functions then those are your only options that exist in the standard. The alternatives would be to use functions from the C standard.

Ah okay. I didn't think about using fgets on stdin, but it makes sense.

You could also use the atoi() function in C++, which is what I would prefer using over a stringstream since it's more succinct.

Interesting! Turns out atoi is 21x faster than stringstream (on my system using clock()).

if (num=atoi(line.c_str()))
But all those round brackets are ugly, in my opinion. Both do the same thing.

From what I'm reading here &line[0] == line.c_str() in C++11 only and could produce undefined behavior in earlier versions of C++. Sadly I am not able to update my gnu compiler because it won't make, so I'm left with pre-c++11 standards.

Thanks


BBS Signature

Response to C++ cin input 2014-02-01 11:48:07


At 1/31/14 10:43 PM, Diki wrote: int main() {
int num;
std::string line;

while (true) {
getline(std::cin, line);
if (num=atoi(&line[0])) {
break;
}
}
}

Actually you know what, I was thinking: doesn't atoi always return, whether or not &line[0] is a valid number?
atoi returns 0 on fail, so the loop would be useless.

And on that note, this would ruin the structure of "using 0 to quit" input from the user. Which isn't a big deal, I could use another number, but it's just convenient.


BBS Signature

Response to C++ cin input 2014-02-01 12:48:38


At 2/1/14 11:48 AM, coln wrote: Actually you know what, I was thinking: doesn't atoi always return, whether or not &line[0] is a valid number?
atoi returns 0 on fail, so the loop would be useless.

You're right, I didn't think about that little problem. One solution to allow the user to give a 0 would be to include an extra parameter in the if statement that simply checks the value of the C-string:

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

int main() {
    int num;
    std::string line;

    while (true) {
        getline(std::cin, line);
        if ((num=atoi(&line[0])) || (line[0] == '0' && line[1] == '\0')) {
            break;
        }
    }
}

Which isn't very elegant, and I don't recommend doing that. Another, and better, solution that I should of thought of in the first place would be to use the strtol function instead:

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

int main() {
    long num;
    std::string line;

    while (true) {
        getline(std::cin, line);
        char* end;
        long temp = strtol(line.c_str(), &end, 10);
        if (!*end) {
            num = temp;
            break;
        }
    }
}

You don't need to use a temporary variable, but strtol() returns 0 upon an error, so if it receives bad input you'll be assigning 0 to your variable. If that won't cause erroneous behaviour in your application then you can get rid of the temporary.

Basically that works by setting the end pointer to the first place it finds an error (i.e. non-digit character), which will end up being a non-zero value. If no error is found then end pointer points to a '\0' character, which is just 0. The 10 being passed as the third argument means the value will be treated as base10. You could set that to 16 if you wanted to accept hexadecimal, for example, which would deem "0xF45AE" as valid input.

That will work better, and will also be faster than using stringstreams. It's also worth nothing that iostreams in C++ are not localisation friendly, not that you're likely to run into that being a problem if you're just converting integers. I know many C++ programmers that recommend never using iostreams for that very reason.

Response to C++ cin input 2014-02-04 13:52:50


At 2/1/14 12:48 PM, Diki wrote: You don't need to use a temporary variable, but strtol() returns 0 upon an error, so if it receives bad input you'll be assigning 0 to your variable. If that won't cause erroneous behaviour in your application then you can get rid of the temporary.

That's exactly it. Thanks for also using strtol instead of stol! Anyway, this was perfect. I have to ask though, I saw an implementation in C using both fgets and sscanf (rather than atoi or strtol). Is there an advantage of one over the other?

That will work better, and will also be faster than using stringstreams. It's also worth nothing that iostreams in C++ are not localisation friendly, not that you're likely to run into that being a problem if you're just converting integers. I know many C++ programmers that recommend never using iostreams for that very reason.

I'm not exactly sure what localisation friendly means, but I'll believe you :)


BBS Signature

Response to C++ cin input 2014-02-04 17:12:08


At 2/4/14 01:52 PM, coln wrote: I saw an implementation in C using both fgets and sscanf (rather than atoi or strtol). Is there an advantage of one over the other?

If you're just converting an integer then strtol() will probably be your best bet, but if you need more flexibility in the conversion, such as converting a floating point, then sscanf() would be a better choice. This StackOverflow answer on the subject goes into pretty good depth on the matter.

At 2/4/14 01:52 PM, coln wrote: I'm not exactly sure what localisation friendly means, but I'll believe you :)

It has to do with dealing with languages from a different locale. I have no experience with it, since I've not required it, but dealing with languages that use non-ASCII characters with iostreams can get hairy.

Response to C++ cin input 2014-02-04 18:43:32


At 2/4/14 05:12 PM, Diki wrote: If you're just converting an integer then strtol() will probably be your best bet, but if you need more flexibility in the conversion, such as converting a floating point, then sscanf() would be a better choice. This StackOverflow answer on the subject goes into pretty good depth on the matter.

That's a pretty good answer. I'm surprised I didn't stumble across it.
What about fscanf? I know it's preferred over scanf, but would fscanf by itself be preferred over fgets or something else? In C of course. I would probably just use strings in C++.

It has to do with dealing with languages from a different locale. I have no experience with it, since I've not required it, but dealing with languages that use non-ASCII characters with iostreams can get hairy.

Oh that kind of localization.


BBS Signature

Response to C++ cin input 2014-02-05 23:27:21


At 2/4/14 06:43 PM, coln wrote: What about fscanf? I know it's preferred over scanf, but would fscanf by itself be preferred over fgets or something else?

It ultimately depends on what it is you want to be doing, for neither is "better" than the other (i.e. in the sense that one should be used over the other). The fgets() function is used to read up to, and including, a line break, or an EOF (end of file). The fscanf() function, on the other hand, is used to read up to a given token that is delimited by whitespace, which could also be, but does not have to be, a line break, and the whitespace is not read/stored. As well fscanf() does not take any argument to specify the number of bytes to be read.

And just to be clear regarding my second sentence, since my wording could be misinterpreted because of its formatting, the EOF is not stored as that would not make sense.

Here are two examples to illustrate the difference (note the use of input data):
- Using fgets() with space
- Using fscanf() with space
- Using fgets() with line break
- Using fscanf() with line break

It is also not necessary to use such large char arrays like I did. I merely did that to make understanding the code easier. And, again, just to be clear, fscanf() is also capable of converting the values found in the input to different scalars, such as in this example.

Response to C++ cin input 2014-02-06 00:39:30


At 2/5/14 11:27 PM, Diki wrote: Here are two examples to illustrate the difference (note the use of input data):
- Using fgets() with space
- Using fscanf() with space
- Using fgets() with line break
- Using fscanf() with line break

Those examples explained everything. Thank you! And just to leave this here, fgets will automatically null-terminate the string, while fscanf will chug along and overwrite memory.


BBS Signature