Guys,

Either there is a bug in the compiler or I'm getting dumber by the day. I want to read an entire file into memory. (Very small file, so no worries about size here). I followed the example here (from www.cplusplus.com)

// reading binary file
#include <iostream.h>
#include <fstream.h>

const char * filename = "example.txt";
int main ()
{
   char * buffer;
   long size;
   ifstream file (filename, ios::in|ios::binary|ios::ate);
   size = file.tellg();
   file.seekg (0, ios::beg);
   buffer = new char [size];
   file.read (buffer, size);
   file.close();
   cout << "the complete file is in a buffer";
   delete[] buffer;
   return 0;
}

<< moderator edit: added [code][/code] tags and reformatted >>

However, when I use this code, I always get the first character of the file twice!!! For example, if I have a file which contains:
SAM LOVES TO TEST FILES

I end up with:
SSAM LOVES TO TEST FILES

Is this a known bug or am I not supposed to believe this reputable site? Is there a 'C' way to do this which is better?

Thanks,
Winbatch

Recommended Answers

All 18 Replies

I didn't see the issue, but WRT a C version I think it would be something like this.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
   static const char filename[] = "file.txt";
   FILE *file = fopen(filename, "rb");
   if ( file )
   {
      char *buffer;
      long size;
      fseek(file, 0, SEEK_END);
      size = ftell(file);
      rewind(file);
      buffer = malloc(size);
      if ( buffer )
      {
         if ( fread(buffer, size, 1, file) == 1 )
         {
            /* puts(buffer); */
         }
      }
      fclose(file);
      free(buffer);
   }
   return 0;
}

Dave,

When you used the code I posted you didn't have any trouble? Wonder if it's a compiler specific bug - I'm using Sun Workshop 6 on solaris... I tried your code and it works... I put the puts(buffer) back ;)


Winbatch

First, I rarely start by looking at the compiler; I'd say about 99% of the time it is the code. Just because I didn't see what you saw doesn't mean there wasn't an issue with it.

I think in part what is missing is what is missing. How do you see what you see? Where is some sort of output statement?

One of the reasons I put the puts line in comments is that it probably isn't what you are doing and it's probably not the correct thing to do. And perhaps that is the issue -- on the output side, not the input side.

This was the code:

// reading binary file
#include <iostream.h>
#include <fstream.h>
const char * filename = "test.txt";
int main ()
{
   char * buffer;
   long size;
   ifstream file (filename, ios::in|ios::binary|ios::ate);
   size = file.tellg();
   file.seekg (0, ios::beg);
   buffer = new char [size];
   file.read (buffer, size);
   file.close();
        cout<<buffer<<endl;
   delete[] buffer;
   return 0;
}

(Also note that cout<<buffer[0]<<endl; and cout<<buffer[1]<<endl; also produced the same letter.....)

Hmm. When I run either C or C++ version in the command shell in SlickEdit (my usual location that helps discover things like the need for fflush(stdout) and such) I get a blank area where the text should be. To me this would tell me that I might be missing something. When I run it in a cmd shell this does not happen. [edit]Ah the CR of the CR-LF was erasing the line for the SlickEdit output window. A fallout of binary mode reading.[/edit]

I am using Windows, so I might suspect my version would have issues with the binary vs text modes. But I wouldn't expect such things on Solaris. Outputting one at a time from 0 to size would have been my suggestion, so I'm a little miffed.

What is the "big picture" of what you are trying to do?


[edit2]This is the code I am using.

#include <iostream>
using std::cout;
using std::endl;
using std::ios;
#include <fstream>
using std::ifstream;

const char filename[] = "file.txt";

int main ()
{
   ifstream file(filename, ios::in | ios::binary | ios::ate);
   long size = file.tellg();
   file.seekg(0, ios::beg);
   char *buffer = new char [size];
   file.read(buffer, size);
   file.close();
   for ( long i = 0; i < size; ++i )
   {
      cout << buffer[i];
   }
   cout << endl;
   delete[] buffer;
   return 0;
}

/* file.txt (no newline at end)
SAM LOVES TO TEST FILES
*/

/* my output
SAM LOVES TO TEST FILES
*/

