5

I am trying to achieve something like this:

while (ifstream has not been entirely read)
{
   read a chunk of data into a buffer that has size BUFLEN
   write this buffer to ostream
}

At first I tried to achieve this by using ifstream.eof() as my while condition, but I've heard that this is not the way to go. I've been looking at std::ios::ifstream's other functions, but can't figure out what else to use.

PS: I am using a buffer because the file that is being transferred can get very big.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
codd
  • 612
  • 1
  • 7
  • 15

3 Answers3

7

The iostream classes take care of all necessary buffering, so you don't have to. The usual idiom to copy an entire file is just:

fout << fin.rdbuf();

iostream takes care of all of the necessary buffering. (This is a somewhat unusual use of <<, since it doesn't format. Historical reasons, no doubt.)

If you need the loop, perhaps because you want to do some transformations on the data before rewriting it, then it's a little tricker, since istream::read “fails” unless it reads the requested number of characters. Because of this, you have to also check how many characters were read, and process them even if the read failed:

int readCount;
while ( fin.read( &buf[0], buf.size() )
        || (readCount = fin.gcount()) != 0 ) {
    //  ...
    fout.write( &buf[0], readCount );
}

This is fairly ugly; a better solution might be to wrap the buffer in a class, and define an operator<< for this class.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • So if I have to read a very big file and write that to some other file, I must not worry about using a buffer, iostream does this already? I am, however, doing it because I am sending the data over TCP in a later stage. Thanks for the code, I will try it now. – codd Nov 25 '11 at 13:35
  • On some other note: should this also work when I work with non-text files? Because I need to be able to do this with any sort of file (pdf, mp3, ...). And it does not seem to work on a pdf I tested it with. – codd Nov 25 '11 at 13:43
  • It should work with any file as long as you open the in and out files as binary. – Retired Ninja Nov 25 '11 at 13:49
  • @Retired Ninja weirdly enough it does not seem to work with my example PDF. I am using a char buffer[BUFLEN] as buffer. I've only changed buf.size() to BUFLEN. – codd Nov 25 '11 at 13:55
  • If the read doesn't fail, then how does `readCount` get set? Should it be initialized to `buf.size()`? – Kerrek SB Nov 25 '11 at 14:05
  • @codd Make sure that where you open the files looks something like this: `std::ifstream infile("infile", std::ios::binary); std::ofstream outfile("outfile", std::ios::binary);` – Retired Ninja Nov 25 '11 at 14:09
  • @KerrekSB Good point. You really need to do the read separately, then set readCount, and then test `fin || readCount != 0`. (As you can see, I don't use streams much for raw reading and writing.) – James Kanze Nov 25 '11 at 15:33
  • How about an abused `for` loop: `for (std::size_t rc = buf.size(); fin.read(buf.data(), rc) || (rc = fin.gcount()); ) { fout.write(buf.data(), rc); }` – Kerrek SB Nov 25 '11 at 15:36
  • @KerrekSB as you say, "abused". If you're doing this a lot, I'd create some sort of buffer class which handled it automatically. Otherwise, I rather like the solution of Joachim Pileborg. – James Kanze Nov 25 '11 at 16:09
6

The istream::read function returns the stream, which can be used as a boolean expression, so you can do something like:

while (is.read(buffer, BUFLEN))
{
    outputfile.write(buffer, is.gcount());
}

if (is.eof())
{
    if (is.gcount() > 0)
    {
        // Still a few bytes left to write
        outputfile.write(buffer, is.gcount());
    }
}
else if (is.bad())
{
    // Error reading
}

You might want to check that the write inside the loop doesn't fail too.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • The following code gave me an empty new file, perhaps you know what the issue with it is? `while (is.read(buffer,BUFLEN)) { outfile.write(buffer,BUFLEN); }` – codd Nov 25 '11 at 13:23
  • Thanks for the example code Joachim. This also seems to work with other files such as pdf's etc! – codd Nov 25 '11 at 13:45
0

Your logic is simply wrong.

You need to go about it more like this:

while (a chunk larger than zero could be read)
{
  write chunk to output
}

See how that is even simpler? There's no need to explicitly check for "end of file", just read data until you fail. Then you're done.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • I see what you mean. I tried achieving that by using read() as condition, but read() gave an empty file and !read() kept rewriting my file with garbage in between the copies. Would you mind elaborating and giving me specific functions to use? – codd Nov 25 '11 at 13:22