8: Files and Streams

What We Will Cover


Continuations

Homework Questions?

  • Nothing

Feedback on CodeLab

More About Arrays

8.1: Streams and File I/O

Objectives

At the end of the lesson the student will be able to:

  • List the types of files
  • Describe the purpose of a stream
  • Read and write text files

8.1.1: About Program I/O

  • Program I/O = Program Input/Output
    • Input to and output from programs
  • Input can be from a keyboard, network or file
  • Output can be to a display screen, printer, network or file
  • Note that files can be both input and output devices for programs
  • Advantages of file I/O
    • Data still exists after the program ends
    • Input can be automated (rather than entered manually)
    • Output from one program can be input to another
  • To store and retrieve data in a file, we need two items:
    • A file
    • A file stream object
  • We will look at files first

8.1.2: Files

File: a collection of data stored under a common name on a storage medium.

  • Files provide long-term storage of large amounts of data
  • Usually, you store files on non-volatile storage mediums
    • Magnetic disks
    • Optical disks
    • Flash storage, like USB storage devices
  • File are a single sequence of bytes
  • Byte 0 Byte 1 Byte 2 ... Byte n−1 End-of-file marker

  • The OS keeps track of the number of bytes in a file
    • Using either an end-of-file marker or counting the number of bytes
  • Files must have a name
    • Naming requirements depend on the underlying operating system (OS)
  • The OS organizes files into directories

8.1.3: Types of Files

  • All data and programs are ultimately just zeros and ones
  • Each binary digit can have one of two values: 0 or 1
  • A bit is one binary digit
  • A byte is a group of eight bits
  • Programs group files into two broad categories: text and binary

Text Files

  • In text files, the bits represent printable characters
  • Files are usually stored as one byte per character (ASCII)
  • Each record (like a line) are often delimited by end-of-line characters
    • Macintosh (before OS-X): "\r"
    • Unix: "\n"
    • Windows: "\r\n"
  • Individual fields within a record are often delimited by commas, tabs or other special characters
  • Source code is stored as a text file
  • You can read text files because each byte is translated by display devices into readable characters

Binary Files

  • Everything other than text is called binary
  • Each bit represents some type of encoded information
    • E.g.: program instructions or numeric data
  • Binary files are easily read by the computer but not by humans

For Example

  • If we wanted to store an int value of 1234 in a file
  • We know an int is four bytes long
  • We can store our value as either 1234 or "1234"
  • How we store it determines what is actually written to the file

Description Byte 0 Byte 1 Byte 2 Byte 3
(int) 1234 as binary bits 00000000 00000000 00000100 11010010
(int) 1234 as bytes 0 0 4 210
"1234" as char's '1' '2' '3' '4'
"1234" as ASCII codes 49 50 51 52

8.1.4: Streams

Stream: a one-way transmission path that either delivers data to a destination (screen, file, etc.) or that takes data from a source (keyboard, file, etc.)

  • A stream connects a program to an I/O object
  • Input stream: an object that provides a sequence of bytes to a program
  • Output stream: an object that accepts a sequence of bytes from a program
  • cin and cout are input and output streams

File Streams

File stream: a one-way transmission path used to connect a program to a file.

  • File streams can be either input or output streams
  • File input streams receive data from a file
  • File output streams send data to a file
  • File I/O also uses streams of type ifstream and ofstream
  • Each file your program uses will need a separate file stream object

Streams and objects

  • Streams are objects and thus cin and cout are objects
  • Objects are special variables that can have a function associated with them
  • To call a function of an object, you use the dot operator
  • That is why we the following code to format output
cout.setf(ios::fixed);     // fixed notation, not scientific
cout.setf(ios::showpoint); // show decimal point
cout.precision(2);         // show 2 decimal places

8.1.5: Example of File I/O

  • As an example, let us write an application that reads and writes files
  • We read a file named infile.txt, which contains the following 3 values:
  • 10
    20
    30
    
  • We sum the numbers and write the sum to a second file named outfile.txt
  • What do you notice that is different about the following code?
