CS330 C++ File IO and Arg Lists


Highlights of this lab:


C++ File IO

There are three predefined streams in C++:

Note: make sure you have #include<iostream> and have used "using namespace std;" at the start of the program.

To open a file for reading, you can use code like the following:

ifstream inFile("name", ios::in);

To open a file for output, you can use code like:

ofstream outfile("name", ios::out);

Make sure you have #include<fstream> before using these functions.

To input a character from an input stream, use the get() function:

char ch;
inFile.get(ch);

To output a character, use the put() function:

outFile.put(ch);

Here is a sample program that gets characters from standard input and puts them into the file copy.out

#include <fstream>

int main()
{
   //open a file copy.out for output
   ofstream outFile("copy.out");

   if (! outFile)
   {
      cerr << "cannot open "copy.out" for output\n";
      return -1;
   }

   char ch;
   while (cin.get(ch))
      outFile.put(ch);
}
To open a file for input only, an ifstream class object is used.
#include <fstream>

int main()
{
   cout << "filename: ";
   string file_name;
   cin >> file_name;

   //open a file copy.out for input
   ifstream inFile(file_name.c_str() );

   if (! inFile)
   {
      cerr << "unable to open input file: "
	   << file_name << " --bailing out! \n";
      return -1;
   }

   char ch;
   while (inFile.get(ch))
      cout.put(ch);
}

Argument Lists

If you want to obtain parameters from the command line you need to define your main function as follows:
int main(int argc, char *argv[])
When you type the programs name followed by some parameters, the shell passes all the words on the command line to the program as an array of NULL-terminated character strings. These can be accessed in your program using argc, which is the total count of the parameters including the program name, and argv[] which is the 0 based index array of strings.

For example, if you have typed :

myprog one two three 

The following program prints out the command line, including the name of the program.

#include <iostream>
using namespace std;

int main(int argc, char *argv[])
{
	int i;

	for (i = 0; i < argc; i++)
	{
		cout << argv[i] << " ";
	}
	cout << endl;
	return 0;
}

getopt()

At certain times, you may want to process command line options. C provides you with a function called getopt() to do this.

getopt() parses the command line and finds any options you may have specified with a leading "-".

The prototype for getopt() is:

int getopt (int argc, char **argv, char *optstring);

The first two arguments are typically the argc and argv from main's definition. The last argument is a string containing all the options that you want. For instance, if you wanted be able to write "-a -b -c" on the command line with the name of your executable. Your optstring would be "abc"

Something to note about optstring is that if the option letter expects a following argument, the option letter in optstring is followed by a colon. For instance, if we wanted to write "-s 100" on the command line, our optstring would be "s:" to indicate that an argument comes after the -s.

getopt will return one of three values:

Another thing to mention is that there is an external integer called optind (option index). This index is initialized to 1, and as you go through the arguments in argv its value will be update. A comparison of the value in optind to argc can be used to determine if all the items on the command line have been processed.

An example program is shown below

//Code based on page 46 of Interprocess Communications in Unix by John Shapley Gray
#include <iostream> 
#include <unistd.h>
using namespace std;

extern char *optarg;
extern int  optind, opterr;


int main(int argc, char *argv[])
{

        int c;
        static char optstring[] = "abs:";
        opterr=0;

        while ((c=getopt(argc, argv, optstring)) != -1)
        {
                switch(c)
                {
                        case 'a':
                                cout << "Found option a" << endl;
                                break;
                        case 'b':
                                cout << "Found option b" << endl;
                                break;
                        case 's':
                                cout << "Found option s with an argument of ";
				cout << optarg << endl;
                                break;
                        case '?':
                                cout << "Found an option that was not in optstring";
				cout << endl;
                }
        }
        if (optind < argc)
                cout << "Left off at " << argv[optind] << endl;
        return 0;
}

The output may look something like this

% a.out -abc -s 34 -b joe -a
Found option a
Found option b
Found an option that was not in optstring
Found option s with an argument of 34
Found option b
Found option a
Left off at joe

Environment Variables

Each process has a list of environment variables. If you try env at the command line, you will get a list of all the variables in your environment. You can also access these variable from main using an external pointer called environ.

To display the environment variables, you can use the following code:

#include <iostream>
using namespace std;

extern char **environ;

int main(int argc, char *argv[])
{
        for ( ; *environ; ++environ)
                cout << *environ<< endl;
        return 0;
}  

 


Binary File I/O

Binary file i/o is similar to normal file i/o but different in that read and write operations to a file are done in binary.

We can use the standard fstream functions to open a binary file for read and write operations by specifying the std::ios_base::binary flag as shown below

To read from a binary file:

std::fstream inFile;
inFile.open("binaryFile.dat", std::ios_base::in | std::ios_base::binary);

To write to a binary file:

std::fstream outFile;
outFile.open("binaryFile.dat", std::ios_base::in | std::ios_base::out | std::ios_base::binary);

Once we have a file open for binary reads and writes we can get data from the stream using the read and write functions.

The read and write funcitons take 2 parameters:

1 - A char* of some kind. 2 - A size, specified in bytes.

Reading a double from a binary file:

char readBuffer[256];
inFile.read(readBuffer, sizeof(double));
double whatWeJustRead = *(reinterpret_cast<double*>(readBuffer));

Reading an integer from a binary file:

char readBuffer[256];
inFile.read(readBuffer, sizeof(int));
int whatWeJustRead = *(reinterpret_cast<int*>(readBuffer));

Reading character data from a binary file is a little different:

char destinationBuffer[256];
char readBuffer[256];
inFile.read(readBuffer, 256); // 256 is the size of the read buffer in this case
int amountRead = inFile.gcount();
strncpy_s(destinationBuffer, 256, readBuffer, amountRead); 

There are no reinterpret_cast calls here because by default fstream read treats everything like a char*.

Writing to a binary file uses a similar mechanism.

Writing a double:

double someDouble = 123.456;
outFile.write(reinterpret_cast<char*>(&theDouble), sizeof(double));

Writing an integer:

int someInt = 987;
outFile.write(reinterpret_cast<char*>(&someInt), sizeof(int));

Again, writing out character data is a little different because fstream write treats data as a char* by default.

char someCharData[256];
outFile.write(someCharData, 256);

Some other useful functions when reading and writing from fstream are:

seekp and seekg

As well as

tellp and tellg

seekp and seekg move the put and get cursors to specific spots in a file.

tellp and tellg return the current positions of the put and get cursors in the file.

To use the files you must specify an offset and a starting position such as:

inFile.seekg(30, std::ios_base::beg); // Sets read cursor 30 bytes past start of file
inFile.seekg(15, std::ios_base::end); // Sets read cursor 15 bytes from end of file
int pos = inFile.tellg(); // Returns position of read cursor
outFile.seekp(pos, std::ios_base::beg); // Sets write cursor 5 bytes past start of file
outFile.seekp(10, std::ios_base::end); // Sets write cursor 10 bytes from end of file
outFile.tellp(); // Returns position of write cursor

These functions are very useful when trying to do random access to data in a file. Knowledge of these functions will likely be necessary for the successful completion of the project.


References