Techniques from reading from files.

Started by
8 comments, last by ender7771 18 years, 4 months ago
I have a file that I load information from for my program. Some of the information is numbers, others is strings. Let's say I have the following text file that I am going to load: 17 14 hello how are you To load it, i would use an ifstream (called infile from now on) and infile >> twice to get the integers. Then, to get the string, I would have to use .getline(....) to get all of it. The problem is, the file pointer is after "14", so when I use .getline, it automatically reads the break that is right after 14. A cheap hack would be to call .getline() twice: the first time it would read the break and reset itself to the end of the next line, the second time it would read the string. If the string was first and the ints were second, however, and getline was called twice, it would immediately read the string, and then the integers. What Im asking is for a method where the program would extract a line from the file, but would keep on extracting if what it extracted was simply a break or something. Understand? Do I need to clarify something?
Advertisement
void ReadLine(const ifstream & file, string & line){    char temp[256];     do    {        file.getline(temp, 256);    } while(temp[0] == '\0' && !file.eof())     line = temp;}
Quote:Original post by Aprosenf
*** Source Snippet Removed ***


Ok. I have both strings and ints, though. When I .getline an int, won't I get a whole lot of stuff because a space isn't the same thing as a null-termination ("\0")?
Well if you want it so you will get an entire line for a string, but just ints on the lines containing ints you could do something like this (psuedo-code)

string strwhile(getline(str)){   // Something like this exists...I think!   if(FindOneOf(str.begin(), str.end(), "0123456789"))   {       vector<string> Inttokens = tokenize(str, " ");       vector<int> intList;       intList.resize(Inttokens.size());       // convert all strings in Inttokens to ints in intList       for(x = 0 to x < Inttokens.size())              {          stringstream ss;          ss << Inttokens[x];          ss >> intList[x];       }       // intList now contains all your ints, so you can either move that vector to a global or process later or here   }   else   {       // str is the string to process   }}

To see where half that craziness came from, look at this thread as well as the Standard C++ Library algorithms.
Wow... er.. thanks.

I guess I'll just do a lame hack where I .getline() twice.

Thanks for the suggestion though.

Edit: What I mean is that that looks too complicated for what I am trying to do. But thanks.
Why don't you try to use C commands in your C++ program.

//Example.

#include <iostream.h>
#include <stdio.h>

void main(void)
{
char* String[1024];
int Number = 0;

FILE *File = fopen("AFile", "r");

fscanf("%s %d %f", &String, &Number, &Float);

fclose(File);
}

With the C commands you can say what you expect to read.

Say you want to read a line like this:

1024 512.00 TakeItToTheHead.

the command for this is:

fscanf("%d %f %s", &varInteger, &varfloat, &varString);


Find more about this commands its easy and you have good results.
Good luck.
Just call
infile >> ws;
before calling getline.

With this you can read the end-line chars and spaces away without storing them.
Quote:Original post by D_a_M_a_G_e_D
Why don't you try to use C commands in your C++ program.

//Example.

#include <iostream.h>
#include <stdio.h>

void main(void)
{
char* String[1024];
int Number = 0;

FILE *File = fopen("AFile", "r");

fscanf("%s %d %f", &String, &Number, &Float);

fclose(File);
}

With the C commands you can say what you expect to read.

Say you want to read a line like this:

1024 512.00 TakeItToTheHead.

the command for this is:

fscanf("%d %f %s", &varInteger, &varfloat, &varString);


Find more about this commands its easy and you have good results.
Good luck.


Oh man! Don't let Zahlman see that code. Lets have a look at whats wrong with it, shall we?
  • #include <iostream.h>
    This is a non-standard (actually pre-standard) header. You should be using #include <iostream> (no .h). This is the standardised iostream header and places symbols in namespace std.

  • #include <stdio.h>
    Likewise in C++ this header is spelt cstdio. Again, this puts the symbols in namespace std.

  • void main(void)
    The main function is required in C++ to have a return type of int. The void parameter list is a C-ism. It is valid C++, but un-idiomatic. In C, functions with empty parameters were allowed to take arbitrary numbers of parameters. Only if a parameter list was void did the function take strictly no parameters. In C++ a function with an empty parameter list takes strictly no parameters.

  • char* String[1024];
    In C++ you use a std::string for text unless you have good reason not to. This protects you against several issues, such as buffer overruns, forgetting to null-terminate strings and the unusual problem you've just demonstrated whereby you declared an array of 1024 uninitialised char *s, not a single stack based array of 1024 characters.

  • FILE *File = fopen("AFile", "r");
    In C++ it is idiomatic to use streams. They have many advantages over C FILE *s and few disadvantages

  • fscanf("%s %d %f", &String, &Number, &Float);
    How can I fail thee? Let me count the ways. If the string is longer than the buffer &String passed then the behaviour is undefined. If any of the parameters does not match the type listed in the format string the behaviour is undefined. If you provide too few target arguments the behvaiour is undefined. If any conversion specification is invalid the behaviour is undefined. If the input does not match the format string then the state of the FILE stream is left partway through parsing. The return value must be checked to see how many arguments were successfully parsed.

    The C++ alternative to this (with no error handling) is:
    std::string String;
    int Number;
    float Float;
    std::ifstream stream(filename);
    stream >> String >> Number >> Float;
    How many ways can this fail? Well, beyond the having the file not exist or not accessible, or malformed, pretty-much none. And all of those erros are well defined are easily detected and leave the program in a valid and easily recoverable state.

  • fclose(File);
    Of course, in C one must remember to close ones FILE streams. In C++ this is unneccessary. The ifstream destructor is automatically called at end of scope and closes the stream.


Quote:Original post by Joni-Matti
Just call
infile >> ws;
before calling getline.

With this you can read the end-line chars and spaces away without storing them.

This is a good suggestion. If leading spaces are required to be retained on the string line the idiomatic alternative is to call stream.ignore(std::numeric_limits< std::streamsize >::max(), '\n'); after reading the integers to ignore the rest of the line.

Enigma
Quote:Original post by Enigma
Oh man! Don't let Zahlman see that code. Lets have a look at whats wrong with it, shall we?


Darn, you beat me to it. :D

Quote:
Quote:Original post by Joni-Matti
Just call
infile >> ws;
before calling getline.

With this you can read the end-line chars and spaces away without storing them.

This is a good suggestion. If leading spaces are required to be retained on the string line the idiomatic alternative is to call stream.ignore(std::numeric_limits< std::streamsize >::max(), '\n'); after reading the integers to ignore the rest of the line.

Enigma


Yep.

Drew's suggestion is good too, if you have to solve the problem of not knowing whether there or not there are numbers you want to read on any given line. If you do know whether the ints are first or not, though, then you shouldn't have a problem with the ".getline() hack" anyway - just don't call it the second time after reading a string line, only after an integer line. ;)

Seriously though, the best solution depends on exactly what the limits are on how the file is structured and how you want to interpret it.
Thanks for all the help.

I know the evils of including c code in c++. Im not new at c++, I just never used file streams in c++ and was wondering about the best techniques. My double .getline hack, though it hurts my eyes, works fine.

This topic is closed to new replies.

Advertisement