/**
 * Reads three numbers from the file infile.txt,
 * sums the numbers, and writes the sum to the
 * file outfile.dat.
 */
#include <fstream>
#include <iostream>
using namespace std;

int main() {
    ifstream inStream;
    ofstream outStream;

    inStream.open("infile.txt");
    if (inStream.fail()) {
        cout << "Input file opening failed.\n";
        exit(1);
    }

    outStream.open("outfile.txt");
    if (outStream.fail()) {
        cout << "Output file opening failed.\n";
        exit(1);
    }

    int first, second, third;
    inStream >> first >> second >> third;
    outStream << "The sum of the first 3\n"
              << "numbers in infile.txt\n"
              << "is " << (first + second + third)
              << endl;

    inStream.close();
    outStream.close();

    cout << "Processing completed\n";

    return 0;
}
  1. How does the program get its input?
  2. Where is the program output?

8.1.6: Procedure For File I/O

  1. Place the following include directives in your program file:
  2. #include <fstream>   // for file I/O
    #include <iostream>  // for cout
    using namespace std;
    
  3. Declare names for input and output streams
  4. ifstream inStream;
    ofstream outStream;
    
  5. Connect each stream to a file using open() and check for failure
  6. inStream.open("infile.txt");
    if (inStream.fail()) {
        cout << "Input file failed to open.\n";
        exit(1);
    }
    
    outStream.open("outfile.txt");
    if (outStream.fail()) {
        cout << "Output file failed to open.\n";
        exit(1);
    }
    
  7. Read or write the data.
    • Read from a file with inStream like using cin
    • inStream >> first >> second >> third;
      
    • Write to a file with outStream like using cout
    • outStream << "first = " << first << endl;
      
  8. Close the streams when finished
  9. inStream.close();
    outStream.close();
    

8.1.7: Summary

  • Programs use streams for input and output
  • cin and cout are types of streams
  • File I/O also uses streams of type ifstream and ofstream
  • Streams are objects and have functions associated with them
  • Some of the functions we call for file I/O include:
    • open("fileName"): establishes a connection from the stream to the file
    • fail(): tests the stream for errors
    • close(): closes the connection of the stream to the file
  • You use input streams like you use cin
  • inStream >> first >> second >> third;
    
  • You use output streams like you use cout
  • outStream << "first = " << first << endl;
    
  • To halt execution of your program on error, use the exit function
  • exit(1);

Exercise 8.1

With a partner, if needed, take 5 minutes to complete the following:

  1. Start a text file named exercise8.txt.
  2. Prepare the exercise header as described in the HowTo on submitting exercises
  3. Label this exercise: Exercise 8.1
  4. Submit all exercises for today's lesson in one file unless instructed otherwise
  5. Complete the following and record the answers to any questions in exercise8.txt.

Specifications

  1. Start a program file named copyone.cpp with one main function. Make sure that the program compiles.
  2. Declare and open an input stream named fin that reads from a file named infile.txt.
  3. Declare and open an output stream named fout that writes to a file named outfile.txt.
  4. Add code to read the first number from the input stream and save it to the output stream.
  5. Add code to close both of the streams.
  6. Compile and run your program.
  7. View the outfile.txt file to verify that one number was copied.
  8. Submit your completed program along with exercise8.txt.
  9. Q1: A program has read half of the lines in a file. What must the program do to reread the first line of the file a second time?

8.2: More I/O Topics

Objectives

At the end of the lesson the student will be able to:

  • Append data to files
  • Check for end-of-file conditions
  • Code user input as file names
  • Code stream parameters in functions

8.2.1: Alternative Syntax for Open

  • It is possible to create a stream and open a file in one step
  • Rather than:
  • ofstream outStream;
    outStream.open("important.txt");
    
  • You can use:
  • ofstream outStream("important.txt");
    

8.2.2: Appending to a File

  • The standard open operation for writing begins with an empty file
  • Even if the file exists you loose all the contents
  • To prevent loosing the information, you must open for appending to the file
  • ofstream outStream("important.txt", ios::app);
    
  • If the file doesn't exist then ofstream creates it
  • If the file exists then ofstream positions itself to append to the end
  • The second argument is a constant defined in class ios

