Programming in C++

Text Files, Strings, Random Numbers, and Member Fu

Working with Files

Working with files is not hard and is actually quite fun. The file streams give you access to read and write data to and from your hard drive. After all, that is whole idea of working with files. Your program will either need to read data from a file on your hard drive, or you will need to save data to a file on your hard drive. Files can be text or binary. Text files are first converted into ASCII by the computer before any manipulation can be done. You can create and view text files using a text editor such as notepad on a windows pc. Your compiler should also be able to view and create text files. If your compiler does have this option, you should use it when viewing text files created from your program because it will give you the most accurate representation of what your program actually produced. Binary files are the multimedia files on your computer; they may have extensions such as: obj, exe, gif, jpg, avi (etc). I will only brush through the topics of text files in this section. I may discuss binary files in later articles.

Text Streams
The standard objects cin, cout, and cerr are predefined text streams that are defined in the header file #include <iostream.h>. All other text objects that I will discuss must be explicitly declared to be of type ifstream (input file ) or ofstream (output files).

ifstream - used for input files when you want to read data from a file; the file should exist on the hard drive or an error will occur.

ofstream - used for output files when you want to write data to a file; if the file doesn't exist, one will be created; if the file does exist, it will be overwritten.

Both of these class types are declared in the header file #include <fstream.h> so when you use them in your program, make sure you have included this header file. It is important to remember that before you read from or write to a file, the file must be open. You open a file by attaching a "logical file name" to a "physical file name". The "logical file name" is a variable for the file that you will use throughout the rest of your program. The "physical file name" is the path of the file on disk. Once you have the two names, the file can be opened by using the member function open ( ).

NOTE: When you are specifying the path of a file on your hard drive, you must use a double slash " \\ " instead of the normal single slash. This is done because in C++, when you issue a single slash, the compiler thinks a command will be after it. By using a double slash, the compiler recognizes that a file is being worked with.

Example (declaring and opening files):

ifstream myInputFile;
ofstream myOutputFile;

// Using the open ( ) member function, the following stmt. associates
// the stream variable myInputFile (logical name) with the file NAMELIST.DAT (physical name)
// on drive D: of the computer's hard drive.

myInputFile.open ("D:\\NAMELIST.DAT");

// From this point on in the program, the logical name should be used
// to refer to the input stream

// Using the open ( ) member function again, the following stmt. associates
// the stream variable myOutputFile (logical name) with the file SUMMARY.RPT (physical name)
// on drive D: of the computer's hard dive.

myOutputFile.open ("D:\\SUMMARY.RPT");


You may encounter two errors when opening files: the file does not exist (for input), or there is a disk error such as hard drive corruption. To check for an error during opening, you can use the following piece of code:

ifstream myInputFile;
myInputFile.open ("C:\\DATA.DAT");

if (!myInputFile)
cout << "Error opening file: " << "C:\\DATA.DAT";
else
.
.
use file
.
.


When you are finished using a file, you should close the file stream. Closing a file is extremely important if you are writing to an output file stream. To close a file, you use the member function close ( ) in the form.:

   logicalname.close ( );

Special Note About Output Streams
You open the file in the same way you open an input stream, except you don't necessarily need to check to see if the file exists. When an output stream is opened, the listed physical file will be overwritten; if not, the file will be created. Also note that once an output stream is open, you can "write" to it the same way you do with cout. Instead of using cout, you need to use the logical name of the output file.

The following full program example uses strings and member fucntions which I have not covered yet [for strings see Strings] [for member functions see Member Functions]. Study the following program to see how input files and output files are used in a program.

................
PROBLEM: Suppose a data file (text file) contains the following information on each line for each student in a class:
1 - student name [columns 1 to 30]
2 - number of credit hours [columns 32 to 34]
3 - number of quality points [columns 36 to 38 or 36 to end]
Write a program that will read through such a file and will display, for each student, the following:
   Name
   Credit Hours
   Quality Points
   GPA
````````````````

///////////// BEGIN PROGRAM [PROGRAM WRITTEN WITH DEV-C++ COMPILER] /////////////

