1

Why this function read files wrong? In buff after read last symbols contain garbage values. Does it related with memory alignment? I know about more safe std::string and rdbuff(), but I want to understand how work C-style strings.

char * read_from_file(const char * path)
{
    std::ifstream fin(path);
    if (fin)
    {        
        fin.seekg(0, fin.end);
        size_t length = fin.tellg();
        fin.seekg(0, fin.beg);       
        
        char * buff = new char[length];
        fin.read(buff, length);
        
        std::cout << buff << "\n";

        fin.close();
        return buff;
    }
    return nullptr;
}

For example:

File:

#version 330 core

out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

buff:

#version 330 core

out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

isample_blit_scaled  <-- garbage
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 4
    `istream::read()` won't null terminate anything, you know. – Fred Larson Feb 02 '22 at 16:32
  • 3
    C strings require a null terminator. `istream::read()` doesn't add a null terminator. You also didn't include room for the null terminator when you did `new char[length]`. – Barmar Feb 02 '22 at 16:32
  • @drescherjm Those are examples of the file he's reading and the output he's getting. – Barmar Feb 02 '22 at 16:34
  • Oh, thank you. So https://www.cplusplus.com/reference/istream/istream/read/ has example with small mistake? – Владимир Лео Feb 02 '22 at 16:36
  • The root cause is: Usage of C-Strings instead of C++ `std::string`. There is no justification to continue to use C-Style strings – A M Feb 02 '22 at 16:37
  • 1
    @ВладимирЛео: I don't see a mistake in that example. It's not doing anything with `buffer` that requires null termination. – Fred Larson Feb 02 '22 at 16:37
  • You could do `std::cout.write(buff, length)` to stay on the same raw data level... – Aconcagua Feb 02 '22 at 16:42
  • And your function should return the pointer together with the length by some means (struct, output parameter, ...) – otherwise length information will get lost and you won't ever know outside the function when you reached the file's end *unless* the file contains some specific sentinel marking the end of (like the null terminator for C-strings). – Aconcagua Feb 02 '22 at 16:44
  • Rather than reading into a `char[]` array, read into a `std::string` instead. See [How do I read an entire file into a std::string in C++?](https://stackoverflow.com/questions/116038/) – Remy Lebeau Feb 02 '22 at 18:24

1 Answers1

3

You need to add a null terminator to the string.

char * read_from_file(const char * path)
{
    std::ifstream fin(path, std::ios::binary);
    if (fin)
    {        
        fin.seekg(0, fin.end);
        size_t length = fin.tellg();
        fin.seekg(0, fin.beg);       
        
        char * buff = new char[length+1]; // allow room for terminator
        fin.read(buff, length);
        buff[length] = '\0'; // add terminator
        
        std::cout << buff << "\n";

        fin.close();
        return buff;
    }
    return nullptr;
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Maybe worth mentioning that binary files containing null bytes break this solution. – Aconcagua Feb 02 '22 at 16:47
  • Also, binary files should be opened in `ios::binary` mode, to avoid linebreak conversions in the default text mode, which can thrown off the length calculation. – Remy Lebeau Feb 02 '22 at 18:22