8.2.3: Checking for End-of-File

  • Sometimes you do not know how many lines are in a file
  • To solve this, the typical approach is to use a loop to process the file
  • Then we use a sentinel value from the file input to end the loop
  • Whenever our program does a read operation it returns a bool value
  • We can use this bool value as our sentinel

For Example

#include <fstream>   // for file I/O
#include <iostream>
using namespace std;

int main(void) {
    ifstream inStream("infile.txt");
    if (inStream.fail()) {
        cout << "Input file failed to open.\n";
        exit(1);
    }

    double next, sum = 0;
    int count = 0;
    while(inStream >> next) {
        sum = sum + next;
        count++;
    }
    cout << "average = " << (sum / count) << endl;
    inStream.close();

    return 0;
}

  • The expression (inStream >> next) returns true if successful
  • Returns false if the program attempts to read beyond the end of a file

Reading Complete Lines

  • Sometimes you need to read and process an entire line
  • For this, we can use the getline() function of the string class
  • This works in a loop as well:
  • while(getline(inStream, next)) {
        cout << line << endl;
    }
    

8.2.4: File Names as Input

  • Note that the argument to open() is a string type
  • inStream.open("infile.txt");
    
  • Instead of a literal string, we can use a string variable
  • However, we must use the c_str() function to convert to a C-string

For Example

#include <fstream>   // for file I/O
#include <iostream>
using namespace std;

int main(void) {
    string filename, line;

    cout << "Enter a file name: ";
    cin >> filename;

    ifstream inStream(filename.c_str());
    if (inStream.fail()) {
        cout << "Input file failed to open.\n";
        exit(1);
    }

    while(getline(inStream, line)) {
        cout << line << endl;
    }

    return 0;
}

8.2.5: Stream Parameters

  • Stream types can be formal parameters in functions
  • However, they must be call-by-reference parameters
  • void sayHello(ostream& aStream) {
        aStream << "hello\n";
    }
    
  • Note that the above parameter type is ostream without an 'f'
  • istream (no 'f') parameters accept cin or ifstream objects as arguments
  • ostream (no 'f') parameters accept cout or ofstream objects as arguments
  • Using this feature, we can make functions work for both files and user I/O
  • We can take this concept even further and use default arguments
  • void sayHello(ostream& aStream = cout);
    
    void sayHello(ostream& aStream) {
        aStream << "hello\n";
    }
    
  • Note that the default value is given in the prototype, not the definition
  • You must give the default argument the first time the function is declared

8.2.6: Other File Operations

  • Some file operations are not supported by C++ streams
  • In these cases, you use the C-style functions
  • Note that these functions take C-string arguments
  • Thus you must use the c_str() function when using string variables

Commonly Used C-Functions for File Manipulation

Function Description
remove(fileName) Deletes the file specified by the C-string fileName.
rename(oldName, newName) Changes the file or directory name specified by the C-string oldName to the newName.
perror(message) Print the error message specified by the C-string along with the system error message.
system(command) Executes a command specified by the C-string like you were using the command line.

Example to Remove a File

string fileName;
cout << "File to remove: ";
cin >> fileName;
int result = remove(fileName.c_str());
if (result == 0) {
    cout << "File successfully removed\n";
} else {
    perror("Error removing file\n");
}

Example to Rename a File

int result;
string oldName, newName;
cout << "Old file name: ";
cin >> oldName;
cout << "New file name: ";
cin >> newName;
result = rename(oldName.c_str(), newName.c_str());
if (result != 0 ) {
    perror( "Error renaming file" );
}

Examples Using system() Function

system("ls");      // list files
system("clearn");  // clear the screen

