Reading user input as strings with getchar()

Safer alternative to scanf() for reading user input as strings:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

#define MAXINPUTLEN 64

int main()
{
        char input[MAXINPUTLEN] = "";
        char cur_input;
        printf("Input: ");
        for(int i=0; i <= MAXINPUTLEN; i++) {
                cur_input = getchar();
                if(cur_input != '\n')
                        input[i] = cur_input;
                else
                        break;
        }
        printf("%s\n", input);
        return 0;
}
Last edited on
But also useless, since the user can't use BACKSPACE to fix typos.

string s;
getline( cin, s );

done.

But also useless, since the user can't use BACKSPACE to fix typos.

string s;
getline( cin, s );

done.


/me needs no BACKSPACE :-)

Perhaps it's Cygwin or my compiler platform, but I don't find any problems backspacing to correct typos on the command line where input is accept. I can type:

asdf

...backspace once and type:

asd1

...and it's read without any problems into my input array of characters. Perhaps a better explanation is in order about why the user can't use a backspace?
Last edited on
Because your input buffer looks like


a s d f \8 1


when that gets printed, it looks like asd1 because the terminal honors the backspace.

print out strlen( input ) or do strcmp( input, "asd1" ) when you type asdf <bs> 1 and
you'll see the length is 6 and the strings are not equal.
printf("%s (size: %d)\n", input, strlen(input));

When run:

1
2
3
$ ./inputecho
Input: asdf<BACKSPACE><BACKSPACE>
as (size: 2)


By the way, have you actually compiled this program for yourself? At any rate, still not seeing the problem.
Last edited on
Ah, it's because getchar() doesn't actually return until the user presses ENTER so the input is being pre-processed before it gets to the program.

Actually, though, now that I think about it,

1
2
char* buf = 0;
scanf( "%as", &buf );


does the same thing as your code, except it does not have the arbitrary input buffer size limitation. Though %as is not standard, IIRC.

Anyways being a C++ forum, I'd use streams instead.

Here's a little function I cobbled up that is not only buffer overflow-proof, but also handles entry of any type so long as it is streamable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/lambda/lambda.hpp> // For test purposes only
#include <string>

using namespace std;
using namespace boost::lambda;

struct accept_all
{
    template< typename T >
    bool operator()( const T& ) const
        { return true; }
};

template< typename T, typename Fn >
T GetInput( const string& prompt, Fn validator )
{
    do {
        cout << prompt << ": " << ends;
        string s;
        getline( cin, s );
        try {
            T result = boost::lexical_cast<T>( s );
            if( validator( result ) )
                return result;
        } catch( const boost::bad_lexical_cast& ) {
            // Just ask again.
        }
        cout << "Invalid input.  Try again." << endl;
    } while( 1 );

    // Appease compiler
    return T(); // should never get here
}

template< typename T >
T GetInput( const string& prompt )
{
    return GetInput<T, accept_all>( prompt, accept_all() );
}


template< typename T >
struct accept_range 
{
    accept_range( T lower, T upper = std::numeric_limits<T>::max() ) :
        lower( lower ), upper( upper ) {}

    bool operator()( T value ) const
        { return lower <= value && value <= upper; }

  private:
    T lower;
    T upper;
};

template< typename T >
accept_range<T> range( T min, T max = std::numeric_limits<T>::max() )
{
    return accept_range<T>( min, max );
}

int main() {
    int x = GetInput<int>( "Enter a positive integer", range( 1 ) );
    int y = GetInput<int>( "Enter an integer (1-10)", range( 1, 10 ) );
    int z = GetInput<int>( "Enter an integer (1-100)", _1 >= 1 && _1 <= 100 );
    double f = GetInput<double>( "Enter a double (-1.0...1.0)", range( -1.0, 1.0 ) );
    string s = GetInput<string>( "Enter a string" );

    cout << "You entered:" << endl 
         << "  integer            = " << x << endl
         << "  int(1-10)          = " << y << endl
         << "  int(1-100)         = " << z << endl
         << "  double(-1.0...1.0) = " << f << endl
         << "  string             = " << s << endl;
}
Topic archived. No new replies allowed.