#include <iostream.h>
#include <stdlib.h>
#include <fstream.h>

void main ( )
{
   int hours, qualityPoints;
   float gpa;

   ifstream gradeFile;
   char studentName[31];
   char fileName[81];

   cout << endl << endl << endl;
   cout << "Enter the name of the grade file: ";
   cin >> fileName;
   cout << endl << endl;

   gradeFile.open ( filename );
   if ( !gradeFile )
   {
           cout << endl << "Error opening file: " << fileName << endl;
           cout << endl << "Program will be terminated" << endl;
           system ( "PAUSE" );
           exit(1);
   }

   while ( !gradeFile.eof ( ) )
   {
           gradeFile.get ( studentName, 31 );
           gradeFile >> hours >> qualityPoints >> ws;
           gpa = float ( qualityPoints ) / hours;
           cout << studentName << "     " << hours << "     " << qualityPoints << "     " << gpa;
   }

   gradeFile.close ( );

}

///////////// END PROGRAM /////////////

Now that I have introduced you to text files and have shown you how they can be used in programs, you may be wondering how to append a file. That is, how do you add data to a file that already has data on it without erasing all of the previous data. You may think there is a lot to this type of problem, but the code is actually quite simple. Read on for more about appending files...

Appending a File

Despite what some people may first think, appending a file is really not difficult at all. Appending a file means that you want to add data or another file to the end of a file without erasing or deleting any previous data on the file. Some pre-conditions must take place before the appending can be done. If you are appending information to the end of a file, the file must exist. If you are appending a file to the end of another, both of the files must exist. The sample complete program provided will append two files and store the result in a third file. Study the code to see how it is done. The program uses pointers, which I haven't discussed, as parameters [see Using Pointers (section 7) for info]. Also note that the program was compiler using Dev-C++ compiler.



/////////////////////////////////////////////////////////////////////

#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include <fstream.h>

void appendFile(char* filename1, char* filename2, char* filename3);

int main()
{
     char filename1[80], filename2[80], filename3[80];

     cout << "Enter the name of the first file: ";
     cin >> filename1;
     cout << "Enter the name of the second file (to be appended to the end of " << filename1 << " ): ";
     cin >> filename2;
     cout << "Enter the name of the file which will contain the appended files: ";
     cin >> filename3;

     appendFile(filename1, filename2, filename3);

     cout << endl
           << "The files were successfully appended to [" << filename3 << "]" << endl << endl;

     system("PAUSE");
     return 0;
}// end main();

void appendFile(char* filename1, char* filename2, char* filename3)
{
     char ch;
     ifstream file1, file2;
     ofstream file3;

     file1.open(filename1);

     if (!file1)
     {
           cout << "Error opening file: " << filename1 << endl;
           exit(1);
     }

     file2.open(filename2);

     if (!file2)
     {
           cout << "Error opening file: " << filename2 << endl;
           exit(1);
     }

     file3.open(filename3);

     while (file1.get(ch))
           file3.put(ch);

     file1.close();

     while (file2.get(ch))
           file3.put(ch);

     file2.close();
     file3.close();
}// end appendFile()

/////////////////////////////////////////////////////////////////////


Well that's all I have to say about working with files. As you can tell, we are starting to get into the good stuff. I know you are probably tired of all this talk about characters and integers so I think it's time to start talking about strings. Read on to explore the wonderful world of strings...

Strings

You can think of a string as a "group" of characters. You can also relate a string and an array because a string can be thought of as an array of characters. Let's first refresh ourselves on how to declare single character variables in C++, and then discuss how to declare and initialize strings.

If you have a program that asks the user if he/she is married, you will most likely store the value of this prompt in a variable declared as char because there is only one answer of two possible values: Y (yes) or N (no). A variable declared as char allocates one char for the variable. In this case a Y would be stored or a N would be stored. If the user were to enter more than one character at the prompt, such as YES, only the Y would be stored in the variable.

While a variable, such as

   char x;

allocates one character for x, a variable declared as

   char x[21];