8.2.7: Summary

  • The standard open operation will create an empty file
  • You can open a file for appending data by using an extra argument
  • ofstream outStream("important.txt", ios::app);
    
  • Sometimes you do not know how many lines are in a file
  • To solve this, the typical approach is to use a loop to process the file
  • while(inStream >> line) {
        cout << line << endl;
    }
    
  • You can use string variables to open files
  • However, we must use the c_str() function to convert to a C-string
  • ifstream inStream(filename.c_str());
    
  • Streams can be arguments to a function, but you must use call-by-reference
  • Type istream for function parameters works for both cin and ifstream
  • Type ostream for function parameters works for both cout and ofstream
  • C++ allows you to specify default arguments of a stream type as well
  • void sayHello(ostream& aStream = cout) {
        aStream << "hello\n";
    }
    

Exercise 8.2

  1. Label this exercise: Exercise 8.2
  2. Submit all exercises for today's lesson in one file unless instructed otherwise
  3. Complete the following and record the answers to any questions in exercise8.txt.

Specifications

  1. Copy the following code into a text editor and save the file as showfile.cpp.
  2. Add a prototype for a function named toScreen() that takes an istream parameter and returns nothing.
  3. Stub out the function toScreen() and add a function call in main().
  4. Define the function toScreen() so that it reads one line at a time from the input stream parameter using function getline() and displays each line it reads to the screen.
  5. Submit your showfile.cpp program along with your exercise8.txt file
  6. Q1: What type of value is returned by the statement:

    (inStream >> next)

    Q2: What value is returned by this statement when it sucessfully reads a value?

    Q3: What value is returned by this statement when it tries to read beyond the end of a file?

#include <fstream>   // for file I/O
#include <iostream>
using namespace std;

// Add function prototype for toScreen() here

int main(void) {
    string filename, line;

    cout << "Enter a file name: ";
    cin >> filename;

    ifstream inStream(filename.c_str());
    if (inStream.fail()) {
        cout << "Input file failed to open.\n";
        exit(1);
    }

    // Call function toScreen() here

    return 0;
}

// Add new function toScreen() here

8.3: Formatting Output

Objectives

At the end of the lesson the student will be able to:

  • Write code using output formatting functions
  • Write code using manipulators

8.3.1: Formatting Functions

  • Formatting functions are used to set the layout of the output
  • These same functions apply to cout
  • precision: sets the number of digits to display for floating-point numbers
  • outStream.precision(2);
  • setf: sets a format flag for the stream
  • outStream.setf(ios::fixed);      // Fixed notation
    outStream.setf(ios::scientific); // Scientific notation
    outStream.setf(ios::showpoint);  // show decimal point
    outStream.setf(ios::showpos);    // Show + signs
    outStream.setf(ios::right);      // Right justify
    outStream.setf(ios::left);       // Left justify
    

  • unsetf: clears a format flag
  • outStream.unsetf(ios::showpos);  // No more + signs
    
  • width: set or return the field width
    • Width expands automatically so that entire item is always output
    • Only applies to one number
    outStream.width(3);
    outStream << 3 << endl; // ouputs <space><space>3
    outStream << 123.45 << endl; // ouputs 123.45
    

8.3.2: Manipulators

  • Manipulator: a function called in a nontraditional way
  • Manipulators in turn call member functions
  • Manipulators may or may not have arguments
  • To use manipulators you may need to include:
  • #include <iomanip>
    using namespace std;
    
  • endl: outputs a newline character
  • setw: sets the field width from that point onward
  • cout << "Start" << setw(4) << 10 << setw(6) << 20;
    
  • setprecision: sets the number of digits of precision
  • cout << "$" << setprecision(2) << 8.3  << endl;
    
  • Further information: C++ I/O Flags (includes manipulators)

8.3.3: Saving Flag Settings

  • Flag settings 'stay' until changed
  • Flags for functions precision() and setf() can be saved and restored
  • Calling precision() with no arguments returns the current setting
  • Calling function flags() returns the current flag settings as a type long
  • To save the current settings:
  • int precisionSetting = cout.precision();
    long flagSettings = cout.flags();
    
  • Then you can change settings and output whatever you like
  • cout.setf(ios::fixed);
    cout.setf(ios::showpoint);
    cout.precision(2);
    
  • To restore the settings:
  • cout.precision(precisionSetting);
    cout.flags(flagSettings);
    

