Link to home
Start Free TrialLog in
Avatar of WillHunting79
WillHunting79

asked on

Convert jpg file in hex string

I need to read a jpg file and convert it in a hex string in c++ 6.0 beacuse i need to create an rtf file with image but with fieldinst i can't resize the dimensions of the image.
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Ooops, sorry - correction: The line

  FILE* pOut = fopen(szOut,"wb");

Open in new window


should actually read

  FILE* pOut = fopen(szOut,"w");

Open in new window


So make that

#include <stdio.h>

unsigned int ConvertFileToHex(const char* szIn, const char* szOut) {

  unsinged int un = 0;

  FILE* pIn = fopen(szIn,"rb");

  if (!pIn) return 0;

  FILE* pOut = fopen(szOut,"w");

  if (!pOut) return 0;

  char ac[3];

  while(!feof(pIn)) {

    sprintf (ac, "%02X", (char)fgetc(pIn)); // cast added, just to be sure
    fprintf(pOut, "%s", ac);
  }

  fclose(pIn);
  fclose(pOut);

  return un;
}
                                          

Open in new window

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of phoffric
phoffric

I am assuming that both the posts by jkr and sarabande are functionally the same. If so, then if performance is an issue, then comparing the two versions, I suspect that jkr's code should run faster than sarabande's code because of extra (hidden) memory allocations in sarabande's code. Also, it is a lot easier to read jkr's code. So, maybe for the additional difficulty in reading sarabande's code and possible performance hits, there is some greater benefit. I'd have to think about that (if I had time - sorry under a gun - no, make that a cannon).
Avatar of WillHunting79

ASKER

Hi Experts!
Thanks for your help!
The solution of sarabande doesn't work because VC++ 6.0 can't include vector.h and sstream.h
The solution of jkr works but with a little correction.

unsigned int ConvertFileToHex(CString szIn, CString szOut)
{
      unsigned int un = 0;

      FILE* pIn = fopen(szIn,"rb");

      if (!pIn)
            return 0;

      FILE* pOut = fopen(szOut,"w");

      if (!pOut)
            return 0;

      char ac[100];

      while(!feof(pIn))
      {
            sprintf (ac, "%02x", fgetc(pIn)); // without cast, with cast doesn't work
            fprintf(pOut, "%s", ac);
      }

      fclose(pIn);
      fclose(pOut);

      return un;
}
I've a question for jkr.
I wish that the output was directly a CString if it's possible because i write the output file, i read it in a CString and i delete the output file.
Thanks!
... doesn't work because VC++ 6.0 can't include vector.h and sstream.h
it includes <vector> and <sstream> (without .h) and the solution is tested.

all stl headers don't have a .h extension. vc6 was released 1998 same as c++ standard. ms participated in the standard committee and therefore added a valid stl library which was sufficiently compliant to the standard that was released a few months later.

the solution should be much faster as it reads the file not character by character but in total. there is only one allocation at begin of the read.

the readability of a c++ solution normally is better than that of equivalent c code (for developers who know both). especially the absence of functions like fopen, sprint, and others with cryptic arguments should make it safer and better readable. probably it is my bad if it was not the case with the code posted.

Sara
I fail to see a point in provinding a solution specific to an outdated compiler.
I fail to see a point in provinding a solution specific to an outdated compiler.
I agree though both solutions are not specific to vc6 but will compile with any c++ compiler since and including vc6.

Sara
Hi experts!
Today i've tried again both solutions.
The jkr's solution with char ac[100] and without cast works and writes hex code in a output file.
The sarabande's solution, including vector and sstream without .h, works perfectly and writes hex code in a string.
Many thanks to both for your solutions!
Great Sara!
if you need the string as CString you may assign the output string to a CString after call like

