1

I have a program which reads integers from a text file and skips non-integers and strange symbols. Then text file looks like:

# Matrix A   // this line should be skipped because it contains # symbol
1 1 2
1 1$ 2.1      // this line should be skipped because it contains 2.1 and $
3 4 5

I have to print out the matrix without strange symbols and non-integers line. That is the output should be:

1 1 2
3 4 5

My code

ifstream matrixAFile("a.txt", ios::in); // open file a.txt
if (!matrixAFile)
{
      cerr << "Error: File could not be opened !!!" << endl;
      exit(1);
}

int i, j, k;
while (matrixAFile >> i >> j >> k)
{
      cout << i << ' ' << j << ' ' << k;
      cout << endl;
}

But it fails when it gets the first # symbol. Anyone helps please ?

Harry
  • 95
  • 2
  • 5
  • 14
  • It wouldn't be much of an assignment if all you had to do was `>>` a few numbers... – Nicol Bolas Sep 02 '12 at 23:04
  • You have to read the whole line into a string, make a stringstream from it and try to parse with `>>` the same way as you do it now. If the parsing fails, the string didn't contain what you looked for. – Vlad Sep 02 '12 at 23:06
  • @Vlad I used istringstream but it did'nt work. the istringstream still convert "5$" to "5" while it is supposed to skip it. – Harry Sep 02 '12 at 23:51
  • @Harry: but you can check if the is something left in the stream, correct? So for `3 4 5` stream is finished, but for `3 4 5$` there is still `$` in the stream. The case `3$ 4 5` is even easier, since it won't be able to get `4`. – Vlad Sep 03 '12 at 19:35

5 Answers5

1

Your problem is with this code.

int i, j, k;
while (matrixAFile >> i >> j >> k)

The assignment is "Find out if the line contains integers"

But your code is saying "I already know that the line contains integers"

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
1

If you are set to three integers per row, I suggest this pattern:

#include <fstream>
#include <sstream>
#include <string>

std::ifstream infile("matrix.txt");

for (std::string line; std::getline(infile, line); )
{
    int a, b, c;

    if (!(std::istringstream(line) >> a >> b >> c))
    {
        std::cerr << "Skipping unparsable line '" << line << "'\n";
        continue;
    }

    std::cout << a << ' ' << b << ' ' << c << std::endl;
}

If the number of numbers per line is variable, you could use a skip condition like this:

line.find_first_not_of(" 0123456789") != std::string::npos
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
0

Since it's an assignment, I'm not giving full answer.

Read the data line by line to a string(call it str),
Split str into substrings,
In each substring, check if it's convertible to integer value.

Another trick is to read a line, then check that every char is between 0-9. It works if you don't need to consider negative numbers.

Seçkin Savaşçı
  • 3,446
  • 2
  • 23
  • 39
  • try/catch is a kind of overkill, given that the standard read operations don't throw – Vlad Sep 02 '12 at 23:07
  • well, but the simple conversion could be just reading with `istream::operator >>`. since failures are expected, I wouldn't treat them as exceptional, so IMHO just a boolean flag would be enough – Vlad Sep 02 '12 at 23:11
  • The problem is; when you are reading you also assume to read integers. Read all the line together, then try to get what you want from it. – Seçkin Savaşçı Sep 02 '12 at 23:16
  • I already tried your way. I used istringstream to convert substrings into integers but when I converted "4$", it is still converted into "4", while it is supposed to skip it. – Harry Sep 02 '12 at 23:20
  • @user1642488 I added another trick, it can work. Also with some more trick you can handle negative numbers, too. – Seçkin Savaşçı Sep 02 '12 at 23:21
  • @SeçkinSavaşçı btw, how could you check if a substring is convertible to integer value ? I used istringstream but it did'nt work – Harry Sep 02 '12 at 23:49
  • @Harry http://stackoverflow.com/questions/194465/how-to-parse-a-string-to-an-int-in-c – Seçkin Savaşçı Sep 03 '12 at 01:11
0

Of course, this fails at the # character: The # isn't an integer and, thus, reading it as an integer fails. What you could do is try to read three integers. If this fails and you haven't reached EOF (i.e., matrixAFile.eof() yields false, you can clear() the error flags, and ignore() everything up to a newline. The error recovery would look something like this:

matrixAFile.clear();
matrixAFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

Note, that you need to bail out if you failed because eof() is true.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • For increased robustness, you should also check if the stream has gone `bad()` and exit the loop. – reima Sep 02 '12 at 23:18
  • Well, using something like `matrixAFile >> val1 >> val2 >> val2` with `int`s `val1`, `val2`, and `val3` attempts to read three integers. This expression fails (i.e., sets `std::ios_base::failbit` on `matrixAFile`) if it can't read three `int`s. – Dietmar Kühl Sep 02 '12 at 23:47
  • @DietmarKühl well if you could provide a short code, that would be great. I'm not an expert of C++ and I never see failbit before. Tks – Harry Sep 02 '12 at 23:53
  • @Harry: I already privided the crucial ingredients! Well, I didn't mention that a stream converts to `false` if `std::ios_base::failbit` is set but this is easily revealed by using Google. Any further help and I should get the score for having done your homework! The main reason for mentioning this approach is that I think it is overkill to read lines and then parse them using string streams. – Dietmar Kühl Sep 03 '12 at 00:54
0

I think I'd just read a line at a time as a string. I'd copy the string to the output as long as it contains only digits, white-space and (possibly) -.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111