-1

I am trying to write a code in C++ reading a text file contains a series of numerics. For example, I have this .txt file which contains the following series of numbers mixed with a character:

1 2 3 a 5

I am trying to make the code capable of recognizing numerics and characters, such as the 4th entry above (which is a character), and then report error.

What I am doing is like

double value;
while(in) {
  in >> value;
  if(!isdigit(value)) {
      cout << "Has non-numeric entry!" << endl;
      break;
  }
  else
      // some codes for storing the entry
}

However, the isdigit function doesn't work for text file. It seems when I am doing in >> value, the code will implicitly type-cast a into double.

Can anyone give me some suggestion?

Thanks a lot!

TurbPhys
  • 37
  • 1
  • 7
  • Read `char`s instead of `double`s and you'll find `isdigit` to be much more effective. – user4581301 Nov 02 '18 at 04:23
  • OK. I'm figuring out where the confusion is. `in >> value;` has different behaviour depending on the type of `value`. If `value` is a `double`, it tries to read a floating point number. if `value` is an `int` it tries to read an integer. if `value` is a string, it tries to read a bunch of characters up to the next whitespace. If `value` is `MyCustomClass` you need to provide an [`operator>>` overload](https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading) that knows how to read a `MyCustomClass`. – user4581301 Nov 02 '18 at 04:28
  • if any of these `>>` overloads cannot transform what's in the stream into whatever `value` is it either halts and returns what it has managed to transform or sets an error flag in `in` and leave it to the programmer to sort out the mess. – user4581301 Nov 02 '18 at 04:31
  • Since you don't know what you are going to be receiving, you have to read for the lowest common denominator, characters, and transform the characters yourself. – user4581301 Nov 02 '18 at 04:32
  • You can use your `double` value, but instead of checking `isdigit`, you need to check whether `failbit` was set on the stream indicating invalid input (e.g. a non-digit (or `+/e-.`) matching failure) by checking `in.fail()`. This should be done after checking `in.eof() || in.bad()`. See [std::basic_ios::rdstate](https://en.cppreference.com/w/cpp/io/basic_ios/rdstate) – David C. Rankin Nov 02 '18 at 05:48

4 Answers4

1

Try reading the tokens into string and explicitly parsing it

ifstream infile("data.txt");
string token;
while (infile >> token) {
    try {
        double num = stod(token);
        cout << num << endl;
    }
    catch (invalid_argument e) {
        cerr << "Has non-numeric entry!" << endl;
    }
}
William Lee
  • 337
  • 4
  • 11
  • 1
    Much better. Now if you get more downvotes they'll probably be because using exceptions to trap a non-exceptional event (you know there will be non-numeric characters in the input) is a fairly heavyweight solution. – user4581301 Nov 02 '18 at 04:40
1

Your while loop doesn't do what you think it does. It only iterates one statement:

in >> value;

The rest of the statements are actually outside the loop. Using curly braces for the while body is always recommended

OrenIshShalom
  • 5,974
  • 9
  • 37
  • 87
1

I created a small mini script where I would be reading in a file through a standard fstream library object as I was a little unsure on what your "in" represented.

Essentially, try to read in every element as a character and check the digit function. If you're reading in elements that are not of just length 1, a few modifications would have to be made. Let me know if that's the case and I'll try to help!

int main() {
    std::fstream fin("detect_char.txt");
    char x;
    while (fin >> x) {
        if (!isdigit(x)) {
            std::cout << "found non-int value = " << x << '\n';
        }
    }
    std::cout << '\n';
    return 0;
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
Drimik Roy
  • 17
  • 2
  • Great googly moogly. Someone finally got it. +1. Mind if I fix up the formatting a little? – user4581301 Nov 02 '18 at 04:36
  • @user4581301 yea if it helps, surely fix it up! – Drimik Roy Nov 02 '18 at 04:40
  • @bolov im not sure what you mean? all whitespaces would be ignored through this process. – Drimik Roy Nov 02 '18 at 04:41
  • To be honest, @bolov , I had to look it up. been a long time since I used `>>` to read into a character. I think Drimik has the right of it. – user4581301 Nov 02 '18 at 04:43
  • Original code doesn't even compile. It contains a `break` not nested in any loop since the while body is missing braces and hence contains a single statement only: `in >> value;`. So either fix the original post, or at least up-vote my answer below which points it out. – OrenIshShalom Nov 02 '18 at 04:44
  • @OrenIshShalom your answer highlights a sideshow. Yes, that's a problem, but the question is primarily about how to differentiate a number and a character in a stream, and your answer does not address this at all. – user4581301 Nov 02 '18 at 04:47
  • thx for the tip. Is there anyway I can do this with type `double`? With some other functions besides `isdigit`? Because I would probably do some numeric operations on these numbers. – TurbPhys Nov 02 '18 at 04:56
  • @SJH if you read directly into a `double`, the stream will be placed into an error condition. You can test for the error, clear it and either `std::istream::ignore` until you find whitespace or... Smurf. Why am I not writing an answer? Anyway read and discard until you find whitespace and the go back to looking for `double`s. – user4581301 Nov 02 '18 at 05:14
1

Since it looks like the Asker's end goal is to have a double value for their own nefarious purposes and not simply detect the presence of garbage among the numbers, what the heck. Let's read a double.

double value;
while (in) // loop until failed even after the error handling case
{
    if (in >> value) // read a double.
    {
        std::cout << value; // printing for now. Store as you see fit
    }
    else // failed to read a double
    {
        in.clear(); // clear error
        std::string junk; 
        in >> junk; // easiest way I know of to read up to any whitepsace. 
                    // It's kinda gross if the discard is long and the string resizes
    }
}

Caveat:

What this can't handle is stuff like 3.14A. This will be read as 3.14 and stop, returning the 3.14 and leave the A for the next read where it will fail to parse and then be consumed and discarded by in >> junk; Catching that efficiently is a bit trickier and covered by William Lee's answer. If the exception handling of stod is deemed to expensive, use strtod and test that the end parameter reached the end of the string and no range errors were generated. See the example in the linked strtod documentation

user4581301
  • 33,082
  • 7
  • 33
  • 54