-1

I'm trying to create a program that displays output of a bmp file in the form of hexadecimal. So far I get the output, but I need it to be organized a certain way.

The way it needs to be organized is with the address of the bmp file to be on the left column and then 16 bytes of data in hex across each row in the order they appear in the file. While leaving an extra space between every 8 bytes. So far, I got the hexadecimal to show up, I just need help with organizing it.

What I have:

image

What I'm trying to make it look like:

image

Here is my code:

#include <iostream>     // cout
#include <fstream>      // ifstream
#include <iomanip>      // setfill, setw
#include <stdlib.h>


using namespace std;  // Use this to avoid repeated "std::cout", etc.

int main(int argc, char *argv[])  // argv[1] is the first command-line argument
[enter image description here][1]{


// Open the provided file for reading of binary data
ifstream is("C:\\Users\\Test\\Documents\\SmallTest.bmp", ifstream::binary);
if (is)   // if file was opened correctly . . . 
{

    is.seekg(0, is.end);   // Move to the end of the file
    int length = is.tellg();   // Find the current position, which is file length
    is.seekg(0, is.beg);  // Move to the beginning of the file

    char * buffer = new char[length]; // Explicit allocation of memory.

    cout << "Reading " << length << " characters... ";
    is.read(buffer, length);  // read data as a block or group (not individually)

    if (is)
        cout << "all characters read successfully.\n";
    else
        cout << "error: only " << is.gcount() << " could be read.\n";
    is.close();

    // Now buffer contains the entire file. The buffer can be printed as if it
    // is a _string_, but by definition that kind of print will stop at the first
    // occurrence of a zero character, which is the string-ending mark.
    cout << "buffer is:\n" << buffer << "\n";  // Print buffer

    for (int i = 0; i < 100; i++)  // upper range limit is typically length
    {


        cout << setfill('0') << setw(4) << hex << i << "   ";

        cout << setfill('0') << setw(2) << hex << (0xff & (int)buffer[i]) << " ";
    }

    delete[] buffer; // Explicit freeing or de-allocation of memory.
}
else // There was some error opening file. Show message.
{
    cout << "\n\n\tUnable to open file " << argv[1] << "\n";
}

return 0;

}

Jongware
  • 22,200
  • 8
  • 54
  • 100
Dylan
  • 1
  • 1
  • You'll have to explain what you are looking for as output better. What does header on the left column mean? – Retired Ninja Feb 06 '18 at 02:01
  • Sorry, instead of header, I mean the bmp address to be on the left column. I've attached images of what my current output is and what my output needs to look like. – Dylan Feb 06 '18 at 02:10
  • You might consider C++ function std::string dumpByteHex(...) in answer: https://stackoverflow.com/a/46083427/2785528 – 2785528 Feb 06 '18 at 04:31
  • I've removed the tag `bmp` because your code is not really *about* BMP files. This can be asked about *any* file type (and even about any hex dump as well). – Jongware Feb 16 '18 at 09:48

3 Answers3

1

You could do it something like this:

#include <iostream>
#include <iomanip>
#include <fstream>
#include <vector>
#include <cctype>

std::ostream& fullLine(std::ostream& out, const std::vector<uint8_t>& v, size_t offset)
{
    //save stream state so we can restore it after all the hex/setw/setfill nonsense.
    std::ios oldState(0);
    oldState.copyfmt(out);

    out << std::hex << std::setfill('0') << std::setw(8) << offset << "  ";
    for (size_t i = 0; i < 16; ++i)
    {
        if (i == 8) out << " ";
        out << std::hex << std::setfill('0') << std::setw(2) << static_cast<uint32_t>(v[i + offset]) << " ";
    }
    out << " ";

    //restore stream state to print normal text
    out.copyfmt(oldState);
    for (size_t i = 0; i < 16; ++i)
    {
        out << (std::isprint(v[i + offset]) ? static_cast<char>(v[i + offset]) : '.');
    }
    out << "\n";
    return out;
}

int main()
{
    std::vector<uint8_t> data;
    std::ifstream f("test.txt", std::ios::binary);
    if (f)
    {
        f.seekg(0, f.end);
        data.resize(static_cast<size_t>(f.tellg()));
        f.seekg(0, f.beg);
        f.read((char*)data.data(), data.size());
        const size_t numFullLines = data.size() / 16;
        const size_t lastLineLength = data.size() % 16;
        for (size_t i = 0; i < numFullLines; ++i)
        {
            if (!fullLine(std::cout, data, i * 16))
            {
                std::cerr << "Error during output!\n";
                return -1;
            }
        }
    }
    return 0;
}

There's probably a fancy way to do it, but I usually go for brute force when I'm looking for particular output using iostreams.

How to handle the partial last line is up to you. :)

Retired Ninja
  • 4,785
  • 3
  • 25
  • 35
0

Use the % operator to break the line after every 16th count:

cout << hex;
for(int i = 0; i < 100; i++)  
{
    if(i && (i % 16) == 0)
        cout << "\n";
    cout << setfill('0') << setw(2) << (buffer[i] & 0xFF) << " ";
}
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
0

I need it to be organized a certain way.

In another answer, I submitted this form of dumpByteHex()... perhaps it can help you achieve what you want. (see also https://stackoverflow.com/a/46083427/2785528)

// C++ support function
std::string  dumpByteHex (char*       startAddr,  // reinterpret_cast explicitly
                          size_t      len,        //     allows to char* from T*
                          std::string  label = "",
                          int          indent = 0)    
{
   std::stringstream ss;

   if(len == 0) {
      std::cerr << "\n  dumpByteHex() err: data length is 0? " << std::endl << std::dec;
      assert(len != 0);
   }

   // Output description
   ss << label << std::flush;

   unsigned char* kar = reinterpret_cast<unsigned char*>(startAddr); // signed to unsigned

   std::string echo;  // holds input chars until eoln

   size_t indx;
   size_t wSpaceAdded = false;
   for (indx = 0; indx < len; indx++)
   {
      if((indx % 16) == 0)
      {
         if(indx != 0) // echo is empty the first time through for loop
         {
            ss << "  " << echo << std::endl;
            echo.erase();
         }

         // fields are typically < 8 bytes, so skip when small
         if(len > 7) {
            if (indent) { ss << std::setw(indent) << "  "; }
            ss << std::setfill('0') << std::setw(4) << std::hex
               << indx   << " " << std::flush;
         } // normally show index
      }

      // hex code
      ss << " " << std::setfill('0') << std::setw(2) << std::hex
         << static_cast<int>(kar[indx]) << std::flush;

      if((indx % 16) == 7) { ss << " "; wSpaceAdded = true; } // white space for readability

      // defer the echo-of-input, capture to echo
      if (std::isprint(kar[indx])) { echo += kar[indx]; }
      else                         { echo += '.'; }
   }

   // finish last line when < 17 characters
   if (((indx % 16) != 0) && wSpaceAdded) { ss << "  ";  indx++; } // when white space added
   while ((indx % 16) != 0)               { ss << "   "; indx++; } // finish line

   // the last echo
   ss << "   " << echo << '\n';

   return ss.str();
} // void dumpByteHex()

Output format:

0000  11 22 33 44 55 66 00 00  00 00 77 88 99 aa        ."3DUf....w...
2785528
  • 5,438
  • 2
  • 18
  • 20