Lesson 1: File IO

In this lesson, you will learn about the file I/O classes provided by MFC. You will also learn about the basic file services offered by the CFile class and how to use its derived class CStdioFile, which provides buffered stream file access.

After this lesson, you will be able to:

  • Create new files and open existing files using member functions of the CFile class.
  • Read and write binary files and text files using the CStdioFile class.
  • Handle errors that might occur while attempting file I/O.
Estimated lesson time: 25 minutes

CFile Class

The MFC CFile class provides access to binary disk files by encapsulating an op-erating system file handle and providing member functions for opening, reading from, writing to, and closing a file. The CFile class provides direct, unbuffered access to files. Derived from CFile, the CStdioFile class implements buffered stream files in MFC.

CFile is the base class for the CMemFile and the CSharedFile classes. CMemFile provides support for in-memory files, which are stored in RAM rather than on a disk for increased performance. CSharedFile provides support for shared in-memory files.

Opening and Closing Files

There is more than one way to open a file using the CFile class. The class provides a constructor that allows you to specify the file to open. This constructor lets you declare a CFile object and associate that object with a disk file in a single statement. Specifying the file in the constructor is risky. If the file doesn't exist, errors will result.

It is generally better to use a two-stage approach. First, create a CFile object; second, associate this object with a disk file. This approach gives you more flexibility in handling errors, and also helps clarify the logical distinction between the file object in your code and the physical disk file.

To open a disk file using the two-stage approach, first declare a CFile object without any parameters. Next, call the CFile::Open() function for the object, specify the path to the disk file, and provide flags that specify the access and sharing modes. For example, note the following code, which uses the two-stage approach:

CString strPath = "MyFile.bin"; CFile MyFile; BOOL bResult = MyFile.Open(strPath, CFile::modeRead); if(!bResult)      AfxMessageBox(strPath + " could not be opened");

Specifying a file name without any path information will result in the Open command searching for the file in the current folder first, and in the folders within the system path next. You can also specify paths relative to the current folder and paths using a Universal Naming Convention (UNC) name. A UNC name provides a computer-independent means of locating the file, and has the format \\Server Name\Network Share\File Path.

When specifying a path name for the file to be opened, don't make any assumptions about the file's location. For example, specifying the path name as follows assumes that the MyFile.bin file will be found in the C:\\Program Files\\MyApp folder:

CString strPath = C:\\Program Files\\MyApp\\MyFile.bin;

This assumption might not be valid if your application has been installed on a drive other than drive C. Your application or setup program can create environment variables or registry entries (see Lesson 3 of this chapter) to record the location of the files that it uses.

The second parameter to the Open() function allows you to specify a bitmask that defines the file's access and sharing modes. The bitmask values are declared as enumerated constants within the scope of the CFile class, which means that the flags must be qualified with the class scope. The access mode determines whether the file will be open for reading only, writing only, or both reading and writing. The sharing mode determines the access to this file granted to other processes while you have the file open. You can create a new file by specifying the CFile::modeCreate value.

The bitmask values passed to the Open() function can be combined by using the bitwise OR operator (|). Generally, you should specify an access mode as well as a sharing mode. For example, the following code will open the MyFile.bin file for reading and writing:

MyFile.Open("MyFile.bin", CFile::modeReadWrite|CFile::shareDenyWrite);

If the file MyFile.bin is successfully opened by this function call, all other processes will be denied write permissions to the file. If MyFile.bin cannot be found, it will not be created.

Table 6.1 summarizes the access and sharing modes defined by the CFile class.

Table 6.1 Access and Sharing Modes

Flag ValueAction
CFile::modeCreate Creates a new file. If the specified file already exists, it is truncated to zero length.
CFile::modeNoTruncate Can be combined with CFile::modeCreate to ensure that, if the specified file already exists, it is opened without being truncated to zero length. Thus, the file is guaranteed to open, either as an existing file or as a newly created file. This value might be useful when opening a settings file that might or might not already exist.
CFile::modeRead Opens the file as read-only.
CFile::modeReadWrite Opens the file as read/write.
CFile::modeWrite Opens the file as write-only.
CFile::shareDenyNone Does not deny other processes read or write permissions to the file.
CFile::shareDenyRead Denies other processes read permissions to the file.
CFile::shareDenyWrite Denies other processes write permissions to the file.
CFile::shareExclusive Denies other processes both read and write permissions to the file.

File Errors

