0

When I search this topic, I find a lot of examples of how to correctly access a binary file using std::fstream. I lifted a snippet and it compiles fine (Visual Studio 2017). However, at runtime (this code is within a DLL), the file doesn't seem to open. I am assuming the file doesn't have to be *.bin or something to work... is that correct? I can't think of what else is going off the rails. The reading into a buffer stuff is probably wrong - I can't even get to that part yet.

std::fstream binaryFile;
binaryFile.open(cdgName, std::ios::binary);
if (binaryFile.good())
{
    BYTE i[24];
    while (binaryFile >> i)
    {
        //handle
    }
    binaryFile.close();
} else 
{
    MessageBoxA(NULL, cdgName, "File Error!", MB_OK);
}

When I try this, the MessageBox shows. FYI, the window contains the file path (cdgName) and it is "C:\Users\Rick\Music\America - You Can Do Magic.cdg", which is a valid file. I also have tried .is_open() instead of .good(). I have tried variations with ifstream and using the ::ios::in flag - not knowing exactly what I am doing - which caused errors at run.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) – Jesper Juhl Nov 04 '22 at 20:11
  • 1
    Try with the additional flag `std::ios::in`. I had a similar issue using `std::ifstream` where the the file was only opened correctly when specifying in, even though that shouldn't be necessary for `std::ifstream`. `binaryFile.open(cdgName, std::ios::binary | std::ios::in);` – fabian Nov 04 '22 at 20:12
  • Thanks fabian - I was editing the post while you were responding to add the last few lines. Inspired by your post I tried std::ifstream binaryFile; binaryFile.open(cdgName, std::ios::in | std::ios::binary); which compiles but at run time a get some kind of memory violation error. – theDickChuck Nov 04 '22 at 20:18
  • Okay - I think you rocked it. I used your suggestion and gutted all the attempting to actually access the data. That seems to at least open the file and NOT crash. Any ideas on how to read the data in as 24 byte chunks until the end of file? :) – theDickChuck Nov 04 '22 at 20:23
  • 1
    @theDickChuck That should be a separate question. Don't forget to include a [mre]. Thanks. – Paul Sanders Nov 04 '22 at 20:56
  • @theDickChuck "*Any ideas on how to read the data in as 24 byte chunks until the end of file*" - run a loop that uses `ifstream::read()` instead of `operator>>`. – Remy Lebeau Nov 04 '22 at 21:35

1 Answers1

0

Standard library's streams are pretty awful. One of the more important things they don't do is to help you debug.

If you do something like this instead then you can get a useful error message beyond just a failure.

Consider this main.cpp:

#include <array>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <functional>
#include <memory>
#include <stdexcept>
#include <string>
#include <string_view>

using BYTE = char;

auto
read_my_file(std::string_view cdgName)
{
    std::string name{cdgName}; // ensure that a null terminating byte is there
    using file_ptr = std::unique_ptr<std::FILE, std::function<decltype(std::fclose)>>;
    file_ptr f{std::fopen(name.c_str(), "r"), &std::fclose};
    if (nullptr == f)
    {
        throw std::runtime_error{std::string{"fopen(): "} + std::strerror(errno)};
    }
    std::array<BYTE, 24u> out{};
    auto count = std::fread(out.data(), sizeof(BYTE), out.size(), f.get());
    if (0 != std::ferror(f.get()))
    {
        throw std::runtime_error{std::string{"fread(): "} + std::strerror(errno)};
    }
    if (count < out.size())
    {
        throw std::runtime_error{std::to_string(count)};
    }
    return out;
}

#include <cstdlib>
#include <iostream>
int main(int argc, char **argv)
try
{
    if (argc <= 1)
    {
        return EXIT_FAILURE;
    }
    auto out = read_my_file(argv[1]);
    std::cout << out.data() << std::endl;
    return EXIT_SUCCESS;
}
catch (const std::runtime_error& e)
{
    std::cerr << e.what() << std::endl;
    return EXIT_FAILURE;
}

Compile with g++ -std=c++17 main.cpp. Then consider some example invokations:

$ ./a.out

this is empty, but process exit code is 1

$ ./a.out example.txt

-> fopen(): No such file or directory

$ echo "foo" > example.txt && ./a.out example.txt

-> 4 (only 4 bytes in the file, not the 24 requested)

$ echo "hello world this is an example" > example.txt && ./a.out example.txt

-> hello world this is an e

OK so that's the happy path, and one of the sad paths. But suppose it's a permissions issue you're seeing?

chmod 700 example.txt && sudo chown root:root example.txt && ./a.out example.txt

-> fopen(): Permission denied

I don't believe fstream can do something like that...

inetknght
  • 4,300
  • 1
  • 26
  • 52