4

A little background: I am working on a sliding block puzzle for a school project and this is our first using C++ instead of Java. This is the first time I have had to implement something that reads data from a file.

I have a simple question regarding reading input from a text file. I understand how to read the file line by line and hold each line in a string, I want to know if I can parse the string into different data types as the file is read.

Currently I am reading each line and storing them as strings in a vector for parsing later, and I know there must be a much simpler way to implement this

The first line holds 2 integers which will indicate the length and width of the grid, the following lines will have 4 integers and a char for use as arguments when creating the blocks.

My question is this, if I read the file character by character instead, is there a function I can use that will detect if the character is an integer or a char (and ignore the spaces) so I can store them immediately and create the block objects as the file is read? How would i deal with integers >10 in this case?

EDIT: Just noting I am using fstream to read the files, I am unfamiliar with other input methods

A sample input:

4  4
3  1  2  1  b
1  1  1  1  a 

4 Answers 4

5

To detect whether a piece of string can be parsed as an integer, you just have to parse it and see if you succeed. The best function for that would probably be std::strtoul(), since it can be made to tell you how many characters it consumed, so that you can continue parsing after that. (See the man page for details.)

However, if you already know the format of your file, you can use iostream formatted extraction. This is quite straightforward:

#include <fstream>


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

int n1, n2, x1, x2, x3, x4;
char c;

if (!(infile >> n1 >> n2)) { /* error, could not read first line! Abort. */ }

while (infile >> x1 >> x2 >> x3 >> x4 >> c)
{
    // successfully extracted one line, data is in x1, ..., x4, c.
}

Another alternative is to read every line into a string (using std::getline), then creating a stringstream from that line, and parsing the stringstream with >>. This has the added benefit that you can discover and skip bad lines and recover, while in the direct formatted extraction I presented above, you cannot recover from any error.

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you very much. Does >> ignore white space? It seems i dont need to worry about the end of each line or the end of file using this method correct?
@Gadesxion: yes, the formatted input function start by skipping whitespace.
@KerrekSB: of course you can recover from input errors even without using std::istringstream: this is about the only use of eof(). When input failed but eof() is not set you would clear() the stream's state and ignore() characters e.g. until there is a newline.
@Gadesxion the >> operator doesn't exactly ignore whitespace. It tries to read the type you tell it to read into. For example, using >> to read 123 456 into an int will put 123 into the variable, because it reads a number as long as it can, encounters the space, and "gives up." Here is more detailed documentation of the istream << operator.
@rob05c: That's wrong. Under normal situations >> ignores leading whitespace then reads the value of the appropriate type.
4

If you can assert each type, I suggest using stream operators, like you would with cin.

#include <fstream>

using namespace std;

int main()
{
    fstream fileStream;
    fileStream.open("inputfile.txt");

    int firstNumber;
    fileStream >> firstNumber;

    char firstChar;
    fileStream >> firstChar;
}

This way, you can read by value, instead of reading by line and then parsing the line. Just read in every value you need into a variable, as you discover you need it, like that.

Comments

1

ifstreams are also istreams, so you can use the same operator >> as with std::cin.

int main()
{
    std::ifstream s("test.txt");
    if (s.is_open())
    {
        int i, j, k;
        s >> i >> j >> k;
    }
}

Note that this is way not the fastest way of parsing, but that is probably irrelevant to you.

10 Comments

Can you explain what is slow about this and which approach is faster?
I don't know exactly why this is slow (ask the library implementers), but I know for a fact that it is slow. A faster way would be to load chunks of a file inside a buffer and parse it with your own parser. -- If you don't want to write it, you can just test parsing 1kk doubles from a text file, strtod() is ~3 times faster than the streams.
I think cooky451's point can be summed up as "C is faster than C++."
@cooky451: Well, I didn't ask for an explanation of why (some) iostream libraries are slow but rather what you are comparing. In fact, having (implemented the iostream and locales libraries)(dietmar-kuehl.de/cxxrt) (note: I haven't touched this code since more than 10 years; it probably has plenty of problems) I know where there are problems but even this old implementation avoids most of them. Specifically floating point formatting is one of the weaker parts because it delegates to strtod() after parsing the value but: there are no floating points in this particular code.
@rob05c: For bad implementations of iostreams which feel like first building a format string and then delegating to <stdio.h> functions this is certainly slower. Iostreams can (and have) been implemented differently and even when I last measured differences between <stdio.h> and my implementation of iostream (see a pointer to it above) we negligible: for certain use cases <stdio.h> was slightly faster (in particular for parsing floating points which is primarily down to not having implemented a parser for those and just delegating to strtod()), for other my library.
|
1

I would read each line into a string (as you have been doing).
Then I would read the tokens from that line into the appropriate variables.

The operator>> when applied to a stream will convert the next value in a stream into the correct type. If this is not possable it sets flags on the stream indicating failure that are easy to test.

 int  x;
 stream >> x; // ignores white space then: reads an integer from the stream into x

 char c;
 stream >> c; // ignores white space then: reads an char from the stream into c

 double d;
 stream >> d; // ignores white space then: reads an double from the stream into d

Assuming your input:

4  4
3  1  2  1  b
1  1  1  1  a 

Not knowing what the the values mean I will put my assumptions in comments.

// Assume that stream is a std::fstream already opened to you file.

std::string line1;
std::getline(stream, line1);           // reads "4 4" into `line1`

std::stringstream  line1stream(line1); // convert line1 into a stream for reading.
int a;
int b;
line1stream >> a >> b;   // reads "4 4" from 'line1' into a (now 4) b (now 4)
if (!stream || !line1stream)
{
     // failed reading the first line.
     // either reading the line failed (!stream)
     // or     reading 2 integers from line1stream failed (!line1stream)
     throw SomeException("Failed");
}


std::string line2;
std::getline(stream, line2);           // reads "3  1  2  1  b" into line2

std::stringstream line2stream(line2);  // convers line2 into a stream for reading.
int  data[4];
char c;

line2stream >> data[0] >> data[1] >> data[2] >> data[3] >> c;
if (!stream || !line2stream)
{
     // failed reading the second line.
     // either reading the line failed (!stream)
     // or     reading 4 integers and one char from line2stream failed (!line2stream)
     throw SomeException("Failed");
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.