std::string hexbuf;
if (!binfile2hexstring("mypicture.jpg", hexbuf)
      return -1;  // error
CString strHexBuf = hexbuf.c_str();

Open in new window


alternatively you may use CString instead of std::string (only if UNICODE is not defined for your project).

#include <sys/stat.h>
#include <sstream>
#include <vector>
#include <iomanip>
#include <fstream>

bool binfile2hexstring(const CString & inputfile, CString & hexbuf)
{
     struct stat fs = { 0 };
     if (stat(inputfile, &fs) != 0 || fs.st_size == 0)
          return false;   // file does not exist or is empty
     std::ifstream ifjpg(inputfile, std::ios::in | std::ios::binary);
     CString buf;
     if (!ifjpg.read(buf.GetBuffer(fs.st_size))
          return false;  // invalid read
     ifjpg.close();
     
     std::ostringstream oss;
     for (int n = 0; n < (int)fs.st_size; ++n)    
           oss << std::hex << std::setw(2) << std::setfill('0') << std::right << (unsigned int)buf[n];

     hexbuf = oss.str().c_str();
     return true;
}

Open in new window

the CString provides an operator LPCSTR which automatically converts a CString to const char * if required (for example in stat function). also the member function GetBuffer is quite useful to get a writeable char buffer. Note, don't call ReleaseBuffer as we have requested the exact count of characters needed and don't want the CString to trim the binary data read from file.

Sara
Hi Sara!
I've already converted the string with the function .c_str() in CString.
Thanks again!
Hmm, maybe. But, let's face it - while I am still fond of supporting 'old' compilers, VC6 is *way* too old. Time to talk to one's employer and bring up the fact that being too tight with money can be a wrong decision ;-)

(licensing issues with Express versions set aside)
FYI - On my current project, we are not permitted to use std::ostringstream as it is considered a heavyweight function - there are hidden memory allocations (and subsequent deallocations) that severely impact performance on our systems. Sometimes the best solution doesn't use the latest C++ standard bells and whistles. (To compensate for its functionality, we use 3rd party libraries that do better than C++03.)
willhunting79, if another solution also helped to answer the question, it is fair to split the points.

we are not permitted to use std::ostringstream as it is considered a heavyweight function
it is the c++ way to convert from binary to hex digits. the number of hidden allocations probably is limited for a two-byte buffer. compared to sprintf function or CString::Format it is type-safe and cannot be misused by manipulating the format string. that's why compiler moaned 'sprintf' is unsafe.

but if speed is an issue you may use the following to convert from binary to hex:

char hexdigits[] = "0123456789ABCDEF";
hexbuf.resize(fs.st_size*2);
char * p = &hexbuf[0];
char * q = &buf[0];
for (int n = 0; n < (int)fs.st_size; ++n)    
{
     *p++ = hexdigits[((*q)&0xf0)>>4];  // convert high half-byte
     *p++ = hexdigits[((*q)&0x0f)];         // convert low half-byte
       q++;
}

Open in new window

I made some tests  and the above converted a 4 MB file in 10 milliseconds. using sprintf for conversion takes more than 300 milliseconds and using ostringstream it is about 1500 milliseconds.

bool binfile2hexstring1(const std::string & inputfile, std::string & hexbuf)
{
     struct stat fs = { 0 };
     if (stat(inputfile.c_str(), &fs) != 0 || fs.st_size == 0)
          return false;   // file does not exist or is empty
     std::ifstream ifjpg(inputfile.c_str(), std::ios::in | std::ios::binary);
     std::vector<unsigned char> buf(fs.st_size, '\0');
     if (!ifjpg.read((char *)&buf[0], fs.st_size))
          return false;  // invalid read
     ifjpg.close();
     hexbuf.resize(fs.st_size*2);
     char ac[3];
     for (int n = 0, m = 0; n < (int)fs.st_size; ++n)    
     {
         sprintf (ac, "%02X", buf[n]); 
         hexbuf[m++] = ac[0];
         hexbuf[m++] = ac[1];
     }
     return true;
}

bool binfile2hexstring2(const std::string & inputfile, std::string & hexbuf)
{
     struct stat fs = { 0 };
     if (stat(inputfile.c_str(), &fs) != 0 || fs.st_size == 0)
          return false;   // file does not exist or is empty
     std::ifstream ifjpg(inputfile.c_str(), std::ios::in | std::ios::binary);
     std::vector<unsigned char> buf(fs.st_size, '\0');
     if (!ifjpg.read((char *)&buf[0], fs.st_size))
          return false;  // invalid read
     ifjpg.close();
     std::ostringstream oss;
     for (int n = 0; n < (int)fs.st_size; ++n)    
           oss << std::hex << std::setw(2) << std::setfill('0') << std::right << (unsigned int)buf[n];

     hexbuf = oss.str();
     return true;
}

bool binfile2hexstring3(const std::string & inputfile, std::string & hexbuf)
{
     struct stat fs = { 0 };
     if (stat(inputfile.c_str(), &fs) != 0 || fs.st_size == 0)
          return false;   // file does not exist or is empty
     std::ifstream ifjpg(inputfile.c_str(), std::ios::in | std::ios::binary);
     std::vector<unsigned char> buf(fs.st_size, '\0');
     if (!ifjpg.read((char *)&buf[0], fs.st_size))
          return false;  // invalid read
     ifjpg.close();

    char hexdigits[] = "0123456789ABCDEF";
    hexbuf.resize(fs.st_size*2);
    char * p = &hexbuf[0];
    unsigned char * q = &buf[0];
    for (int n = 0; n < (int)fs.st_size; ++n)    
    {
         *p++ = hexdigits[((*q)&0xf0)>>4];
         *p++ = hexdigits[((*q)&0x0f)];
          q++;
    }
    return true;
}


 int main(int nargs, char * pszargs[])
 {
     std::string hexbuf;
     clock_t d1 = clock();
     if (nargs == 2 && pszargs[1][0] == '1')
     {
          binfile2hexstring1("c:\\temp\\x.xxx", hexbuf);
     }
     else if (nargs == 2 && pszargs[1][0] == '2')
     {
          binfile2hexstring2("c:\\temp\\x.xxx", hexbuf);
     }
     else if (nargs == 2 && pszargs[1][0] == '3')
     {
          binfile2hexstring3("c:\\temp\\x.xxx", hexbuf);
     }
     clock_t d2 = clock();
     std::cout << pszargs[1][0] << ": " << d2 - d1 << " msec" << std::endl;
     return 0;              
 }

Open in new window


by the way, I wonder why jkr's code was looked on as an equivalent answer to the original question. the output was a file and not a string as requested and obviously it is c code while the question was posted in the c++ topic area (only). c code can be used in a visual c++ and mfc environment but surely it is not the usual or recommended way to do so without need.

I also would recommend to keep up the ostringstream solution which pretty well shows the better readability of c++ (standard) over c which should outweigh the lower speed as long as you don't have high performance requirements.

Sara
sprintf and assembly language have been given a very bad rap due to people who do not know how to use them safely.

>> obviously it is c code while the question was posted in the c++ topic area (only). c code can be used in a visual c++

Jkr addressed this in his first post:
>> This is intentionally more C-like, since VC++ 6.0 was pre standard and I don't know whether it will accept standard C++ code from nowadays.

Your timing tests are interesting.. A 5x penalty for using ostringstream - did I read that right. Now I know why it is banned. If you want high performance then there is boost spirit karma and qi.

>> was looked on as an equivalent answer to the original question
I just read the author's post: "Many thanks to both for your solutions!"
visual c++ was always c++ (even the 16-bit windows 3.0 vc 1.1 variant of 1992) and never c-like. the mfc provided/provides a class wrapper of the winapi that covers more than 80 percent of the api. jkr was wrong with the assumption that vc 6.0 requires c code.

the sprintf has a 30x penalty for the conversion compared to using bit operations. the absolute times nevertheless are not dramatic. it always depends on the requirements whether that is an issue or not.

yes, the author thanked for both solutions. that's why I supported a split. the author's decision for a c++ solution nevertheless was ok (we have a lot questions with more than one solution and where not all of them were honored). it is also ok that the author was asked if they have considered a split. but I don't understand so much why we need to substantially discuss this question again because of that.

Sara
So, what you are saying is that ostringstream has a 150x penalty over the better c-like code that you presented. If you use boost, you may do even better.
I think http:#a40219068 should get 50 points for identifying that ostringstream runs 150x slower than a C-like solution.
ostringstream has a 150x penalty over the better c-like code

yes, it is a good info but it is not the answer to the original question nor is it important for the vast majority of applications. there are many ways to convert an integer to hex string and if best performance is a requirement you would need to use another method as when you want to show compliant and readable c++ code. also the expression "c-like" is not suitable for the improvement as c++ and c have same base when using bit operations on integers. c++ is not using c libraries for this. also the c++ standard library provides a class bitset which is highly optimized and should provide similar results as with plain bit operations at least compared with the sprintf which is c and nevertheless is 30 times slower.

you are in the best position for knowing what the solution is here can you PLEASE post the question IDs of the comments you feel the split should cover

Open in new window

there are two comments which provided a solution to the original question:

http:#a40139796 (jkr)
http:#a40142465 (sarabande)

the first one was "intentionally more C-like, since VC++ 6.0 was pre standard and I don't know whether it will accept standard C++ code" as remarked by jkr.

the second one was c++ code using functions of the c++ standard library stl which already was provided in the pre-standard compiler vc6. it compiles with any c++ compiler of the last 15 years and was the accepted solution before the question was reopened.

I would welcome if WillHunting79 again would close the question and split points to comments which were helpful for answering the question.

if a force close is required I would recommend to do an equally split by accepting the c++ solution.

Sara
>> nor is it important for the vast majority of applications.
I brought up performance because of your comment about c code, which I take issue with.
obviously it is c code while the question was posted in the c++ topic area (only). c code can be used in a visual c++ and mfc environment but surely it is not the usual or recommended way to do so without need.
You wrote
good info but it is not the answer to the original question nor is it important for the vast majority of applications.
This would be true if this were not in the C++ zone. The vast majority of applications these days that are not embedded, high performance, or  low latency systems are likely not written in C++ or C; there are a number of other languages that are likely better for those applications. For high performance, low latency systems, for example, C and C++ are the languages of choice. If, even a C system was done in Windows, Visual Studio (VC C++ 6.0) makes an excellent choice for the IDE.

To ignore such extremes in performance is not only interesting, but essential in the vast majority of  C/C++ applications these days.

If a force accept is performed, I would recommend my posts (50 points each):
http:#a40142623
http:#a40219068
I would welcome if WillHunting79 again would close the question and split points to comments which were helpful for answering the question.
I agree completely. At this point, I think WillHunting79 has all the information he needs to make an informative fair split. I have no objections to whatever WillHunting79 decides at this point.
I knew you would defend your opinion that the performance is essential.

but you should see that you put that point after the solution already was accepted by WillHunting79 and was reopened not because of the poor performance but because of a completely different reason. I assume it wasn't your intention to discredit the accepted solution by making the point but fact is that we now have more comments regarding the performance of one single statement in a solution than we had before reopening.

WillHunting79 wants to convert .jpg files. the biggest jpg file I found on my system was 600k in size. for that the absolute time of the slowest solution was 200 milliseconds. my average jpg size was less than 20k where a file was converted by any of the solutions within a not measurable time. so I maintain my statement that performance on converting bytes to hex strings it is not relevant for the majority of applications especially not for those made with Visual Studio which mainly was used for GUI applications. regarding visual studio there still seems to be a wrong estimate on your side as surely nearly all major windows desktop applications which firstly were released before 2002/2003 (where VS.NET was introduced) have been initially developed with visual studio vc++. only  a few of them would/could be ported to c# or vb.net, so any update or enhancement mostly was done still with vc++. even today I don't know any really big newer desktop application (for windows) which was developed solely with newer languages like java, vb.net or c#. that is similar to new c++ standard where it will last years if not decades until it could cut out the old standard.

Sara