You are here:

C++/How to read a text file in c which contains variable

Advertisement


Question
I am trying to make a code that can read a text file . Because this file has some float numbers which is the input of that programe. and i don't want to put those numbers in the main programe but i want to make simple code that it can read a text file where are those number and can use as an input variable.Please help me how to write a code for that.  

Answer
There are many ways to do this but the basic idea is to read the values from the file into one or more variables. You cannot directly use the data in the file as a variable in the program. I also have no idea how much C++ you know nor what dialect you might be using, so I shall assume ISO/ANSI standard C++.

As the data is stored in a text file the most natural type for the data is as a string. For it to be anything else requires that it be converted.

Luckily for us the C++ IOStreams library provides such conversions through the >> operators, for example:

   input_stream >> float_var;

Of course there is a problem if the data read from the file is not convertible to the type of the provided object. In this case the stream's failbit is set and can be tested using the stream's fail() operation:

   if ( input_stream.fail() )
   {
   // handle formatting error
   }

See also the badbit, eofbit and goodbit and the stream bad() good() and eof() operations as well as the stream void* and ! operators, among other stream state support facilities.

Note that for brevity I may not show error checking in the examples below and that not all the examples have been compiled so may contain typos and errors - for which I apologise now.

So you need a stream object associated with your file. The appropriate type would be a std::ifstream std:: because it is part of the C++ standard library and ifstream because we want to input from a file (i and f prefixes in the ifstream name). To use these you should include <fstream>. That stream needs to be opened with the file name. This can be done either in the constructor or by using the open() operation. Likewise the stream is closed when the stream object is destroyed or when the close() operation is called:

   std::ifstream input_stream( "floats.dat" );

Of course we should check to see that the stream is open:

   if ( input_stream.is_open() )
   {
   // proceed with processing
   }
   else
   {
   // handle unable to open file error
   }

The obvious way to extract data would be to pass the float variable to a function that reads from the stream, however this means the stream is accessible from the function as well:

   void GetNextFloat( std::ifstream & in, float & value )
   {
       in >> value;
   }

Or if you prefer:

   float GetNextFloat( std::ifstream & in )
   {
       float value(0.0);
       in >> value;
       return value;
   }

Note the latter example while maybe nicer to use is less efficient as it involves at least one additional float variable.

The problem here is that we need to pass around the stream all the time. These are ways around this. We could make the stream a static variable, opened the first time GetNextFloat is called:


   void GetNextFloat( float & value )
   {
       static std::ifstream in;

       if ( !in.is_open() )
       {
         in.open( "floats.dat" );
       }

       in >> value;
   }

But this leaves us with the problem of how to close the stream. Maybe only when we read the end, but this would mean we would always have to read the whole file plus make a call that failed with eof (end of file) so we can detect this condition and close it. A solution would be to make the stream variable available across more than one function by making it global:

   static std::ifstream gIn;

   void GetNextFloat( float & value )
   {
       if ( !gIn.is_open() )
       {
         gIn.open( "floats.dat" );
       }

       gIn >> value;
   }

Then any function that gIn can be seen from can manipulate it - e.g. close it. However local statics are bad enough and global variables are _never_ a good idea in the long run. Both suffer from the fact that there can only ever be one file providing values at any one time. This may be ok today but will this still be true say a year down the line?

So what else can we do? Well we can wrap the functionality in a class, here is one way to do it:

   class FloatFileParameters
   {
   public:
       explicit FloatFileParameters( char const * filename )
       : iStrm( filename )
       {
       }

       ~FloatFileParameters()
       {
         iStrm.close();
       }

       float GetValue()
       {
         float value(0.0);
         iStrm >> value;
         return value;
       }

   private:
       std::ifstream      iStrm;
   };

The above is very basic but show the idea. It can be used as below:

   void SomeFunction()
   {
       FloatFileParameters floatParams( "floats.dat" );
       float param1( floatParams.GetValue() );
       float param2( floatParams.GetValue() );
       float param3( floatParams.GetValue() );
   } // floatParams goes out of scope, is destroyed and
     //destructor closes file stream.

The interface to FloatFileParameters can be changed to take advantage C++ niceties. For example a dereference operator:

   class FloatFileParameters
   {
   public:

   // ...

       float operator*()
       {
         float value(0.0);
         iStrm >> value;
         return value;
       }

   // ...
   };

We can then write:

   void SomeFunction()
   {
       FloatFileParameters floatParams( "floats.dat" );
       float param1( *floatParams );
       float param2( *floatParams );
       float param3( *floatParams );
   }

This makes floatParams act a little like it was a pointer to the values. Unlike a pointer each time we dereference the 'pointer' we effectively also increment it onto the next value.

This is very similar to C++ standard library iterators, except they work more like traditional pointers in that you have to explicitly increment them to move onto the next value.

Usefully, C++ standard library includes stream iterators for input and output. They require a pre-existing stream to be passed to them for use, and you can detect if one is in the end of file state by comparing it with default constructed instance. Like a lot of the C++ library classes they are in fact class templates requiring the type of items read in (or written out) as the template parameter. The previous example would look like as follows using std::istream_iterator:

   #include <fstream>
   #include <iterator>

   void SomeFunction()
   {
       std::ifstream in( "floats.dat" );
       std::istream_iterator<float> floatReader( in );
       std::istream_iterator<float> floatReaderEOF;
       float param1( *floatReader );
       ++floatReader;
       float param2( *floatReader );
       ++floatReader;
       float param3( *floatReader );

       if (floatReader == floatReaderEOF )
       {
       // Handle end of file...
       }
   }

I added an example test for end of file just to show the form. Of course it would be more useful after each increment operation. If you like a terser programming style then you can increment after dereferencing:

   float param1( *floatReader++ );
   float param2( *floatReader++ );
   float param3( *floatReader );

Or even before dereferencing, which is probably more efficient:

   float param1( *floatReader );
   float param2( *++floatReader );
   float param3( *++floatReader );

So it you can live with the library stream iterator style there is no need to write anything yourself. You can just use them in you main function.

Hope this has been useful.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.