Question:
Why can't my C program copy video/JPG files even if I use "r+b"/"w+b" binary mode?
electric
2012-09-18 13:15:06 UTC
A variant of this program can copy text files well ( in the default text mode).But if I use the following program with binary mode to copy a video file "shrek_trailer.mp4" on my hard disk, then the destination file "trailer_copy.mp4" is indeed created, but only sized 0 byes!! That means it exists in name only, not a copy of the video file, as if with no contents.If i change the source file to a graphic JPEG file, then again a new JPEG file is created but with 0 bytes size.What's going on? PLZ HELP.

Is my notion that mp4 and JPEG files are BINARY files, while .TXT files are text files wrong?How exactly would you define a binary file?I mean, after all, all files, including txt files, are stored as zeroes and ones,aint' they?Please shed some light in the matter.Thanks!!

PS: If my program is wrong, tell me where to edit it to copy a video or graphic file.

//PROG--VideoCopy

#include

int main()
{

FILE *fp,*sp;
char ch;

fp=fopen("D:\\shrek_trailer.mp4","r+b");
sp=fopen("D:\\trailer_copy.mp4","w+b");

if(fp==NULL)
{
printf("Couldn't open source file");
fclose(fp);
}

if(sp==NULL)
{
printf("Couldn't create target file");
fclose(sp);
}

while(1)
{
ch=fgetc(fp);
if(ch==EOF);
break;
fputc(ch,sp);
}

fclose(fp);
fclose(sp);
}
Four answers:
anonymous
2012-09-18 21:13:10 UTC
It doesn't matter what type of file it is, text/binary only affects how it's opened. If you open a file in binary mode, you will read the data exactly as appears in its external representation. On the other hand, if you open a file in text mode, you will read data that has been broken up into lines by the C implementation, with each line (or at least what the implementation considers a line) terminated by the '\n' character.



You have your mode string wrong. "r+b" means ``open a binary file for reading AND writing'' and "w+b" means ``create a binary file (or truncate to zero length if it exists) for reading AND writing''. You want "rb" and "wb", which will open two streams in reading and writing mode, respectively.



Your calls to fclose are incorrect in both of these branches:



- if(fp==NULL) ...

- if(sp==NULL) ...



Since a null pointer is passed to fclose in both cases. Furthermore, you don't exit the program in any of these branches, so you'll be passing null pointers to fgetc, fputc, and fclose as well. Here's what you could do instead:



- Make the first call to fopen.

- If it returns a null pointer, emit an error (preferably on stderr), then exit (preferably with the status EXIT_FAILURE).

- Make the second call to fopen.

- If it returns a null pointer, call fclose with the value returned by the first

call to fopen, emit an error, then exit.



The type used for 'ch' may not be large enough to store the value returned by fgetc (if char is unsigned or EOF is less than CHAR_MIN). If it's signed and EOF is within the range of char, you may still have problems reading data that gets the same value as EOF, due to the type conversion. In any case, you should store it in an int when checking for EOF.



Aside from those errors, there isn't much else wrong with it. It can be helpful to check the return value of fclose, particularly on writes, and emit a message if any errors happened to occur.
Jonathan
2012-09-18 15:30:03 UTC
Eddy is simply wrong. fgetc() works for text files AND binary files, just fine. In fact, if you look at the C standard (either 1999, or 2011, though in different numbered sections for each), you find the following on fread:



    "For each object, size calls are made to the fgetc function ..."



(size here means the size parameter sent to fread().) Similarly, for fwrite.



Eddy is simply wrong on that point.



Also, Eddy is wrong on another point. fgetc() does provide EOF values. Which brings up the problem in YOUR code.



You defined:



    char ch;



when you should have written:



    int ch;



Notice that fgetc() returns an "int" not a "char." Once you fix this problem, EOF tests will start to work. You can also use feof() and ferror() calls. But it probably isn't necessary in your case. (Also, fputc() wants an int, too, not a char.)



One thing you may need to watch out for, when opening files as binary, is that if the file is a text file AND if the file resides on Windows, THEN you may find that if you open it in binary mode you will actually get a ctrl-Z as a valid character from fgetc() just before the EOF is reached. This is due to a very old behavior that arrived due to the old CP/M operating system (which ONLY used explicit ctrl-Z characters to determine the end of file and NOT a file size value in bytes, which CP/M didn't have) and DOS (which added a file size down to the byte level to the directory entry, which could be used to indicate end of file instead of the ctrl-Z.)





EDIT: Here is an example of a command-line version of what you want to achieve. The first filename is for reading, the second filename for writing. A check is made to ensure that you aren't writing on top of an existing file, too. And at the end, another check is made so that a message reports an abnormal EOF, if it occurs. (The code also deals with that ctrl-Z I mentioned. This is only appropriate if you might try copying text files with the code, running under Windows or DOS. Remove that line if you will only be processing binary type files or working on some other operating system.) It should be pretty easy to read and understand.



#include

#include

#include

int main( int argc, char *argv[] ) {

    FILE *fin, *fout;

    int c;

        if ( argc < 3 ) return 0;

        if ( (fin= fopen( argv[1], "rb" )) == NULL ) {

            printf( "Unable to open '%s' for reading as a binary file.\n", argv[1] );

            return 0;

        }

        if ( (fout= fopen( argv[2], "rb" )) != NULL ) {

            fclose( fout );

            printf( "The file '%s' already exists.\n", argv[2] );

            return 0;

        }

        if ( (fout= fopen( argv[2], "wb" )) == NULL ) {

            printf( "Unable to open '%s' for writing as a binary file.\n", argv[2] );

            return 0;

        }

        while ( (c= fgetc( fin )) != EOF ) {

            if ( c == '\032' && feof( fin ) ) break;

            fputc( c, fout );

        }

        if ( !feof( fin ) && ferror( fin ) != 0 )

            printf( "Error occurred during reading.\n" );

        fclose( fin );

        fclose( fout );

    return 0;

}
anonymous
2012-09-18 13:18:56 UTC
Of course you use EOF for text file, that is not what I meant.



The binary(not text) file will likely return EOF (EOF is really -1 OxFF) and this is likely to appear in your mpeg file thousands of times if you read char by char. You CANNOT test for this return value for a nontext file.



I might have been wrong why you got nowhere in your attempt to copy. But you CANT test for EOF in a binary.



fgetc is for text files, don't use EOF either.



It is simply ridiculous to use fgetc for this.



fread will return 0 when you are done reading.



use fread and fwrite, they return the number of bytes read and written



http://www.cplusplus.com/reference/clibrary/cstdio/fread/

http://www.cplusplus.com/reference/clibrary/cstdio/fwrite/
anonymous
2016-09-17 01:33:08 UTC
I was curious on the answer to this too


This content was originally posted on Y! Answers, a Q&A website that shut down in 2021.
Loading...