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.