allocates practically 20 characters for x and room for a null terminater ( '\0' ).

As shown above, char x[21] is a string variable. You can store a value for x that is up to 20 characters long. All strings in C++ end with a null terminator because it denotes the end of the string. Whenever you try to search through strings for certain values or manipulate strings, keep in mind that all strings have a null terminator as the last value. So, if your program asks the user to enter his/her first name, you could declare the string as char firstName[16].

You can initialize a string as follows:

   variable[max num of chars + 1] = "value";

   Ex: firstName[8] = "Michael";

Assignment statements when dealing with strings are different compared to assignment statements dealing with integers and floating-point variables. You can assign a value to a string using the member function:

   strcpy (destination, source);
   EX: strcpy (nameOfLang, "C++");

where source is a group of characters enclosed in quotations or a variable, and destination is the variable that you want the source to be stored in.

For example, if you have a string variable named firstName and a string variable named newFirst, you could assign the contents of firstName to newFirst by using the following statement:

   strcpy (newFirst, firstName);

Let's look at some examples of how strings can be used in programs.

Example 1:

Write a section of code that will calculate and display (to screen) the number of vowels contained in a string identified as mixedString that was given a value earlier in the program:

   int count = 0;
   int k = 0;

   while ( mixedSting[k] != '\0' ) // '\0' is the null terminator
   {
           if ( str[k] == 'A' || str[k] == 'a'
                str[k] == 'E' || str[k] == 'e'
                str[k] == 'I' || str[k] == 'i'
                str[k] == 'O' || str[k] == 'o'
                str[k] == 'U' || str[k] == 'u' )
           count++;
           k++;
   }

cout << "The number of vowels contained in [" << mixedString << "] is: " << count << endl << endl;


Example 2:

Write a section of code that will turn all of the commas in a string identified as string1 into semi-colons:

   int k = 0;

   while ( string1[k] != '\0' )
   {
           if ( string1[k] == ',' )
                string1[k] = ';';
           k++;
   }



//////////////////////////////// Sample Program Compiled with Dev-C++ ////////////////////////////////

#include <iostream.h>
#include <stdlib.h>
#include <iomanip.h>
#include <fstream.h>
#include <string.h>

int countVowels(char str[]);

int main()
{
     char myString[25], myString2[25];
     int numberVowels;
     int k = 0;

     cout << endl;
     cout << "Enter a string to be evaluated: ";
     cin.get(myString, 25);
     cin.ignore(80, '\n');

     cout << endl;
     cout << "Enter a second string to be evaluated [any commas will be changed to asterisks]: ";
     cin.get(myString2, 25);
     cin.ignore(80, '\n');

     while (myString2[k] != '\0')
     {
           if (myString2[k] == ',')
                 myString2[k] = '*';
           k++;
     }

     numberVowels = countVowels(myString);

     cout << endl << endl;
     cout << "The number of vowels in " << myString << " is " << numberVowels;
     cout << endl << endl;
     cout << "Your updated second string: " << myString2;
     cout << endl << endl;

     system("PAUSE");
     return 0;
}// end of main()

int countVowels(char str[])
{
     int count = 0, k = 0;

     while (str[k] != '\0')
     {
           if (str[k] == 'a' || str[k] == 'A' || str[k] == 'e' || str[k] == 'E'
                 || str[k] == 'i' || str[k] == 'I' || str[k] == 'o'
                 || str[k] == 'O' || str[k] == 'u' || str[k] == 'U')
               count++;
           k++;
     }// end while

     return count;
}// end countVowels

//////////////////////////////// Sample Program Compiled with Dev-C++ ////////////////////////////////


With strings covered, I would now like to move on to random numbers. Generating random numbers is not difficult at all due to some "built in" functions. Read on for more...

Random Numbers

There may be times when you need to generate and use random numbers in your programs. Maybe you are writing code for a game program in which you need to simulate rolling dice. If you truly want to have a fair game, you should use random numbers. The code to generate random numbers differs depending on your compiler. In this tutorial, I am assuming you are using Dev's C++ compiler. How do random numbers get generated? Well, every computer has an internal clock and this clock makes it possible to seed random numbers. In standard C++, the statement:

   srand(unsigned(time(&t)));

