Adam
Adam

Reputation: 3965

Is there a way to check input data type using only basic concepts?

I'm being challenged to find ways to perform tasks that usually require the use of headers (besides iostream and iomanip) or greater-than-basic C++ knowledge. How can I check the data type of user input using only logical operators, basic arithmetic (+, -, *, /, %), if statements, and while loops?

Obviously the input variable has a declared data type in the first place, but this problem is covering the possibility of the user inputting the wrong data type.

I've tried several methods including the if (!(cin >> var1)) trick, but nothing works correctly. Is this possible at all?

Example

int main() {
    int var1, var2;
    cin >> var1;
    cin >> var2;
    cout << var1 << " - " << var2 << " = " << (var1-var2);

    return 0;
}

It's possible to input asdf and 5.25 here, so how do I check that the input aren't integers as expected, using only the means I stated earlier?


I understand this problem is vague in many ways, mostly because the restrictions are extremely specific and listing everything I'm allowed to use would be a pain. I guess part of the problem as mentioned in the comments is figuring out how to distinguish between data types in the first place.

Upvotes: 0

Views: 385

Answers (3)

Matteo Italia
Matteo Italia

Reputation: 126787

The standard library is not magic - you just have to parse the data read from the user, similarly to what the standard library does.

First read the input from the user:

std::string s;
cin >> s;

(you may use getline instead if you want to read a whole line)

Then you can go on parsing it; we'll try to distinguish between integer (*[+-]?[0-9]+ *), real number (*[+-][0-9](\.[0-9]*)?([Ee][+-]?[0-9]+)? *), string (*"[^"]" *) and anything else ("bad").

enum TokenType {
    Integer,
    Real,
    String,
    Bad
};

The basic building block is a routine that "eats" consecutive digits; this will help us with the [0-9]* and [0-9]+ parts.

void eatdigits(const char *&rp) {
    while(*rp>='0' && *rp<='0') rp++;
}

Also, a routine that skips whitespace can be handy:

void skipws(const char *&rp) {
    while(*rp==' ') rp++;
    // feel free to skip also tabs and whatever
}

Then we can attack the real problem

TokenType categorize(const char *rp) {

first, we want to skip the whitespace

    skipws(rp);

then, we'll try to match the easiest stuff: the string

    if(*rp=='"') {
        // Skip the string content
        while(*rp && *rp!='"') rp++;
        // If the string stopped with anything different than " we
        // have a parse error
        if(!*rp) return Bad;
        // Otherwise, skip the trailing whitespace
        skipws(rp);
        // And check if we got at the end
        return *rp?Bad:String;
    }

Then, on to numbers, notice that the real and integer definitions start in the same way; we have a common branch:

    // If there's a + or -, it's fine, skip it
    if(*rp=='+' || *rp=='-') rp++;
    const char *before=rp;
    // Skip the digits
    eatdigits(rp);
    // If we didn't manage to find any digit, it's not a valid number
    if(rp==start) return Bad;
    // If it ends here or after whitespace, it's an integer
    if(!*rp) return Integer;
    before = rp;
    skipws(rp);
    if(before!=rp) return *rp?Bad:Integer;

If we notice that there's still stuff, we tackle the real number:

    // Maybe something after the decimal dot?
    if(*rp=='.') {
        rp++;
        eatdigits(rp);
    }
    // Exponent
    if(*rp=='E' || *rp=='e') {
        rp++;
        if(*rp=='+' || *rp=='-') rp++;
        before=rp;
        eatdigits(rp);
        if(before==rp) return Bad;
    }
    skipws(rp);
    return *rp?Bad:Real;
}

You can easily invoke this routine after reading the input.

(notice that here the string thing is just for fun, cin does not have any special processing for double-quotes delimited strings).

Upvotes: 0

AKJ88
AKJ88

Reputation: 733

You can do that using simple operations, although it might be a little difficult, for example the following function can be used to check if the input is a decimal number. You can extend the idea and check if there is a period in between for floating point numbers.

Add a comment if you need further help.

bool isNumber(char *inp){
    int i = 0;
    if (inp[0] == '+' || inp[0] == '-') i = 1;
    int sign = (inp[0] == '-' ? -1 : 1);
    for (; inp[i]; i++){
        if (!(inp[i] >= '0' && inp[i] <= '9'))
            return false;
    }
    return true;
}

Upvotes: 1

m8mble
m8mble

Reputation: 1545

General checking after reading is done like this:

stream >> variable;
if (not stream.good()) {
    // not successful
}

This can be done on any std::ios. It works for standard types (any numeric type, char, string, etc.) stopping at whitespace. If your variable could not be read, good returns false. You can customize it for your own classes (including control over good's return value):

istream & operator>>(istream & stream, YourClass & c)
{
    // Read the data from stream into c
    return stream;
}

For your specific problem: Suppose you read the characters 42. There is no way of distinguishing between reading it as - an int - a double as both would be perfectly fine. You have to specify the input format more precisely.

Upvotes: 0

Related Questions