The big picture is that I am loading an MQ series queue with the text of a file. (It's a command line utility). Since I don't know the size of the message at the start, I am reading it in binary so to that I can set the size of the buffer dynamically. In any case, I was able to get it to work with the C code version, so I'm good to go.

A vector of strings for C++?

I don't need a vector, I have a single string. (The file contains one message, not a bunch of messages)

Another stray thought:

#include <iostream>
#include <fstream>
#include <sstream>

int main ()
{
   static const char filename[] = "file.txt";
   std::ifstream file(filename);
   std::ostringstream text;
   text << file.rdbuf(); // slurp
   std::cout << text.str() << std::endl;
   return 0;
}

/* my output
SAM LOVES TO TEST FILES
*/

That works nicely - I guess rdbuf reads the entire contents in one shot? (If so, it's nice to not to have to do the size calculation and to just use string's dynamic allocation)... How big a file you think I need to try this with to really test it out?

This took about 30 seconds for the 1 Meg file. (Made a little change, too.)

#include <iostream>
#include <fstream>
#include <sstream>

int main ()
{
   static const char filename[] = "file.txt";
   std::ifstream file(filename, std::ios::binary);
   std::ostringstream text;
   text << file.rdbuf(); // slurp
   std::cout << "okay" << std::endl;
   return 0;
}

/* my output
C:\Test>"TestPP.exe"
okay

C:\Test>dir file.txt
 Volume in drive C has no label.
 Volume Serial Number is 3BA1-7549

 Directory of C:\Test

07/14/2005  01:08p           1,179,648 file.txt
               1 File(s)      1,179,648 bytes
               0 Dir(s)   7,913,684,992 bytes free
*/

Dave,

I ran it on a SUN box (4 cpu machine), a 1 mb file took 7 seconds in your new way (with the stringstream) and basically 0 seconds in the 'C' way you provided earlier... Guess which one I'm going to stick with ... ;)
14-JUL-05 14:30:47->Program Start, HoffLib version: 2.3.5 JULY 14, 2005
14-JUL-05 14:30:47->STARTING stringstream WAY
14-JUL-05 14:30:54->ENDING 1229678
14-JUL-05 14:30:54->STARTING C/FILE * WAY
14-JUL-05 14:30:54->ENDING 1229678

(1229678 was the size read, just to confirm I'm getting the same data)

Yay C! :p

I'm not sure if it would make any noticeable difference, but sometimes the platform-specific functions can be quicker, too. Such as maybe using stat. But maybe even the platform's read may be speedier too. (I remember a contest on another forum a while back.) If speed is a real issue.

Speed is definitely an issue as this function is part of my new 'library' that I'm building. The performance difference is HUGE with 3.8 MB...

14-JUL-05 14:41:58->Program Start, HoffLib version: 2.3.5 JULY 14, 2005
14-JUL-05 14:41:58->STARTING stringstream WAY
14-JUL-05 14:42:56->ENDING 3807956
14-JUL-05 14:42:56->STARTING C/FILE * WAY
14-JUL-05 14:42:57->ENDING 3807956
14-JUL-05 14:42:57->SAME
14-JUL-05 14:42:57->Program Completion, Total Runtime: 59 SECOND(S). Return Code: [0]

By the way (somewhat unrelated), is there any way in C/C++ to keep track of milliseconds? the time_t structure only seems to go up to seconds. (Since java has milliseconds, I was hoping there would be a way in C++, but I suspect no..)

>is there any way in C/C++ to keep track of milliseconds?
No, the smallest portable measurement is a second. However, most systems provide a way to have sub-second measurements. Check your man pages.

I primarily use Sun solaris. I'm not even sure what to search for in the man pages...

How about:

$ man -k second

man -k second
/usr/dt/man/windex: No such file or directory
/usr/man/windex: No such file or directory
/usr/openwin/share/man/windex: No such file or directory


whereas any other command like ,
man whodo
Reformatting page. Please Wait... done
System Administration Commands whodo(1M)
NAME
whodo - who is doing what
SYNOPSIS
/usr/sbin/whodo [-h] [-l] [user]
DESCRIPTION
The whodo command produces formatted and dated output ...etc..

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.