2

I'm making a C++ program to be able to open a .bmp image and then being able to put it in a 2D array. Right now i have the code like this:

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include "Image.h"
using namespace std;

struct colour{
    int red;
    int green;
    int blue;
};

Image::Image(string location){

    fstream stream;
    string tempStr;

    stringstream strstr;
    stream.open(location);

    string completeStr;

    while(!stream.eof()){
        getline(stream, tempStr);
        completeStr.append(tempStr);
    }
    cout << endl << completeStr;

    Image::length = completeStr[0x13]*256 + completeStr[0x12];
    Image::width = completeStr[0x17]*256 + completeStr[0x16];
    cout << Image::length;
    cout << Image::width;
    cout << completeStr.length();

    int hexInt;
    int x = 0x36;
    while(x < completeStr.length()){
        strstr << noskipws << completeStr[x];
        cout << x << ": ";
        hexInt = strstr.get();
        cout << hex << hexInt << " ";
        if((x + 1)%3 == 0){
            cout << endl;
        }
        x++;
    }
}

Now if i run this on my test file of 256x256 it will print fine, until it reaches 0x36E where it gives an error / doesn't go further. This happens because the completeStr string doesn't recieve all the data that is in the bmp file. Why isn't able to read all the lines in the bmp file?

Marckvdv
  • 139
  • 1
  • 9
  • 3
    `while (!eof())` is [buggy and wrong](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong). I wouldn't be surprised if that's your problem. – chris Apr 05 '13 at 17:32
  • 1
    You might consider reading the bitmap as a binary file, which it is, not a collection of lines. – Retired Ninja Apr 05 '13 at 17:37
  • possible duplicate of [Reading a file to a string in C++](http://stackoverflow.com/questions/3286822/reading-a-file-to-a-string-in-c) – Jerry Coffin Apr 05 '13 at 17:45

2 Answers2

8

There are a number of problems with your code. The principal one (and probably the reason for your problem) is that you are opening the file in text mode. Technically, this means that if the file contains anything but printable characters and a few specific control characters (like '\t'), you have undefined behavior. In practice, under Windows, this means that sequences of 0x0D, 0x0A will be converted into a single '\n', and that a 0x1A will be interpreted as the end of the file. Not really what one wants when reading binary data. You should open the stream in binary mode (std::ios_base::binary).

Not a serious error, but you shouldn't really use an fstream if you are only going to read the file. In fact, using an fstream should be very rare: you should use either ifstream or ofstream. The same thing holds for stringstream (but I don't see any role for stringstream when reading a binary file).

Also (and this is a real error), you are using the results of getline without checking whether is succeeded. The usual idiom for reading lines would be:

while ( std::getline( source, ling ) ) ...

But like stringstream, you don't want to use getline on a binary stream; it will remove all of the '\n' (which have already been mapped from CRLF).

If you want all of the data in memory, the simplest solution is something like:

std::ifstream source( location.c_str(), std::ios_base::binary );
if ( !source.is_open() ) {
    //  error handling...
}
std::vector<char> image( (std::istreambuf_iterator<char>( source ) ),
                         (std::istreambuf_iterator<char>()) );
James Kanze
  • 142,482
  • 15
  • 169
  • 310
  • Thanks! I'm a java programmer trying to shift over to c++. Wouldn't it be faster to extract to file size from the binary file, and then making an array with that size? Assuming that a vector is slower than an array because it needs resizing. – Marckvdv Apr 06 '13 at 12:44
  • @user2250025 Probably slightly. It sounds to me like he's doing image processing afterwards, though, and I wouldn't imagine that the difference is significant. Not to mention that there's no portable way of finding out the file size, and if you start accepting non-portable constructs, `mmap` is significantly faster. – James Kanze Apr 06 '13 at 18:56
2

std::getline reads in a line of text.

It's not useful for a binary file.

Open the file in binary mode and use unformatted input operations (like read).

Cheers and hth. - Alf
  • 135,616
  • 15
  • 192
  • 304