It is not difficult to see that a number of factors might cause the CFile::Open() function to fail. Attempting to open a file that does not exist (without specifying the CFile::modeCreate flag) will cause the Open() function to fail. Attempting to open a file already opened exclusively by another process will also result in failure. Numerous environmental factors can cause errors when working with files.

In the example code showing how to use CFile::Open(), the function returned a Boolean value indicating success or failure. If there was a failure, you will usually want to retrieve information about why the operation failed. You can then relay such information to the user, and suggest steps they can take to remedy the problem.

MFC provides the CFileException class (derived from the CException base class) to represent a file error condition. The MFC exception classes contain member data and functions that allow you to retrieve information about the error that generated the exception.

The version of the CFile constructor that attempts to open a file will throw the exception CFileException upon failure. If you use this constructor, you should enclose it in a try/catch exception handling block, as follows:

try {      CFile MyFile("MyFile.old", CFile::modeRead); } catch(CFileException * fx) {      TCHAR buf[255];      fx->GetErrorMessage(buf, 255);      CString strPrompt(buf);      AfxMessageBox(strPrompt); }

To learn more about exception handling and MFC exceptions, refer to Lesson 1 of Chapter 13.

The CFile::Open() function does not throw an exception. Instead, it takes an optional third parameter that is a pointer to a CFileException object. If the file open operation fails, the CFileException object is filled with information about the error's nature. This information can be used by subsequent code to provide information to the user, as shown in the following code:

CFile MyFile; CFileException fx; if(!MyFile.Open("MyFile.old", CFile::modeRead, &fx)) {      TCHAR buf[255];      fx.GetErrorMessage(buf, 255);      CString strPrompt(buf);      AfxMessageBox(strPrompt); }

Closing Files

CFile provides the Close() member function to close an open disk file. The following code provides an example of how to use the Close() function:

