Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

need help converting c to c++ (simple error but cant fix)

Tags:

c++

I have a c++ homework. The homework is asking to convert a c program to c++. Below is the question:

You are requested to convert the following C function into a C++ function and then embed it into a complete program and test it. Note that this function copies a binary file of integers and not a text file. The program must accept the arguments (the file to copy and the file to be copied to) from the command line.

/* ==================== cpyFile =====================

This function copies the contents of a binary file
of integers to a second file.
Pre fp1 is file pointer to open read file
fp2 is file pointer to open write file
Post file copied
Return 1 is successful or zero if error
*/
int cpyFile (FILE *fp1, FILE *fp2)
{
  /* Local Definitions */
  int data;

  /* Statements */
  fseek (fp1, 0, SEEK_END);
  if (!ftell (fp1))
  {
    printf ("\n\acpyFile Error : file empty\n\n");
    return 0;
  } /* if open error */
  if (fseek (fp1, 0, SEEK_SET))
    return 0;
  if (fseek (fp2, 0, SEEK_SET))
    return 0;

  while (fread (&data, sizeof (int), 1, fp1))
    fwrite (&data, sizeof (int), 1, fp2);
  return 1;
} /* cpyFile */

I did my best and managed to convert it, but unfortunately when I'm using it , the file that I get after the copy is empty. Below is my answer:

#include <fstream>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
  if(argc!=3)
  {cerr<<"invalid number of arguments. must be 3."<<endl;exit(1);}

  fstream fp1(argv[1],ios::in);
  if(!fp1)+{cerr<<argv[1]<<" could not be opened"<<endl;exit(1);}

  fstream fp2(argv[2],ios::out);
  if(!fp2)+{cerr<<"file could not be found."<<endl;exit(1);}


  int data;

  fp1.seekg (0,ios::end);
  if (!fp1.tellg ())
  {
    cout<<"\n\acpyFile Error : file empty\n\n";
    return 0;
  } /* if open error */
  if (fp1.seekg (0, ios::beg))
    return 0;
  if (fp2.seekg (0, ios::beg))
    return 0;

  while (fp1.read (reinterpret_cast<char*>(&data), sizeof (int)))
  {
    fp2.seekp(0);
    fp2.write (reinterpret_cast<char*>(&data), sizeof (int));
  }
  return 1;
}

I did my best and everything is working fine, except that when I copy a binary file, the file that i get is empty and I have no idea why.

like image 872
LebTech Avatar asked Jan 11 '12 23:01

LebTech


3 Answers

You need to open the file in binary mode, as others have said, by doing

fstream fp1(argv[1], ios::in | ios::binary); // combine ios::in with ios::binary

fstream fp2(argv[2], ios::out | ios::binary); // combine ios::out with ios::binary

Or you can make them ifstream (in file stream for reading only) and ofstream (out file stream, for writing only) and remove the ios::in and ios::out because ifstream implies ios::in and ofstream implies ios::out:

ifstream fp1(argv[1], ios::binary);

ofstream fp2(argv[2], ios::binary);

You need to do this because if you don't, the file will be translated when you read from or write to it for things like turning line endings from \r\n or \r to just \n, etc, which will mess up your binary data which may happen to have those bytes in them.

This:

if (fp1.seekg (0, ios::beg))
    return 0;

if (fp2.seekg (0, ios::beg))
    return 0;

Will always make your code return because seekg returns the object you call it on. It's not the equivalent of fseek in this regard because fseek returns 0 on success. So you never get to the while loop. Take those out of the if statements so that it looks like this:

fp1.seekg(0, ios::beg);
fp2.seekg(0, ios::beg);

Or if you have to have the checking, you want to do

if (!fp1.seekg (0, ios::beg)) // notice the added !
    return 0;

if (!fp2.seekg (0, ios::beg)) // notice the added !
    return 0;

Also, this (inside the while):

fp2.seekp(0);

Is setting the point you are going to write to to the beginning of the file. So you'll never write anything but at the beginning of the file. Just remove that line completely.

Also, you have a return inside the loop which makes it return on the first iteration. Move the return 1; outside the loop so you only return after the loop is finished. Nevermind that, misread due to the unusual brace style.

like image 56
Seth Carnegie Avatar answered Nov 17 '22 22:11

Seth Carnegie


Every time you read a new data block from fp1, you rewind fp2 to the beginning of the stream, essentially discarding what you have already written to fp2. Try moving fp2.seekp(0) out of your main loop.

like image 36
Tamás Avatar answered Nov 17 '22 22:11

Tamás


You have a few problems. I'd start by fixing this bit:

if (fp1.seekg (0, ios::beg))
    return 0;
if (fp2.seekg (0, ios::beg))
    return 0;

The seekg method returns a reference to the istream it's called on, so the above is equivalent to this:

fp1.seekg (0, ios::beg);
if (fp1) // i.e., if fp1 is in a valid state (as opposed to e.g. end-of-file)
    return 0;
fp2.seekg (0, ios::beg);
if (fp2) // i.e., if fp2 is in a valid state (as opposed to e.g. end-of-file)
    return 0;

which is obviously not what you want.

To debug your code, you can use statements like std::cout << "Got to line " << __LINE__ << std::endl; to figure out which parts of the program are actually being run. That would have found the above problem pretty quickly.

like image 37
ruakh Avatar answered Nov 18 '22 00:11

ruakh