C++ Ripped Apart :: Section 4
Author: Mike Ware
Website: [warebiz] :: "The Programmer's Domain" - http://warebiz.tripod.com
Email: warebiz@yahoo.com
Copyright © 2003 Michael Shawn Ware, All Rights Reserved.


You are here --> [Section 4 :: Text Files, Strings, Random Numbers, and Member Functions]

"Jump To Articles"
    --> Working With Text Files
    --> Appending A File
    --> Strings
    --> Random Numbers
    --> File Input Member Functions


SIDE NOTE: Some example programs in this section use functions. For reference on functions, see [In-Depth Look at Functions (Section 5)].

Working With Text Files

Working with files is not difficult and adds a new dimension to programming. The file streams provide routines to read and write data to and from a hard drive. This is the idea behind working with files. A program will either need to read data from a file on a hard drive or will need to save data to a file on a particular 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, use it when viewing text files created by a program because it will give the most accurate representation of what the program 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/O (Input/Output) Streams
The standard objects cin, cout, and cerr are predefined text streams that are defined in the header file #include <iostream.h>. File objects that I cover 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 following member function: open ( ).

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

Example (declaring and opening files):


// file object declarations
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 after attempting to open a file, use the following piece of code:


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

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

The open( ) function returns a value of 0 if an error occurred while attempting to open the file or a value of 1 indicating success; therefore, the above code would be sufficient. 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 following form of the member function: close ( )

    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 using the same approach as cout. Instead of using cout, use the logical name of the output file.

The following full program example uses strings and member functions 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
````````````````


//////////////////////////////// USING DEV C/C++ V.4 COMPILER //////////////////////////////////////////////

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

const int MAXNAMELEN = 31;
const int MAXFILELEN = 81;

int main ( )
{
    int hours, qualityPoints;
    float gpa;
    char studentName[MAXNAMELEN], fileName[MAXFILELEN];
    ifstream gradeFile;

    cout << endl << endl << endl;
    cout << "Enter full path of grade file: ";
    cin >> fileName;
    cout << endl << endl;

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

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

    gradeFile.close( );
    system("PAUSE");
    return EXIT_SUCCESS;
}

//////////////////////////////// USING DEV C/C++ V.4 COMPILER //////////////////////////////////////////////

Now that I have introduced text files and have demonstrated 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 not difficult. 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 separate third file. Study the code to see how it is done. This program uses pointers, which I haven't discussed, as parameters [see Using Pointers (section 5) for info].


////////////////////////////// USING DEV C/C++ V.4 COMPILER ///////////////////////////////////////

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

const int MAXFILELEN = 80;

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

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

      cout << "Enter full path of first file: ";
      cin >> filename1;
      cout << "Enter full path of second file (to be appended to the end of "
            << filename1 << " ): ";
      cin >> filename2;
      cout << "Enter full path of 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 EXIT_SUCCESS;
}// 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(EXIT_FAILURE);
      }

      file2.open(filename2);

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

      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()

////////////////////////////// USING DEV C/C++ V.4 COMPILER ///////////////////////////////////////

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

Think of a string as a "group" of characters. A string also relates to 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 character byte for the variable. In this case, Y would be stored or N would be stored. If the user were to enter more than one character at the prompt, such as YES, only 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 terminator ( '\0' ).

As shown above, char x[21] is a string variable. You can store a value for x that is up to 20 characters in length. All strings in C++ end with a null terminator which denotes the end of the string. Whenever you try to search strings for certain values or manipulate strings, keep in mind that all strings have a null terminator as the last value. 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";

You can also neglect to specify the size of the character array during initialization. The compiler will then determine its size and handle its internal address appropriately. For example, consider:

    firstName[ ] = "John William Babbage, Jr";

Assignment statements 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 strcpy( ) member function (defined in string.h):

    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);

A key point to remember when assigning strings to a new location is to be sure that the new location is defined as having a capacity large enough to handle the source string. Let's look at some examples of how strings can be used in programs.

Example 1:

Write 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:


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

int main()
{
      int count = 0, k = 0;
      char mixedString[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

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

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

      system("PAUSE");
      return EXIT_SUCCESS;
}

Example 2:

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


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

int main()
{
      int subscript = 0;
      char string1[] = "PROGRAMMING,IN,C++";

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

      cout << string1 << endl << endl;

      system("PAUSE");
      return EXIT_SUCCESS;
}

Final Example:

This program will ask the user to input two strings. The first string will be evaluated for any internal vowels, and any commas contained in the second string will be replaced with asterisks. Study the code carefully:


//////////////////////////////// USING DEV C/C++ V.4 COMPILER ////////////////////////////////

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

const int MAXSTRINGLEN = 25;
const int MAXFLUSH = 80;

int countVowels(char str[]);
void commaToAsterisk(char str[]);

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

      cout << endl << "Enter a string to be evaluated [vowels will be summed]: ";
      cin.get(myString, MAXSTRINGLEN);
      cin.ignore(MAXFLUSH, '\n');

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

      numberVowels = countVowels(myString);
      commaToAsterisk(myString2);

      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 EXIT_SUCCESS;
}// 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

