Reading From and Writing to Files
This document provides an overview of how to perform basic file input and output operations in Windows applications. Understanding these fundamental operations is crucial for managing data persistence and interacting with the file system.
Core Concepts
File operations typically involve:
- Opening a file: Establishing a connection to a file, specifying its name and desired access mode.
- Reading from a file: Transferring data from the file into memory.
- Writing to a file: Transferring data from memory into the file.
- Seeking within a file: Moving the file pointer to a specific position for reading or writing at arbitrary locations.
- Closing a file: Releasing the connection to the file and ensuring all buffered data is flushed.
Using the File System API
The Windows API provides a rich set of functions for file manipulation. For low-level file operations, the CreateFile function is central.
Opening a File with CreateFile
The CreateFile function is used to open or create a file and returns a handle to the file. This handle is then used in subsequent I/O operations.
HANDLE CreateFile(
LPCSTR lpFileName, // File name
DWORD dwDesiredAccess, // Access mode (e.g., GENERIC_READ, GENERIC_WRITE)
DWORD dwShareMode, // Sharing mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // Security attributes
DWORD dwCreationDisposition, // How to create or open
DWORD dwFlagsAndAttributes, // File attributes and flags
HANDLE hTemplateFile // Handle to template file
);
Key parameters:
lpFileName: The name of the file.dwDesiredAccess: Specifies the requested access to the file. Common values includeGENERIC_READfor reading andGENERIC_WRITEfor writing.dwCreationDisposition: Determines whether to create a new file, open an existing one, or both. Examples includeCREATE_ALWAYS,OPEN_EXISTING, andOPEN_ALWAYS.
If the function succeeds, the return value is an open handle to the specified file. If the function fails, the return value is INVALID_HANDLE_VALUE.
Reading from a File with ReadFile
Once a file handle is obtained, ReadFile can be used to read data from the file.
BOOL ReadFile(
HANDLE hFile, // Handle to the file
LPVOID lpBuffer, // Buffer that receives the data
DWORD nNumberOfBytesToRead,// Number of bytes to read
LPDWORD lpNumberOfBytesRead, // Number of bytes actually read
LPOVERLAPPED lpOverlapped // Overlapped structure for asynchronous I/O
);
The data read is placed into the buffer pointed to by lpBuffer. lpNumberOfBytesRead receives the actual number of bytes read.
Writing to a File with WriteFile
Similarly, WriteFile is used to write data to an opened file.
BOOL WriteFile(
HANDLE hFile, // Handle to the file
LPCVOID lpBuffer, // Buffer containing the data
DWORD nNumberOfBytesToWrite, // Number of bytes to write
LPDWORD lpNumberOfBytesWritten, // Number of bytes written
LPOVERLAPPED lpOverlapped // Overlapped structure for asynchronous I/O
);
The data to be written is supplied in lpBuffer. lpNumberOfBytesWritten will contain the number of bytes successfully written.
Closing a File with CloseHandle
It is essential to close file handles when they are no longer needed to free system resources.
BOOL CloseHandle(
HANDLE hObject // Handle to the object
);
This function closes an open object handle, including file handles.
Example: Basic File Writing
The following C++ snippet demonstrates writing a string to a text file:
#include <windows.h>
#include <iostream>
int main() {
HANDLE hFile;
DWORD dwBytesWritten;
const char* message = "Hello, Windows File I/O!";
const char* filename = "example.txt";
hFile = CreateFile(
filename,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "Unable to open file: " << GetLastError() << std::endl;
return 1;
}
if (!WriteFile(
hFile,
message,
strlen(message),
&dwBytesWritten,
NULL)) {
std::cerr << "Unable to write to file: " << GetLastError() << std::endl;
CloseHandle(hFile);
return 1;
}
std::cout << dwBytesWritten << " bytes written to " << filename << std::endl;
CloseHandle(hFile);
return 0;
}
Example: Basic File Reading
Here's a C++ example for reading the content of a text file:
#include <windows.h>
#include <iostream>
#include <vector>
int main() {
HANDLE hFile;
DWORD dwBytesRead;
const char* filename = "example.txt";
char buffer[1024];
hFile = CreateFile(
filename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "Unable to open file: " << GetLastError() << std::endl;
return 1;
}
while (true) {
if (!ReadFile(
hFile,
buffer,
sizeof(buffer) - 1, // Leave space for null terminator
&dwBytesRead,
NULL)) {
std::cerr << "Unable to read from file: " << GetLastError() << std::endl;
CloseHandle(hFile);
return 1;
}
if (dwBytesRead == 0) {
break; // End of file
}
buffer[dwBytesRead] = '\0'; // Null-terminate the string
std::cout << buffer;
}
CloseHandle(hFile);
return 0;
}
High-Level I/O with .NET Framework
For managed code applications, the .NET Framework provides a more abstract and object-oriented approach to file I/O using classes in the System.IO namespace. These classes handle many of the underlying details, making file operations simpler and safer.
Writing to a File (.NET)
The File.WriteAllText method is a convenient way to write a string to a file.
using System.IO;
// ...
string textToWrite = "This is written using .NET!";
string filePath = "dotnet_example.txt";
try
{
File.WriteAllText(filePath, textToWrite);
Console.WriteLine($"Successfully wrote to {filePath}");
}
catch (Exception ex)
{
Console.WriteLine($"Error writing file: {ex.Message}");
}
Reading from a File (.NET)
Similarly, File.ReadAllText reads the entire content of a file into a string.
using System.IO;
// ...
string filePath = "dotnet_example.txt";
try
{
string fileContent = File.ReadAllText(filePath);
Console.WriteLine($"Content of {filePath}:\n{fileContent}");
}
catch (Exception ex)
{
Console.WriteLine($"Error reading file: {ex.Message}");
}