8

I'm trying to read an image into a char array. Here is my try:

ifstream file ("htdocs/image.png", ios::in | ios::binary | ios::ate);
ifstream::pos_type fileSize;
char* fileContents;
if(file.is_open())
{
    fileSize = file.tellg();
    fileContents = new char[fileSize];
    file.seekg(0, ios::beg);
    if(!file.read(fileContents, fileSize))
    {
        cout << "fail to read" << endl;
    }
    file.close();
                
    cout << "size: " << fileSize << endl;
    cout << "sizeof: " << sizeof(fileContents) << endl;
    cout << "length: " << strlen(fileContents) << endl;
    cout << "random: " << fileContents[55] << endl;
    cout << fileContents << endl;
}

And this is the output:

size: 1944
sizeof: 8
length: 8
random: ?
?PNG

Can anyone explain this to me? Is there an end-of-file char at position 8? This example was taken from cplusplus.com

Running Mac OS X and compiling with XCode.

Community
  • 1
  • 1
Emil
  • 1,015
  • 3
  • 10
  • 18
  • possible duplicate of [C++ - Reading and writing binary file](http://stackoverflow.com/questions/5420317/c-reading-and-writing-binary-file) – Nawaz May 28 '11 at 15:39
  • No its not a duplicate, the problem OP is facing is different, not really a problem in source code(apart from leaking memory) but in understanding it. – Alok Save May 28 '11 at 16:04
  • You didn't state what output you expected instead. – Lightness Races in Orbit May 28 '11 at 16:05
  • I expected one of sizeof or length to be 1944, but considering the comments below I see now that it is not an output I can get – Emil May 28 '11 at 19:48

5 Answers5

10
  1. Returns the size of the file. size of your image.png is 1944 bytes.

    cout << "size: " << fileSize << endl;

  2. Returns the sizeof(char*), which is 8 on your environment. Note that size of any pointer is always the same on any environment.

    cout << "sizeof: " << sizeof(fileContents) << endl;

  3. The file you are reading is a binary file so it might contain 0 as a valid data. When you use strlen, it returns the length until a 0 is encountered, which in the case of your file is 8.

    cout << "length: " << strlen(fileContents) << endl;

  4. Returns the character at the 56th location (remember array indexing starts from 0) from start of file.

    cout << "random: " << fileContents[55] << endl;

A suggestion:

Do remember to deallocate the dynamic memory allocation for fileContents using:

delete[] fileContents;

if you don't, you will end up creating a memory leak.

Lightness Races in Orbit
  • 358,771
  • 68
  • 593
  • 989
Alok Save
  • 190,255
  • 43
  • 403
  • 518
  • Thank you for making it clear to me. I understand my logical errors. I tried to verify that I successfully read the whole image. Thank you! – Emil May 28 '11 at 19:55
4

fileSize - the number of bytes in the file.

sizeof( fileContents ) - returns the size of a char* pointer.

strlen( fileContents) - counts the number of characters until a character with a value of '0' is found. That is apparently after just 8 characters - since you are reading BINARY data this is not an unexpected result.

cout << fileContents - like strlen, cout writes out characters until one with a value of '0' is found. From the output it looks like some of the characters aren't printable.

Your example has some other issues - it doesn't free the memory used, for example. Here's a slightly more robust version:

vector< char > fileContents;

{
  ifstream file("htdocs/image.png", ios::in | ios::binary | ios::ate);
  if(!file.is_open())
    throw runtime_error("couldn't open htdocs/image.png");

  fileContents.resize(file.tellg());

  file.seekg(0, ios::beg);
  if(!file.read(&fileContents[ 0 ], fileContents.size()))
    throw runtime_error("failed to read from htdocs/image.png");
}

cout << "size: " << fileContents.size() << endl;

cout << "data:" << endl;
for( unsigned i = 0; i != fileContents.size(); ++i )
{
  if( i % 65 == 0 )
    cout << L"\n';

  cout << fileContents[ i ];
}
3

This answer of mine to another question should be exactly what you are looking for (especially the second part about reading it into a vector<char>, which you should prefer to an array.

As for your output:

  • sizeof(fileContents) return the size of a char *, which is 8 on your system (64 bit I guess)
  • strlen stops at the first '\0', just as the output operator does.
Community
  • 1
  • 1
Björn Pollex
  • 70,106
  • 28
  • 177
  • 265
2

What do you expect? png files are binary so they may contain '\0' character (character having numeric value 0) somewhere.

If you treat the png file contents as string ('\0' terminated array of characters) and print it as string then it will stop after encountering the first '\0' character.

So there is nothing wrong with the code, fileContents is correctly contains the png file (with size 1944 bytes)

size: 1944 // the png is 1944 bytes
sizeof: 8  // sizeof(fileContents) is the sizeof a pointer (fileContents type is char*) which is 8 bytes
length: 8  // the 9th character in the png file is '\0' (numeric 0)
random: ?  // the 56th character in the png file
?PNG       // the 5th-8th character is not printable, the 9th character is '\0' so cout stop here
dragon135
  • 1,326
  • 8
  • 16
  • I tried to varify that the image was successfully read to the array. But I see now that it probobly is. – Emil May 28 '11 at 19:51
0

It's a good practice to use unsigned char to use with binary data. The character randomly selected might not be displayed properly in the console window due to the limitations in the fonts supported. Also you can verify the same thing by printing it in hexadecimal and open the same file in a hex editor to verify it. Please don't forget to delete the memory allocated after use.

sarat
  • 9,454
  • 6
  • 38
  • 71