HOWTO · C++
How to Read File Into String in C++
This article demonstrates how to read file into string in C++.
On this page
To read a file into a std::string, use std::ifstream with std::getline when you want to process lines, or construct the string from std::istreambuf_iterator<char> when you want the complete stream. The examples below create their own temporary input file, so each program can be compiled and run on its own.
Use std::ifstream and std::getline to Read File Into String in C++
std::getline removes each line delimiter. Appending "\n" after every successful call restores separators, but also adds a newline after the last line even when the source file did not end with one. If that distinction matters, track whether a delimiter was present or use a whole-stream method below.
This complete example writes two lines to a temporary file, opens it with std::ifstream, and appends each line to a string. The prose before the example describes its ordinary successful use.
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
int main() {
const auto path = std::filesystem::temp_directory_path() / "cpp-string-lines.txt";
{
std::ofstream sample(path);
sample << "alpha\nbeta";
if (!sample) {
std::cout << "Could not create sample file\n";
return 0;
}
}
std::ifstream inputFile(path);
if (!inputFile) {
std::cout << "Could not open sample file\n";
std::filesystem::remove(path);
return 0;
}
std::string fileContent;
std::string line;
while (std::getline(inputFile, line)) {
fileContent += line + "\n";
}
if (inputFile.bad()) {
std::cout << "Read failed\n";
inputFile.close();
std::filesystem::remove(path);
return 0;
}
std::cout << fileContent;
inputFile.close();
std::filesystem::remove(path);
}
The loop stops when std::getline can no longer extract a line. Checking bad() distinguishes a low-level I/O failure from the normal end of the stream. The file is opened in text mode by default; on systems that translate line endings, the bytes in the resulting string can differ from those stored in the file. Use binary mode when byte-for-byte preservation is required.
Use istreambuf_iterator to Read File Into String in C++
For the complete stream, construct a string from a pair of std::istreambuf_iterator<char> values. The first iterator reads from the stream buffer and the default-constructed iterator marks the end. This reads data without splitting it into lines, so it preserves newline characters delivered by the stream.
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
int main() {
const auto path = std::filesystem::temp_directory_path() / "cpp-string-iterator.txt";
{
std::ofstream sample(path, std::ios::binary);
sample << "alpha\nbeta";
if (!sample) {
std::cout << "Could not create sample file\n";
return 0;
}
}
std::ifstream inputFile(path, std::ios::binary);
if (!inputFile) {
std::cout << "Could not open sample file\n";
std::filesystem::remove(path);
return 0;
}
const std::string fileContent{
std::istreambuf_iterator<char>(inputFile),
std::istreambuf_iterator<char>()};
if (inputFile.bad()) {
std::cout << "Read failed\n";
} else {
std::cout << fileContent << '\n';
}
inputFile.close();
std::filesystem::remove(path);
}
alpha
beta
Use this form when the entire file fits in memory and line-by-line processing is unnecessary. Opening in binary mode avoids text-mode newline conversion. A std::string can contain zero bytes, but functions that treat c_str() as a null-terminated text string may stop at the first one.
Use rdbuf to Read File Into String in C++
Another standard-library option is to insert the input stream’s rdbuf() into a std::ostringstream, then call str() to obtain its contents as a std::string. This is convenient when the string stream is already part of the surrounding code. Check the input stream after the transfer if read failures need to be reported.
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
int main() {
const auto path = std::filesystem::temp_directory_path() / "cpp-string-rdbuf.txt";
{
std::ofstream sample(path, std::ios::binary);
sample << "alpha\nbeta";
if (!sample) {
std::cout << "Could not create sample file\n";
return 0;
}
}
std::ifstream inputFile(path, std::ios::binary);
if (!inputFile) {
std::cout << "Could not open sample file\n";
std::filesystem::remove(path);
return 0;
}
std::ostringstream stream;
stream << inputFile.rdbuf();
if (inputFile.bad()) {
std::cout << "Read failed\n";
} else {
const std::string fileContent = stream.str();
std::cout << fileContent << '\n';
}
inputFile.close();
std::filesystem::remove(path);
}
The insertion copies characters from the file buffer into the string stream; str() returns the accumulated string. For a direct whole-file construction, the iterator approach uses fewer moving parts. Neither form removes the need to check whether the file opened and whether a read error occurred.
Use the fread Function to Read File Into String
fread is a C standard library function and can be used from C++. It is useful when code already uses FILE* streams or needs a byte-oriented C API. It is not inherently faster than the C++ stream approaches; performance depends on the workload and implementation.
This example uses fseek and ftell to size a seekable sample file, allocates a string of that size, and reads bytes into it. For non-seekable streams or files too large for memory, read fixed-size chunks instead. The file size can change between measuring and reading, so the returned byte count and ferror still need checking.
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
int main() {
const auto path = std::filesystem::temp_directory_path() / "cpp-string-fread.txt";
{
std::ofstream sample(path, std::ios::binary);
sample << "alpha\nbeta";
if (!sample) {
std::cout << "Could not create sample file\n";
return 0;
}
}
std::FILE* file = std::fopen(path.string().c_str(), "rb");
if (file == nullptr) {
std::cout << "Could not open sample file\n";
std::filesystem::remove(path);
return 0;
}
if (std::fseek(file, 0, SEEK_END) != 0) {
std::cout << "Could not seek sample file\n";
std::fclose(file);
std::filesystem::remove(path);
return 0;
}
const long length = std::ftell(file);
if (length < 0 || std::fseek(file, 0, SEEK_SET) != 0) {
std::cout << "Could not determine sample size\n";
std::fclose(file);
std::filesystem::remove(path);
return 0;
}
std::string content(static_cast<std::size_t>(length), '\0');
const std::size_t bytesRead = std::fread(content.data(), 1, content.size(), file);
const bool failed = std::ferror(file) != 0 || bytesRead != content.size();
std::fclose(file);
std::filesystem::remove(path);
if (failed) {
std::cout << "Read failed or file changed while reading\n";
return 0;
}
std::cout << content << '\n';
}
The third argument is the number of elements, and the second is the size of each element; using 1 as the element size makes the return value a byte count. fread may return fewer bytes than requested, so do not assume one call fills the buffer. This sample is for a seekable file and uses the long range supported by ftell; applications needing larger files should use platform-appropriate large-file APIs or read chunks. The string stores bytes and does not append a null terminator for C string functions.
Use read to Read File Into String
The POSIX read function works with a file descriptor and a caller-provided buffer. It is not part of standard C++; include the appropriate POSIX headers and use this method only on platforms that provide the API. A successful call can return fewer bytes than requested, so repeat calls until end-of-file, and handle interruptions and errors.
The following POSIX-specific program reads in chunks rather than relying on a prior file-size measurement. Its system calls cannot be represented on every C++ platform, so treat it as a POSIX example rather than portable C++.
#include <cerrno>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include <fcntl.h>
#include <unistd.h>
int main() {
const auto path = std::filesystem::temp_directory_path() / "cpp-string-read.txt";
{
std::ofstream sample(path, std::ios::binary);
sample << "alpha\nbeta";
if (!sample) {
std::cout << "Could not create sample file\n";
return 0;
}
}
const int fd = ::open(path.c_str(), O_RDONLY);
if (fd < 0) {
std::cout << "Could not open sample file\n";
std::filesystem::remove(path);
return 0;
}
std::string content;
char buffer[4096];
bool failed = false;
for (;;) {
const ssize_t count = ::read(fd, buffer, sizeof buffer);
if (count > 0) {
content.append(buffer, static_cast<std::size_t>(count));
} else if (count == 0) {
break;
} else if (errno != EINTR) {
failed = true;
break;
}
}
if (::close(fd) != 0) {
failed = true;
}
std::filesystem::remove(path);
if (failed) {
std::cout << "Read or close failed\n";
return 0;
}
std::cout << content << '\n';
}
For a regular file, read returns zero at end-of-file. With other descriptor types, the behavior depends on the device or stream. Each positive return count is the number of bytes to append; a negative return indicates an error, and EINTR means the call can be retried. Always close the descriptor, and account for partial reads rather than assuming a single call fills a requested buffer. The example uses std::string::append to preserve the exact byte count, including any zero bytes.
Conclusion
For portable C++, choose std::istreambuf_iterator or rdbuf() when the whole stream should become a string, and use std::getline when line-oriented processing is useful. Remember that line extraction removes delimiters and appending them can change a final unterminated line. Use fread when a C FILE* interface fits the surrounding code, and use POSIX read only when its platform-specific descriptor API is appropriate. For large files, consider processing chunks instead of keeping the entire file in memory, and check both open and read results.