BOOL MyFileFunction() {      CFile MyFile;      if(!MyFile.Open("MyFile.old", CFile::modeRead))      {           AfxMessageBox("Cannot open MyFile.old");           return FALSE;      }      // Do something with the file . . .      MyFile.Close();      return TRUE; }

In this example, the call to MyFile.Close() is not strictly necessary. The CFile destructor, which is called as the object loses scope, will call the Close() function for you if it detects that you have not done so already. However, it is good programming style to always match a call to CFile::Open() with a corresponding call to CFile::Close().

You can use the Close() function to disassociate your CFile object from a file before reusing the object to access a different file. For example, the following code will create three empty files named file1.txt, file2.txt, and file3.txt in the current application folder. All three files are created using the same CFile object:

CString  strFiles[3] = { "file1.txt", "file2.txt", "file3.txt" }; CFile file; for(int i = 0; i < 3; i++) {      file.Open(strFiles[i], CFile::modeCreate);      file.Close(); }

Reading and Writing Files

CFile supplies the Read() and Write() functions, which call the Microsoft Windows API functions ReadFile() and WriteFile(), to provide direct, unbuffered read and write operations to disk files. Because direct file access is quite tricky to use, the C run-time library provides stream I/O functions. (For example, buffers and pointer offsets must be specified in units that are integer multiples of the disk volume's sector size.)

Stream I/O functions allow you to process data from disk files and other sources in formats ranging from single characters to large data structures. These functions also provide I/O buffering, which can improve performance. Ex-amples of stream I/O functions include fopen(), fseek(), fread() and fwrite(). As a C++ programmer, you might be familiar with stream I/O through the iostream classes.

MFC provides access to stream file I/O through the CStdioFile class. The CStdioFile versions of Read() and Write() use the run-time stream I/O functions. Unless you have a specific need for low-level direct access to a disk file, you should use the CStdioFile class, which is flexible and easy to use.

Disk files associated with a CStdioFile object can be opened in text mode or in binary mode. Text mode provides special processing for carriage return/linefeed (CR/LF) pairs. When you write a newline character (0x0A) to a text-mode CStdioFile object, the byte pair for a CR/LF (0x0D, 0x0A) is sent to the file. When you read a text-mode file, the byte pair 0x0D, 0x0A is translated to a single 0x0A byte. To open a CStdioFile object as a textmode file, supply the CFile::typeText flag to the Open() function as follows:

CStdioFile inFile("MyFile.txt", CFile::modeRead | CFile::typeText);

Use the CFile::typeBinary flag to open a CStdioFile in binary mode. Newline characters are not converted in binary mode.

To read data from a CStdioFile file object, you can use either the Read() function or the ReadString() function.

Read() takes a pointer to a buffer that will contain the data read from the file, and an unsigned integer (UINT) value that specifies the number of bytes to read. The Read() function returns the number of bytes that were read. If the required number of bytes could not be read because the end of the file was reached, the actual number of bytes read is returned.

If a read error occurs, a CFileException exception is thrown. When writing to a text mode file, use the ReadString() function, which is similar to Read() except that:

  • Reading stops when a newline character is encountered.
  • A terminating null is appended to the text in the buffer.
  • A pointer to the buffer is returned. This pointer will contain a NULL if the end of the file was reached without reading any data.

ReadString() provides a simple way to read a single line from a text file into a string by providing a version that returns a BOOL value, which indicates success or failure.

Write() is similar to Read(), taking a buffer containing the bytes to be written and a value that specifies the number of bytes to read. The number of bytes written is not returned with the use of Write(). If a write error occurs, including not writing all bytes specified, CFileException is thrown. When writing to a text mode file, use CStdioFile::WriteString(), which writes a newline character.

The following code illustrates how to use the CStdioFile class to read and write disk files. The code opens the MyFile.bin file in binary mode and reads its data in blocks of 10 bytes. Each block is written to a new line in the newly created Output.txt file, as demonstrated in the following code example:

try {      CStdioFile inFile("MyFile.bin", CFile::modeRead |            CFile::typeBinary);      CStdioFile outFile("outfile.txt", CFile::modeCreate |            CFile::modeWrite | CFile::typeText);      const UINT linelength = 10;      TCHAR strBuf[16];      while(inFile.ReadString(strBuf, linelength))      {              _tcscat(strBuf, _T("\n"));           outFile.WriteString(strBuf);      }                } catch(CFileException * fx) {      TCHAR buf[255];      fx->GetErrorMessage(buf, 255);      AfxMessageBox(buf); }

Random File Access

Text files are generally read and written sequentially, a line at a time. When reading binary files, you often need to perform random access. Random access allows you to jump directly to a specific location, then immediately access data stored at that location.

An open file maintains a file pointer that specifies the next byte to be read, or the location to receive the next byte written. When a file is opened, the file pointer is placed at the beginning of the file. You can position the file pointer by using the CFile::Seek() function. CFile::Seek() moves the file pointer to a specified offset from the beginning or end of the file, or from the current file pointer position. A read or write operation will advance the file pointer by the number of bytes read or written.

Lesson Summary

The MFC class CFile provides direct, unbuffered I/O to disk files. CFile is also the base class for the CStdioFile class, which allows access to files through the buffered stream I/O provided by the C run-time library. The CStdioFile class allows you to open files in text mode (in which newline characters are converted to CR/LF pairs) or in binary mode (in which newline characters are not converted). If you need specific direct access to disk files, you can use the CFile class. For general-purpose disk file I/O, you should use the CStdioFile class.

Objects derived from CFile are either associated with a disk file at creation time (by using the appropriate constructor), or created using a two-stage process, in which a CFile-derived object is created and its Open() function is called. The Open() function accepts a path to the file to be opened, and also accepts flag values that specify the access and sharing modes to be used. The access mode determines whether the file will be open for read-only, write-only, or read/write permissions, and whether a new file should be created. The sharing mode determines the level of access to this file that is granted to other processes while the file is open.

Many of the CFile member functions throw MFC exceptions of the type CFileException. CFileException objects can be queried to provide information about the error that caused the exception condition. The Open() function takes an exception object as a parameter.

To read and write an open disk file, you can use the Read() and Write() functions provided by CFile, or you can use CStdioFile::ReadString() and CStdioFile::WriteString(). Both Read() and ReadString() place characters read from the file into a buffer that you supply to the function. ReadString() stops reading when a newline character is encountered, and appends a terminating null character to the text in the buffer.

Use the Write() function to write text in a buffer into a file. When writing to a text mode file, use CStdioFile::WriteString(), which writes a newline character as a CR/LF pair.

A file opened for random access maintains a file pointer that specifies the next byte to be read or the location to receive the next byte written. Use the CFile::Seek() function to move the file pointer to a specified offset within the file before performing a read or write operation.



Microsoft Press - Desktop Applications with Microsoft Visual C++ 6. 0. MCSD Training Kit
Desktop Applications with Microsoft Visual C++ 6.0 MCSD Training Kit
ISBN: 0735607958
EAN: 2147483647
Year: 1999
Pages: 95

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net