initializes, or "seeds", the random number generator. You must use this code before attempting to get any random number. You should only use this statement once in a program. It does not generate a random number; it simply gets the generator ready.

t must be declared as: time_t t;

You also must include the following two header files:

   #include <stdlib.h> // for srand ( ) and rand ( )
   #include <time.h> // for time ( ) and time_t

To get a random number in the range 0 to 100, you could use:

   int x = rand ( ) % 101;

Suppose you want to generate random numbers in the range -15 to 33. After initializing the random number generator, use:

   var = rand ( ) % 49 - 15;

In general, use the following form depending on the range of random numbers you wish to generate:

   rand ( ) % [number of integers in desired range] + [lowerbound];

Example:

Write code that will fill the contents of an integer array, MAXSIZE of 50 integers, named intArr with randomly chosen numbers in the range -10 to 110. NOTE: If you are not familiar with arrays, see [One-Dimensional Arrays (section 8)].



   srand(unsigned(time(&t)));
   time_t t;

   for (int k = 0; k < 50; k++)
       intArr[k] = rand() % 121 - 10;


Our next topic is the member functions which are built in to C++ that allow programmers to perform operations necessary for programs to complete their overall purpose. Read on for more...

Member Functions

Member functions allow programmers to manipulate areas in programs and perform operations that are necessary for a program to complete its overall purpose. For example, if you try to extract a string using a cin statement and extraction operator >>, extraction will stop at the first white space character. So if you are working with a string that contains nothing but characters, you are fine. But if you are working with a string that contains a space (considered white space), such as a string that would contain a person's full name, you would run into some problems. Obviously, something needs to be done so you can extract the whole string including the space or spaces (considered white space). This is where a member function called get ( ) comes into play.

get ( )
The get ( ) function is a member function of the istream classes. One version has two required parameters and one optional one. The two required parameters are a string variable (array of chars) and a length (max. number of chars to get + 1). The optional one, by default is the new line character '\n' and is called the delimeter. When get ( ) is called during execution, chars will be extracted from the input stream and assigned into the given string until one of the following occurs:
1 - there are no more characters to be extracted (end-of-file has been reached)
2 - the next available character for extraction would be the delimeter
3 - the # of chars extracted is one less than the value of the second parameter

Regardless of which condition stops the extraction, a null char ( '\0' ) will be stored in the string following the last character extracted.
NOTE: even if the delimeter is reached, get ( ) will not extract it.

Example:

Write a section of code that will store the value of input by the user (user's full name) into a variable named fullName that contains at most 40 characters:

   cout << "Enter your full name: ";
   cin.get(fullName, 40);


peek ( )
The peek ( ) function is another member function of the input stream classes. It enables you to "peek" at or look ahead in an input stream. If the input stream contains additional data, peek ( ) will return the value of the next char to be extracted; otherwise, it will return EOF, which represents the end-of-file.

Example:

Write the beginning piece of a section code that will determine if there is more data at a certain point in a text file name gradeFile that is being processed:

   .
   .
   .
   while ( gradeFile.peek ( ) != ' \n ' )
   {
           .
           .
           .


eof ( )
The eof ( ) function is another member function of the input stream classes that returns true if the input stream is at the end-of-file; otherwise, it returns false.

Example:

Write the beginning piece of a section of code that will continue processing a file called gradeFile as long as the end-of-file has not been reached:

   .
   .
   .
   while ( !gradeFile.eof ( ) )
   {
           .
           .
           .


ignore ( )
The ingore ( ) will ignore or "skip" over a specified amount of chars or all chars until '\n' has been found.

Example:

   myInFile.ignore ( 100, '\n' );     ---> will "skip" over next 100 chars


With text files, strings, and member functions out of the way, I feel it is a good time to discuss functions, especially user-defined functions. Read on to learn more about the powerful world of functions...

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Memory is like an orgasm. It's a lot better if you don't have to fake it.” - Seymour Cray