File input and output
To interact with files, you must use file streams. File streams form the foundation of file operations in C++. They allow your C++ program to link to external files for reading and writing. The two primary file stream classes are ifstream for input, and ofstream for output. To work with both input and output simultaneously, you can use the fstream class.
First, you need to include the header file:
#include <fstream>To write data to a file, you have to create an object of type std::ofstream. For reading from a file, you'll need an object of type std::ifstream. After this, you open the file, perform certain manipulations - such as reading data from a file or writing it there - if you need to, and then close the file:
#include <iostream>
#include <fstream>
int main() {
// Creating an ofstream object for writing to a file
std::ofstream outputFile;
std::string filename = "SomeText.txt";
// Opening the file
outputFile.open(filename);
// Writing data to a file via stream
outputFile << "Hi, I'm just a text you're trying to write to a file.";
// Closing the file
outputFile.close();
return 0;
}After running this program, you should have a file called SomeText.txt.
⚠️ Be aware that the file will be located in the folder where the executable file of our program is located.
If you want to create the file in a different folder, you can specify an absolute path:
- /Users/YOUR_USERNAME/Documents/SomeText.txt - Linux/Mac
- C:\Users\YOUR_USERNAME\Documents\SomeText.txt - Windows
or a relative path:
- ./text/SomeText.txt - Linux/Mac/Windows
To read data from a file, the code will be similar:
#include <iostream>
#include <fstream>
int main() {
// Creating an ifstream object for reading from a file
std::ifstream inputFile;
//Opening the file
inputFile.open("SomeText.txt");
// Reading data from the file
std::string line;
getline(inputFile, line);
// Closing the file
inputFile.close();
// Displaying the read data
std::cout << "Data from file: " << line << std::endl;
return 0;
}Upon executing our program, the following appears in the console:
Data from file: Hi, I'm just the text you're trying to write to a file.File streams can open in different modes, depending on the operation you want to perform. The description of the modes is stored in a special built-in class std::ios. You simply select the desired mode according to the task:
std::ios::in: This mode is used to open a file for reading. If the file does not exist, the open operation fails.std::ios::out: This mode is used to open a file for writing. If the file already exists, its contents are erased. If the file does not exist, a new file is created.std::ios::app: This mode opens a file for appending, meaning data will be written to the end of the file, regardless of the current position in the file. If the file does not exist, a new file is created.std::ios::binary: This mode opens a file in binary mode, as opposed to text mode. This is important when reading or writing binary data, such as an image file. It can be combined withstd::ios::in,std::ios::out, andstd::ios::app.std::ios::ate(at end): This mode, when opening a file, moves the position to the end of the file. It can be combined with other modes likestd::ios::inorstd::ios::out. If the file does not exist, the open operation fails.std::ios::trunc(truncate): This mode, when combined withstd::ios::out, will clear the contents of the file if it already exists. If the file does not exist, a new file is created. Be careful with this mode as it deletes all the data in the file if it exists.
These modes can be combined using the bitwise OR operator (|). For instance, to open a file in binary mode for reading and writing, use std::ios::in | std::ios::out | std::ios::binary.
Here are some basic examples of working with modes. Please note that we've shortened the notation and do not use the open function, but immediately initialize the object with the file name:
std::ios::ate Go to the end of the file before reading/writing:
#include <fstream>
int main() {
std::ofstream file("example.txt", std::ios::ate | std::ios::out);
file << "This will be at the end, regardless of file's current content.\n";
file.close();
return 0;
}std::ios::trunc Delete contents of the file if it exists:
#include <fstream>
int main() {
std::ofstream file("example.txt", std::ios::trunc | std::ios::out);
file << "All previous content, if any, is deleted.\n";
file.close();
return 0;
}File manipulation
File manipulation involves creating, deleting, moving, renaming, copying. You can use the std::rename() function to move a file:
#include <iostream>
int main() {
const char* oldFileName = "./file.txt";
const char* newFileName = "../file.txt";
if (std::rename(oldFileName, newFileName) == 0) {
std::cout << "File renamed successfully." << std::endl;
} else {
std::cerr << "Error renaming file!" << std::endl;
}
return 0;
}⚠️ Please note that we simply changed the directory here (from ./ to ../, i.e. to the folder above). If this action is unclear to you, you can read the documentation on the CD command (https://en.wikipedia.org/wiki/Cd_(command))
Renaming a file is the equivalent of moving a file. You only change the name instead of changing the directory:
#include <iostream>
int main() {
const char* oldFileName = "old.txt";
const char* newFileName = "new.txt";
if (std::rename(oldFileName, newFileName) == 0) {
std::cout << "File renamed successfully." << std::endl;
} else {
std::cerr << "Error renaming file!" << std::endl;
}
return 0;
}And you can use std::remove() to delete files:
#include <iostream>
int main() {
const char* newFileName = "new.txt";
if (std::remove(newFileName) == 0) {
std::cout << "File deleted successfully." << std::endl;
} else {
std::cerr << "Error deleting file!" << std::endl;
}
return 0;
}You can read the source file and write to the destination file:
#include <fstream>
#include <iostream>
int main() {
std::ifstream src("source.txt", std::ios::binary);
std::ofstream dst("dest.txt", std::ios::binary);
dst << src.rdbuf();
return 0;
}std::ios::binary specifies that files should be opened in binary mode. This means that files will be read and written as they are, without any transformation or interpretation of the data. In this case, src.rdbuf() returns a pointer to the I/O buffer of the file src, which is then written to the file dst.
We can also write data not just at the end of the file, but at any place. File seeking allows you to move the file pointer to a specific position in the file. This is handy when you want to read or write data in a particular location:
std::ifstream inputFile("data.txt");
inputFile.seekg(10); // Move to the 11th character in the file
char ch;
inputFile.get(ch); // Read the character at the current positionWe can interact with files. However, the standard C++ library does not support working with folders. Yet, from C++17 onwards, the standard library includes the <filesystem> library which provides functionality for manipulating files and directories.
Directories manipulation
Using the <filesystem> library, you can rename, move not only files but also work with directories. Here are some examples of its use (these are the basics, we'll detail further in other topics):
Creating a Directory:
#include <filesystem>
#include <iostream>
int main() {
std::filesystem::path dir{"old_directory"};
if (std::filesystem::create_directory(dir)) {
std::cout << "Directory created successfully\\n";
} else {
std::cout << "Failed to create directory\\n";
}
return 0;
}Renaming (moving) a directory:
#include <filesystem>
#include <iostream>
int main() {
std::filesystem::path old_dir{"old_directory"};
std::filesystem::path new_dir{"new_directory"};
try {
std::filesystem::rename(old_dir, new_dir);
std::cout << "Directory renamed successfully\\n";
} catch (std::filesystem::filesystem_error& e) {
std::cout << "Failed to rename directory: " << e.what() << '\\n';
}
return 0;
}It's important to bear in mind that file and directory operations can cause errors, such as due to lack of required permissions. In real code, potential exceptions that may occur while working with files or directories should be handled.
Error handling
When working with files, it's crucial to check if the file was opened successfully. This can be done by examining the file stream's state:
std::ifstream inputFile("nonexistent.txt");
if (!inputFile.is_open()) {
std::cerr << "Failed to open file!" << std::endl;
}Reading from a file may encounter errors, such as reaching the end of the file prematurely. You should check for such errors using the eof() function:
std::ifstream inputFile("data.txt");
std::string line;
while (getline(inputFile, line)) {
if (!inputFile.eof()) {
// Process the line
}
}Conclusion
In this guide, we covered the crucial aspects of working with files in C++. You learned how to perform file input and output using file streams, handle errors during file operations, and manipulate files by renaming, deleting, and seeking within them. These skills are fundamental for any C++ programmer when working with file-based data. Remember to handle errors gracefully and close files after use to ensure your programs are robust and efficient.