8.3.4: Example of Formatting Data

  • Following example is adapted from textbook pages 526-527
  • As an example of formatting data, consider the following data file:
  • 12.34    -9.87654
    2.3131     -89.506
    
    12.33333333 92.8765
    -1.234567e2
    
  • We want to write a program to take the raw data from the file and format it neatly
  • Also, we will write first to the screen and a then to a file
/**
 * Reads all the numbers in the file rawdata.txt
 * and writes the numbers to the screen and to
 * the file neat.txt in a neatly formatted way.
 */
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;

void formatData(istream& messyFile, ostream& neatFile,
        int precision, int fieldWidth);

int main()
{
    ifstream fin("rawdata.txt");
    if (fin.fail()) {
        cout << "Input file opening failed.\n";
        exit(1);
    }

    ofstream fout("neat.txt");
    if (fout.fail()) {
        cout << "Output file opening failed.\n";
        exit(1);
    }

    formatData(fin, cout, 5, 12);

    // Close and reopen input file
    fin.close();
    fin.clear(); // allows reuse of stream
    fin.open("rawdata.txt");
    if (fin.fail()) {
        cout << "Input file opening failed.\n";
        exit(1);
    }

    formatData(fin, fout, 5, 12);

    fin.close();
    fout.close();

    cout << "End of program.\n";
    return 0;
}

void formatData(istream& messyFile, ostream& neatFile,
        int precision, int fieldWidth) {
    neatFile.setf(ios::fixed);
    neatFile.setf(ios::showpoint);
    neatFile.setf(ios::showpos);
    neatFile.precision(precision);

    double next;
    while (messyFile >> next) {
        neatFile << setw(fieldWidth) << next << endl;
    }
}

8.3.5: Summary

  • C++ has many formatting functions to set the layout of the output
    • You can use them with cout and any ofstream type
  • You can use manipulators to change formatting within the stream
    • Affects output after the manipulator is used and onwards
    • You can use manipulators to format one value at a time
  • Also, you can save and restore flag settings

Exercise 8.3

  1. Label this exercise: Exercise 8.3
  2. Submit all exercises for today's lesson in one file unless instructed otherwise
  3. Complete the following and record the answers to any questions in exercise8.txt.

Specifications

  1. Run the following code:
  2. #include <iostream>
    #include <iomanip>
    using namespace std;
    
    
    int main(void) {
        cout << "*";
        cout.width(5);
        cout << 123
             << "*" << 123 << "*" << endl;
        cout << "*" << setw(5) << 123
             << "*" << 123 << "*" <<  endl;
    
        return 0;
    }
    
    

    Q1: To how many number fields does width() or setw() apply?

  3. Run the following code:
  4. #include <iostream>
    #include <iomanip>
    using namespace std;
    
    
    int main(void) {
        cout << "*" << setw(5) << 123;
        cout.setf(ios::left);
        cout << "*" << setw(5) << 123;
        cout.setf(ios::right);
        cout << "*" << setw(5) << 123 << endl;
    
        return 0;
    }
    
    

    Q2: By default, are numbers output ios::left or ios::right?

  5. Run the following code:
  6. #include <iostream>
    #include <iomanip>
    using namespace std;
    
    
    int main(void) {
        cout << "*" << setw(3) << 12345 << "*\n";
    
        return 0;
    }
    
    

    Q3: How many digits are displayed? Why?

Wrap Up

    Reminders

    Due Next: A7: Tic-Tac-Toe (10/28/04)
    Exercise 7 and CodeLab Lesson 7 (10/28/04)

  • When class is over, please shut down your computer
  • You may complete unfinished exercises at the end of the class or at any time before the next class.
  • Instructions on submitting exercises are available from the HowTo's page.

Home | WebCT | Announcements | Day Schedule | Eve Schedule
Course info | Help | FAQ's | HowTo's | Links

Last Updated: December 07 2004 @14:50:21