Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing only numbers from istream in C++

I have a bunch of input files that look like the following:

(8,7,15)
(0,0,1) (0,3,2) (0,6,3)
(1,0,4) (1,1,5)

I need to write a function that parses these inputs one number at a time, so I need to be able to separate the input by numbers, e.g.: 8, then 7, then 15, then 0, another 0, so on.

The only way I've thought of so far is to use istream.get() which returns the next character's ASCII code, which I can convert back to its character format by casting it to char. Then I'd check if the character was a number or not (so the brackets are ignored) but this way, any double (or triple) digit numbers are only read one digit at a time.

What would be the best way to achieve this?

By the way, I must use istream. It's part of the specification that I'm not allowed to change

Thanks

like image 883
Arvin Avatar asked Aug 19 '11 06:08

Arvin


2 Answers

This is one solution:

struct integer_only: std::ctype<char> 
{
    integer_only(): std::ctype<char>(get_table()) {}

    static std::ctype_base::mask const* get_table()
    {
        static std::vector<std::ctype_base::mask> 
            rc(std::ctype<char>::table_size,std::ctype_base::space);

        std::fill(&rc['0'], &rc['9'+1], std::ctype_base::digit);
        return &rc[0];
    }
};

int main() {
        std::cin.imbue(std::locale(std::locale(), new integer_only()));
        std::istream_iterator<int> begin(std::cin);
        std::istream_iterator<int> end;
        std::vector<int> vints(begin, end);
        std::copy(vints.begin(), vints.end(), std::ostream_iterator<int>(std::cout, "\n"));
        return 0;
}

Input:

(8,7,15)
(0,0,1) (0,3,2) (0,6,3)
(1,0,4) (1,1,5)

Output:

8 7 15 0 0 1 0 3 2 0 6 3 1 0 4 1 1 5 

Online demo : http://ideone.com/Lwx9y

In the above, you've to replace std::cin with the file stream after opening the file successfully, as:

 std::ifstream file("file.txt");
 file.imbue(std::locale(std::locale(), new integer_only()));
 std::istream_iterator<int> begin(file);
 std::istream_iterator<int> end;
 std::vector<int> vints(begin, end); //container of integers!

Here, vints is a vector which contains all the integers. You would like work with vints to do something useful. Also, you can use it where int* is expected as:

void f(int *integers, size_t count) {}

f(&vints[0], vints.size()); //call a function which expects `int*`.

Similar trick can be applied when reading only words from a file. Here is an example:

  • Elegant ways to count the frequency of words in a file
like image 50
Nawaz Avatar answered Sep 18 '22 10:09

Nawaz


Here's some code, you can adapt to meet your precise needs

for (;;)
{
  int ch = in.get();
  if (ch == EOF)
    break;
  if (isdigit(ch))
  {
    int val = ch - '0';
    for (;;)
    {
      ch = in.get();
      if (!isdigit(ch))
        break;
      val *= 10;
      val += ch - '0';
    }
    // do something with val
  }
}

This is untested code.

like image 33
john Avatar answered Sep 18 '22 10:09

john