0

I am trying to write a std::string in a file and then reading it back. Why do i need to resize the string while reading back the text (see the commented line below while reading back the string)? Doesn't the string handles its size automatically?

#include <iostream>
#include <fstream>

int main()
{
    {
        std::ofstream ofile("c:\\out.txt", std::ios_base::binary);

        if (!ofile.is_open())
        {
            std::cout << "Failed to open the file";
            return 1;
        }

        std::string s = "Hello World";

        try
        {
            ofile.write(s.data(), s.size());
            if (ofile.fail())
            {
                std::cout << "Failed to write the file";
                return 1;
            }
        }
        catch (std::ios_base::failure& e)
        {
            std::cout << e.what();
        }



        ofile.close();
    }

    {
        std::ifstream ifile("c:\\out.txt", std::ios_base::binary);
        if (!ifile.is_open())
        {
            std::cout << "Unable to open input file";
            return 1;
        }
        ifile.seekg(0, std::ios::end);
        auto length = ifile.tellg();
        ifile.seekg(0, std::ios::beg);

        std::string outstr;
        //outstr.resize(length);
        try
        {
            ifile.read(reinterpret_cast<char*>(&outstr.front()), length);
        }
        catch (std::ios_base::failure& e)
        {
            std::cout << e.what();
        }

        std::cout << outstr;
    }
    return 0;
}
Helena
  • 444
  • 2
  • 15
  • Won't `ofile.write(s.data(), s.size())` fail to write the null terminator? – Nathan Pierson Mar 29 '22 at 05:48
  • 1
    Anyway, `string` handles its size automatically _if you use its member functions_. `outstr += c;` will increase the size. But you're grabbing the address of its first character, `reinterpret_cast`ing it, and effectively just handing a `char` pointer to `ifile::read`. You're pretty explicitly discarding the `std::string`-iness there. – Nathan Pierson Mar 29 '22 at 05:50
  • Yeah that doesn't look safe at all, specially because you're handing the file in binary mode. There is no guarantee the string will be properly null-terminated as it should, the binary mode doesn't do that! Besides, instancing a `std::string` and literally not using any of it's member functions defeats it's whole purpose. If you want to use the std classes to read a file, take a look at this answer: https://stackoverflow.com/a/2602258/156811 – Havenard Mar 29 '22 at 06:04
  • Use `reinterpret_cast` only when you know what you're doing. Most of time I don't want to use it. – Louis Go Mar 29 '22 at 06:16
  • @LouisGo : that reinterpret_cast is good thing to do. the ifstream and ofstream is templated on char – Helena Mar 29 '22 at 15:59

2 Answers2

1

The parameters of istream::read specify a buffer, not a string. Therefore, the function cannot know that there is an object that could theoretically be instructed to resize storage. For this reason, the caller has to do the resizing.

j6t
  • 9,150
  • 1
  • 15
  • 35
0

After C++11, std::string is guaranteed to have contiguous memory buffer.

Quoted from cppreference

The elements of a basic_string are stored contiguously

So you may use this characteristic of std::string to use it as a buffer as long as you have std::string reserve the size you need. Note that std::string won't allocate the buffer for you while using its underlying buffer.

However I'd suggest to use std::string as is instead of using it as a raw buffer. So you don't have to resize it manually. Eg: using std::stringstream to take the buffer from std::ifstream

// ... open ifstream ifile...
std::stringstream ss;
ss << ifile.rdbuf();

// Use ss.str() to get `std::string`

If you really wants to use std::string as raw buffer, you need to reserve the size beforehand.

// ... 
std::string outstr(length, ' '); // or call `outstr.resize()` after
ifile.read(&outstr[0]), length);
Louis Go
  • 2,213
  • 2
  • 16
  • 29
  • Your answer about using `ss << ifile.rdbuf();` got me wondering that whether this will potentially run into size issues with significantly large files? After all there will be some size limit to buffer where `rdbuf` will attempt to read to. isn't it? – Helena Mar 31 '22 at 01:25
  • I presume you don't consider this issue since your original attempt is also getting the full buffer. If you need to consider size, please edit your question. – Louis Go Mar 31 '22 at 01:33