2

I was trying to read an entire file into a buffer using std::ifstream which failed for no obvious reason, so I constructed a minimal code example that demonstrates the problem:

std::vector<char> vec;
vec.resize(1000);
std::ifstream file("G:/Pictures/Webcam/Snapshot_20110209.jpg");
file.exceptions(std::ifstream::badbit | std::ifstream::failbit | std::ifstream::eofbit);
std::cout << file.good() << std::endl;
try {
    file.read(vec.data(), 100);
} catch (std::ios_base::failure f) {
    std::cout << f.what() << " Characters extracted: " << file.gcount() << std::endl;
} catch (...) {
    std::cout << "Some other error" << std::endl;
}
std::cout << "Done" << std::endl;
file.close();

The file I'm trying to read is 48kb of size, so reading 100 bytes shouldn't be the problem. The buffer in 1000 bytes big, so that should also be ok. Now, what happens is that the stream only reads 61 bytes and then sets the failbit. The output generated is the following:

1
ios_base::failbit set: iostream stream error Characters extracted: 61
Done

So for some reason the failbit is set after 61 bytes. If I read less than 61 bytes, it works. If I try to read more, it also fails at 61. I also tried other files of similar size, same problem. Some completely different files of different size showed the same behaviour, but after 166 bytes.

Now, if I use Qt's QFile class for reading the data, everything works fine and I can read the full file. The code looks like this then:

QFile file(path);
std::vector<char> buffer;
buffer.resize(file.size());
if (!file.open(QIODevice::ReadOnly)) return;
file.read(buffer.data(), file.size());
file.close();

I know, now you'd say here I'm only reading as much as the file's size is, but that is actually more than 61 bytes. Also reading a fixed 100 is no problem.

Lightness Races in Orbit
  • 358,771
  • 68
  • 593
  • 989
bweber
  • 2,976
  • 2
  • 20
  • 49

1 Answers1

8
std::ifstream file("G:/Pictures/Webcam/Snapshot_20110209.jpg");

Whoops! You're opening the file in text mode.

Depending on your platform, any number of nasty binary characters could make the stream think the data flow has ended, since different platforms use different "sentinel characters" to signal this (e.g. Ctrl+Z or 0x1A on Windows — is there a 0x1A at byte 62?1).

Here:

std::ifstream file("G:/Pictures/Webcam/Snapshot_20110209.jpg", std::ios::binary);

1 I have a JPEG file with 0x05 at that location; a quick glance at an EXIF format description makes me think we are both looking at the TIFF data field describing the horizontal resolution encoding, as 0x1A01 and 0x0500 are common options.

Lightness Races in Orbit
  • 358,771
  • 68
  • 593
  • 989
  • God, I'm such a fool. Thank you so much, that must have been wiped from my mind completely, I used it before. Somehow I managed to avoid any information about that flag in my internet research and also didn't have the idea to just look into some of my older code... – bweber Sep 27 '15 at 12:49
  • @user1488118: Don't worry; literally three days ago I realised I had the same bug in library code I'd been using in production for five years. :) – Lightness Races in Orbit Sep 27 '15 at 13:05