Reading a string from a file in C++

12,682

Solution 1

What am I doing wrong?

You are reading from a file and trying to put the data in the string structure itself, overwriting it, which is plain wrong.

As it can be verified at http://www.cplusplus.com/reference/iostream/istream/read/ , the types you used were wrong, and you know it because you had to force the std::string into a char * using a reinterpret_cast.

C++ Hint: using a reinterpret_cast in C++ is (almost) always a sign you did something wrong.

Why is it so complicated to read a file?

A long time ago, reading a file was easy. In some Basic-like language, you used the function LOAD, and voilà!, you had your file. So why can't we do it now?

Because you don't know what's in a file.

  • It could be a string.
  • It could be a serialized array of structs with raw data dumped from memory.
  • It could even be a live stream, that is, a file which is appended continuously (a log file, the stdin, whatever).
  • You could want to read the data word by word
  • ... or line by line...
  • Or the file is so large it doesn't fit in a string, so you want to read it by parts.
  • etc..

The more generic solution is to read the file (thus, in C++, a fstream), byte per byte using the function get (see http://www.cplusplus.com/reference/iostream/istream/get/), and do yourself the operation to transform it into the type you expect, and stopping at EOF.

The std::isteam interface have all the functions you need to read the file in different ways (see http://www.cplusplus.com/reference/iostream/istream/), and even then, there is an additional non-member function for the std::string to read a file until a delimiter is found (usually "\n", but it could be anything, see http://www.cplusplus.com/reference/string/getline/)

But I want a "load" function for a std::string!!!

Ok, I get it.

We assume that what you put in the file is the content of a std::string, but keeping it compatible with a C-style string, that is, the \0 character marks the end of the string (if not, we would need to load the file until reaching the EOF).

And we assume you want the whole file content fully loaded once the function loadFile returns.

So, here's the loadFile function:

#include <iostream>
#include <fstream>
#include <string>

bool loadFile(const std::string & p_name, std::string & p_content)
{
    // We create the file object, saying I want to read it
    std::fstream file(p_name.c_str(), std::fstream::in) ;

    // We verify if the file was successfully opened
    if(file.is_open())
    {
        // We use the standard getline function to read the file into
        // a std::string, stoping only at "\0"
        std::getline(file, p_content, '\0') ;

        // We return the success of the operation
        return ! file.bad() ;
    }

    // The file was not successfully opened, so returning false
    return false ;
}

If you are using a C++11 enabled compiler, you can add this overloaded function, which will cost you nothing (while in C++03, baring optimizations, it could have cost you a temporary object):

std::string loadFile(const std::string & p_name)
{
    std::string content ;
    loadFile(p_name, content) ;
    return content ;
}

Now, for completeness' sake, I wrote the corresponding saveFile function:

bool saveFile(const std::string & p_name, const std::string & p_content)
{
    std::fstream file(p_name.c_str(), std::fstream::out) ;

    if(file.is_open())
    {
        file.write(p_content.c_str(), p_content.length()) ;

        return ! file.bad() ;
    }

    return false ;
}

And here, the "main" I used to test those functions:

int main()
{
    const std::string name(".//myFile.txt") ;
    const std::string content("AAA BBB CCC\nDDD EEE FFF\n\n") ;

    {
        const bool success = saveFile(name, content) ;
        std::cout << "saveFile(\"" << name << "\", \"" << content << "\")\n\n"
                  << "result is: " << success << "\n" ;
    }

    {
        std::string myContent ;
        const bool success = loadFile(name, myContent) ;

        std::cout << "loadFile(\"" << name << "\", \"" << content << "\")\n\n"
                  << "result is: " << success << "\n"
                  << "content is: [" << myContent << "]\n"
                  << "content ok is: " << (myContent == content)<< "\n" ;
    }
}

More?

If you want to do more than that, then you will need to explore the C++ IOStreams library API, at http://www.cplusplus.com/reference/iostream/

Solution 2

You can't use std::istream::read() to read into a std::string object. What you could do is to determine the size of the file, create a string of suitable size, and read the data into the string's character array:

std::string str;
std::ifstream file("whatever");
std::string::size_type size = determine_size_of(file);
str.resize(size);
file.read(&str[0], size);

The tricky bit is determining the size the string should have. Given that the character sequence may get translated while reading, e.g., because line end sequences are transformed, this pretty much amounts to reading the string in the general case. Thus, I would recommend against doing it this way. Instead, I would read the string using something like this:

std::string   str;
std::ifstream file("whatever");
if (std::getline(file, str, '\0')) {
     ...
}

This works OK for text strings and is about as fast as it gets on most systems. If the file can contain null characters, e.g., because it contains binary data, this doesn't quite work. If this is the case, I'd use an intermediate std::ostringstream:

std::ostringstream out;
std::ifstream      file("whatever");
out << file.rdbuf();
std::string str = out.str();
Share:
12,682
porkpork
Author by

porkpork

Updated on June 04, 2022

Comments

  • porkpork
    porkpork almost 2 years

    I'm trying to store strings directly into a file to be read later in C++ (basically for the full scope I'm trying to store an object array with string variables in a file, and those string variables will be read through something like object[0].string). However, everytime I try to read the string variables the system gives me a jumbled up error. The following codes are a basic part of what I'm trying.

    #include <iostream>
    #include <fstream>
    using namespace std;
    
    /*
    //this is run first to create the file and store the string
    int main(){
        string reed;
        reed = "sees";
        ofstream ofs("filrsee.txt", ios::out|ios::binary);
        ofs.write(reinterpret_cast<char*>(&reed), sizeof(reed));
        ofs.close();
    
    }*/
    
    //this is run after that to open the file and read the string
    int main(){
        string ghhh;
        ifstream ifs("filrsee.txt", ios::in|ios::binary);
        ifs.read(reinterpret_cast<char*>(&ghhh), sizeof(ghhh));
        cout<<ghhh;
        ifs.close();
        return 0;
    }
    

    The second part is where things go haywire when I try to read it. Sorry if it's been asked before, I've taken a look around for similar questions but most of them are a bit different from what I'm trying to do or I don't really understand what they're trying to do (still quite new to this).

  • porkpork
    porkpork over 11 years
    Oh man, this doesn't look as straightforward as I had hoped. Thanks for the answers, though. I hope I can take it all in and make it work.
  • Andrew
    Andrew over 11 years
    I would highly recommend using the second method. The first method is more complicated and the second method should work just fine.