1

This is a sort of self-imposed extra credit problem I'm adding to my current programming assignment which I finished a week early. The assignment involved reading in integers from a file with multiple integers per line, each separated by a space. This was achieved easily using while(inFile >> val) .

The challenge I put myself up to was to try and read integers from a file of mixed numbers and letters, pulling out all contiguous digits as separate integers composed of those digits. For examples if I was reading in the following line from a text file:

12f 356 48 r56 fs6879 57g 132e efw ddf312 323f

The values that would be read in (and stored) would be

12f 356 48 r56 fs6879 57g 132e efw ddf312 323f

or

12, 356, 48, 56, 6879, 57, 132, 312, and 323

I've spent all afternoon digging through cplusplus.com and reading cover to cover the specifics of get, getline, cin etc. and I am unable to find an elegant solution for this. Every method I can deduce involves exhaustive reading in and storing of each character from the entire file into a container of some sort and then going through one element at a time and pulling out each digit.

My question is if there is a way to do this during the process of reading them in from a file; ie does the functionality of get, getline, cin and company support that complex of an operation?

user3776749
  • 667
  • 1
  • 10
  • 20

1 Answers1

3

Read one character at a time and inspect it. Have a variable that maintains the number currently being read, and a flag telling you if you are in the middle of processing a number.

If the current character is a digit then multiple the current number by 10 and add the digit to the number (and set the "processing a number" flag).

If the current character isn't a digit and you were in the middle of processing a number, you have reached the end of the number and should add it to your output.

Here is a simple such implementation:

std::vector<int> read_integers(std::istream & input)
{
    std::vector<int> numbers;

    int number = 0;
    bool have_number = false;

    char c;

    // Loop until reading fails.
    while (input.get(c)) {
        if (c >= '0' && c <= '9') {
            // We have a digit.
            have_number = true;

            // Add the digit to the right of our number.  (No overflow check here!)
            number = number * 10 + (c - '0');
        } else if (have_number) {
            // It wasn't a digit and we started on a number, so we hit the end of it.
            numbers.push_back(number);
            have_number = false;
            number = 0;
        }
    }

    // Make sure if we ended with a number that we return it, too.
    if (have_number) { numbers.push_back(number); }

    return numbers;
}

(See a live demo.)

Now you can do something like this to read all integers from standard input:

std::vector<int> numbers = read_integers(std::cin);

This will work equally well with an std::ifstream.

You might consider making the function a template where the argument specifies the numeric type to use -- this will allow you to (for example) switch to long long int without altering the function, if you know the file is going to contain large numbers that don't fit inside of an int.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • That works perfectly thank you! My teacher has discouraged us from using vectors up until now so it didn't occur to me to try that. – user3776749 Oct 01 '14 at 22:02
  • 1
    @user3776749 "My teacher has discouraged us from using vectors" - lets pray that ends soon. With the possible exception of `std::basic_string`, you would be hard pressed to find a standard library template that is more frequently used by C++ engineers than `std::vector<>`. (and +1 on the answer, btw). – WhozCraig Oct 01 '14 at 22:11
  • @user3776749 Np. Note the updated answer, which uses `input.get(c)` instead of `input >> c`. The `>>` operator for `char` on input streams will skip over whitespace characters, which is not what you want. – cdhowie Oct 01 '14 at 22:20