CIS 150 Binary File I/O

Objectives

  • Open a binary file for reading
  • Open a text file for writing
  • Open a text file for readingf and writing
  • Detect and handle errors when trying to open a file
  • Read data from a binary file
  • Write data to a binary file
  • Calculate the size of a binary file
  • Calculate the number of records in a fixed length record binary file
  • Use iteration to process all of the records in a binary file
  • Close a file

Overview

  • Include the proper library: #include <fstream>
  • Usually also: #include <iostream>
  • Make sure the program can see what you want in that library: using namespace std;
  • More precise versions of above
    • using std::fstream;
    • using std::ifstream;
    • using std::ofstream;
  • The steps to processing a file:
    • open the file
    • process the file records
    • close the file
  • You get a reference to the file stream when you open it. That reference is then used to access the file stream, and eventually to close it.

Opening a binary file

  • Note: Reading from a file is input, writing is output.
  • Declaring a file stream variable: fstream file;
  • Opening a binary file named test.bin for reading: file.open("test.bin", ios::binary | ios::in);
  • Easier version of above: ifstream file("test.bin", ios::binary); // for reading
  • Detecting an error opening the file: if (!file) { ... error code ... }
  • Opening a binary file named test.bin for writing (over-writing): file.open("test.bin", ios::binary | ios::out);
  • Easier version of above: ofstream file("test.bin", ios::binary | ios::out); // for writing
  • Opening a binary file named test.bin for reading and writing: file.open("test.bin", ios::binary | ios::in | ios::out);
  • Easier version of above: ofstream file("test.bin", ios::binary | ios::in | ios::out); // for writing

Reading or writing to a file stream

By now you should be familiar with using the insertion operator (<<) to write to an output stream such as cout. You use the insertion operator to write to your output file stream the same way.

You should also be familiar with using the extraction operator (>>) to read from an input stream such as cin. You use the extraction operator to read from your input file stream the same way.

Examples:

  • ofile << num1 << " " << num2 << endl;
  • ifile >> num1 >> num2;

Checking for EOF and closing file streams

  • You do not check for EOF (end of file) on a binary file stream. You calculate the size of the file and work from that.
  • Closing a file stream is easy. Just use its close() function: file.close();

Calculating the size of a binary file

  • Seek to the end of the file: file.seekg(0L, ios::end);
  • Find the position the read pointer is at: long bytes = file.tellg(); // bytes is the file size
  • You can also use the write pointer (seekp(), tellp()), but be consistent
  • Don't forget to return the pointer to the start of the file when you are done: file.seekg(0L, ios::beg);
  • If the file uses fixed length records, you can find out how many records are in the file using: bytes / sizeof(record);

Reading and writing records to a binary file

  • Assume file is a variable that refers to a fstream opened for binary reading and writing.
  • Assume Record is a class that defines what a record looks like in this file.
  • Assume rec is a variable that refers to a Record object: Record rec;
  • Read a record from the file into rec: file.read(reinterpret_cast<char *>(&rec), sizeof(rec));
  • Write a record from rec into the file: file.write(reinterpret_cast<char *>(&rec), sizeof(rec));
  • Once you have the number of records in the file (see previous section), then you can create a loop from 1 through the number of records to read each record in the file.

Example 1

This example is also available here: fileio2.cpp

#include <fstream> #include <iostream> using std::cout; using std::endl; using std::ifstream; using std::ofstream; using std::ios; int main(void) { int n; float x = 0.0f; // specify binary when opening file ofstream ofile("testfileio2.txt", ios::binary); if (!ofile) { cout << "Error: couldn't open output file" << endl; return 1; } // use write function to write binary data for (n = 1; n < 10; n++, x += 25) { ofile.write(reinterpret_cast<char *>(&n), sizeof(n)); ofile.write(reinterpret_cast<char *>(&x), sizeof(x)); } ofile.close(); // specify binary when opening file ifstream ifile("testfileio2.txt", ios::binary); if (!ifile) { cout << "Error: couldn't open input file" << endl; return 2; } // use priming read and loop to read all records ifile.read(reinterpret_cast<char *>(&n), sizeof(n)); ifile.read(reinterpret_cast<char *>(&x), sizeof(x)); while (ifile) { cout << n << ", " << x << endl; ifile.read(reinterpret_cast<char *>(&n), sizeof(n)); ifile.read(reinterpret_cast<char *>(&x), sizeof(x)); } ifile.close(); return 0; }

Example 2

This example is also available here: fileio3.cpp

#include <fstream> #include <iostream> using std::cout; using std::endl; using std::ifstream; using std::ofstream; using std::ios; // this struct defines what the record is in the file struct recType { int n; float x; }; int main(void) { int i; recType rec; rec.x = 0.0f; // open output file as binary ofstream ofile("testfileio3.bin", ios::binary); if (!ofile) { cout << "Error: couldn't open output file" << endl; return 1; } // whole record is written at once to disk for (i = 1; i < 10; i++) { rec.n = i; ofile.write(reinterpret_cast<char *>(&rec), sizeof(rec)); rec.x += 25; } ofile.close(); // open file for input as binary ifstream ifile("testfileio3.bin", ios::binary); if (!ifile) { cout << "Error: couldn't open input file" << endl; return 2; } // find out how many records are in the file ifile.seekg(0L, ios::end); long numrecs = ifile.tellg() / sizeof(rec); // don't forget to get back to start of file ifile.seekg(0L, ios::beg); // read data in record-size chunks for (i = 0; i < numrecs; i++) { ifile.read(reinterpret_cast<char *>(&rec), sizeof(rec)); cout << rec.n << ", " << rec.x << endl; } ifile.close(); return 0; }