void commaToAsterisk(char str[])
{
      int subscript = 0;

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

}// end commaToAsterisk()

//////////////////////////////// USING DEV C/C++ V.4 COMPILER ////////////////////////////////

With strings covered, I would like to move on to random numbers. Generating random numbers is not difficult due to a few convenient "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 the rolling of 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 assume you are using Dev's C/C++ V.4 compiler. How do random numbers get generated? Well, every computer has an internal clock that 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 generate 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 must also include the following header files:

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

An alternative method for preparing the random generator is by using the following statement:

    srand((unsigned)time(NULL));

Instead of using the time_t t variable, the above code sets the internal values to NULL until a value is attempted to be generated.

After seeding the random generator, random numbers are generated using the rand( ) function. To generate a random number in the range 0 to 100, use:

    int x = rand( ) % 101;

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

    var = rand( ) % 61 - 10;

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 -110 to 110. NOTE: If you are not familiar with arrays, [see One-Dimensional Arrays (section 6)].


    srand((unsigned)time(NULL));

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

The next section covers member functions which are built in to C++. These functions belong mostly to the input/output stream classes. Read on for more...

File Input 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. 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 character space (considered white space), such as a string that would contain a person's full name, you would run into problems. Obviously, something needs to be done so you can extract the whole string including the space or spaces (considered white space). This is when 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:


	const int nameLen = 40;

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

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 and is commonly used to check for more data when processing files. 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 on a certain line contained in a text file named gradeFile during processing:


    .
    .
    .
    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. This function is commonly used to check for end-of-file during file processing.

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( ) function will ignore or "skip" over a specified amount of chars or all chars until '\n' has been found. This function is commonly used to "flush" the input stream after get( ) has been issued.

Example:

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


Study the following program example to see how these functions can be used:


////////////////////////////////////////// USING DEV C/C++ V.4 COMPILER ////////////////////////////////////////////

// PURPOSE: This program uses an input stream file to read data from a specified
//          file and will display the name, # of credit hours, # quality points,
//          and the gpa of each student in the file. The input file is formatted
//          as follows:
//                LINES 1-30: Student Name
//                LINES 31-33: Credits Attempted
//                LINES 34-36: Quality Points Earned
//                LINES 37-END: HONORS Character Symbol
//
//          DATA FILE USED FOR EXAMPLE: [C:\DATAFILES\DATA.DAT]
//
// PROGRAMMER: Mike Ware
// DATE LAST UPDATED:

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

const int NAMELEN = 31;
const int FILENAMELEN = 81;
const int FLUSHLEN = 80;

int main()
{
      int hours, qualityPoints;
      float gpa;
      char honorsSymbol, studentName[NAMELEN], fileName[FILENAMELEN], userName[NAMELEN];
      ifstream gradeFile;

      // set output formatters
      cout << setiosflags(ios:: showpoint | ios::fixed);
      cout << setprecision(2);

      // get user name
      cout << "Before using program, please tell me your full name: ";
      cin.get(userName, NAMELEN);
      cin.ignore(FLUSHLEN, '\n');

      // get input file from user
      cout << endl << "Enter full path of grade file: ";
      cin >> fileName;
      cout << endl << endl;

      // open and verify specified input file
      gradeFile.open(fileName);

      if (!gradeFile)
      {
            cout << endl << "Error opening file: " << fileName << endl;
            exit(EXIT_FAILURE);
      }

      // process file and display results
      while (!gradeFile.eof())
      {
            gradeFile.get(studentName, NAMELEN);
            gradeFile >> hours >> qualityPoints >> honorsSymbol;
            gradeFile >> ws;
            gpa = float(qualityPoints) / hours;
            cout << studentName
                  << "   " << hours
                  << "   " << qualityPoints
                  << "   " << honorsSymbol
                  << "   " << gpa;
            cout << endl;
      }

      cout << endl << "Thanks, " << userName << " for using my capabilites!";

      // close specified input file
      gradeFile.close();

      cout << endl << endl << endl;

      system("PAUSE");
      return EXIT_SUCCESS;
}

////////////////////////////////////////// USING DEV C/C++ V.4 COMPILER ////////////////////////////////////////////
With text files, strings, random numbers, and member functions covered, 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...

Move on to next set of topics: [Section 5] - In-Depth Look at Functions / Pointers

Back to Top