|
|
|
Let's now compare programs in C, C++, and Java for copying one file into another file. The input file may be a text file or a binary file. We will first show the C implementation. We will assume that the executable for this program would be invoked with a command line like
copy sourceFile destFile
where copy is the
/* FileCopy.c */ #include <stdio.h> #include <stdlib.h> main(int argc, char* argv[]) /* (A) */ { FILE *in, *out; /* (B) */ int ch; if ( argc != 3 ) { /* (C) */ fprintf( stderr, "usage: copy in_file out_file\n" ); exit( EXIT_FAILURE ); } if ( ( in = fopen(argv[1], "rb" ) ) == NULL ) { /* (D) */ fprintf( stderr, "Can't open %s\n", argv[1] ); exit( EXIT_FAILURE ); } if ( ( out = fopen(argv[2], "wb" ) ) == NULL ) { /* (E) */ fprintf( stderr, "Can't
open
%s\n", argv[2] ); fclose( in ); exit( EXIT_FAILURE ); } while ( ( ch = getc( in ) ) != EOF ) /* (F) */ if ( putc( ch, out ) == EOF ) /* (G) */ break; if ( ferror( in ) ) /* (H) */ printf( "Error while reading source file.\n" ); if (
ferror
( out ) ) /* (I) */ printf( "Error while writing into dest file.\n" ); fclose( in ); /* (J) */ fclose( out ); /* (K) */ return 0; }
With the command line for invoking the executable of this program as shown previously, the parameter argc in line (A) will be set to 3 and the parameter argv will denote an array of character pointers, each pointer pointing to one of the strings in the command line. So the value stored in argv[0] will point to the string copy, the value stored in argv[1] to the string sourceFile, and the value stored in argv[2] to the string destFile .
In line (B), we declare two file pointers to serve as two I/O streams, each of type FILE*, a type declared in the header file
stdio.h.
We will use one of these,
in,
for reading the
Line (C)
In line (D), we open the source file in the read binary mode. [15] The function fopen() returns a file pointer that becomes the value of in. In this line, we also ensure that the file is opened successfully by checking the value of the file pointer returned. If the file was not successfully opened, a message is sent to the standard error stream stderr . The block of code that begins in line (E) does the same for the destination file.
The actual job of copying from the source file to the destination file is done by the statements in lines (F) and (G). The getc() function gets one character at a time from the input stream, and the putc() function deposits the character into the output stream. If getc() returns EOF, it could be either because the end of file was reached, or because an error occurred during the process of reading the input stream. [16] Note also that we read what's returned by getc() into an int, since ch is of that type, and not into a char. This is to enable the detection of the EOF condition.
To make sure that if either the
getc()
or the
putc()
function returns
EOF,
it is not because of an error condition
Shown below is a C++ program for doing the same thing—copying one file into another:
//FileCopy.cc #include <fstream> //(A) #include <cstdlib> using namespace std; //(B) void print_error(const char*, const char* = " "); //(C) int main(int argc, char* argv[]) //(D) { if (3 != argc) print_error("usage: copy source dest");
ifstream
in( argv[1], ios::binary ); //(E) if (!in) print_error( "can't open", argv[1] );
ofstream
out( argv[2], ios::binary ); //(F) if (!out) print_error( "can't open", argv[2] ); char ch; //(G) while ( in.get(ch) ) //(H) out.put( ch ); //(I) if ( !in.eof() ) //(J) print_error("something
strange
happened
"); return 0; } void print_error(const char* p, const char* p2) { //(K) cerr << p << ' ' << p2 << '\n'; //(L) exit(1); //(M) }
In line (A), we include the header file fstream. [17] This file includes the classes ifstream and ofstream that are needed for setting up the input and the output streams. The using directive in line (B) takes care of the fact that the identifiers used in the header fstream are defined in the namespace std. [18]
Line (C) declares the function prototype for the print_error function. This function has a default argument for its second parameter, the default argument being the empty string. [19]
The header of main() in line (D) is the same as the header of the C program shown before. Then, after we make sure that the program is called with the correct number of command line arguments, in line (E) we create an input stream by making an object named in of class ifstream. The file name associated with this object is the string pointed to by the pointer argv[1]. The object in will evaluate to false if the file cannot be opened for some reason. Similarly, in line (F) we construct an output stream object named out for the destination file in a manner similar to what was done for the input stream object. [20]
The actual file-to-file copy takes place in lines (H) and (I). In line (G), the function
get()
returns the
Line (J) checks the state of the input stream object. The function eof() will return true if the stream to which it is applied has encountered the end of file. So, if the flow of control has reached line (J) without the input stream having reached the end of the file, !eof() will return true and the error message in line (K) will be printed out on the standard terminal.
That
The reader will note that, unlike in the C program, we did not explicitly close the streams. In C++, as the streams go out of scope, their destructors are invoked; the destructors automatically close the streams. However, there do arise situations when we may need to explicitly close a stream. This can be done by invoking the function close() on the stream. For example, if we wanted to close the in input stream, we would say
in.close();
We will now show a Java program that does the same thing:
//FileCopy.java import java.io.*; //(A) class FileCopy { //(B) public static void main( String[] args ) //(C) { int ch = 0; FileInputStream in = null; //(D) FileOutputStream out = null; //(E) if ( args.length != 2 ) { //(F) System.err.println( "usage: java FileCopy source dest" ); System.exit( 0 ); } try { in = new FileInputStream( args[0] ); //(G) out = new FileOutputStream( args[1] ); //(H) while ( true ) { ch = in.read(); //(I) if (ch == -1) break; out.write(ch); //(J) } out.close(); //(K) in.close(); //(L) } catch (IOException e) { System.out.println( "IO error" ); } } }
In line (A), we import the package java.io because it contains the classes we need to create the input and output streams. Since a function in Java can reside only inside classes, in line (B) we define a class FileCopy inside which we will write the file copying function. The syntax in line (C) is the same as for the main() function in Java shown before, except that now the parameter args will actually be bound to an array of Strings corresponding to our command line arguments. We will use the following command line invocation of this program:
java fileIODemo sourceFile destinationFile
With this command line invocation, args[0] will be bound to the String sourceFile and args[1] to destFile .
With the declarations in lines (D) and (E),
in
can be used as an input stream and out as an output stream. The input and the output streams are bound to the respective files in lines (G) and (H). The function
read()
associated with the class
FileInputStream
reads one byte at a time in line (I). And the function
write()
In the program shown, setting up of the input and output streams and reading the input stream and writing to the output stream have all been carried out inside a
try—catch
block. This is necessitated by the fact that these Java
[15] The undesirable consequences of reading a binary file in character mode are discussed in Section 6.8.3.
[16] To refresh the memory of the reader, EOF, a macro defined in the header file <stdio.h>, is a negative integer constant, usually—1.
[17] The fstream header file includes the iostream header file. See Chapter 6 for the C++ iostream library.
[18] Namespaces are discussed in Chapter 3.
[19] The default arguments feature of C++ will be discussed in Chapter 9. Suffice it here to say that we have the choice of calling the function print_error() with only one argument. That argument will become the value of the first parameter. The second parameter in such a case will be set to an empty string.
[20] The classes ifstream and ofstream are subclasses of the class iostream. They inherit all the public attributes of the parent class iostream and in addition have their own specialized attributes for file I/O. See Chapter 6 for a treatment of the C++ iostream library.
[21] An alternative would be to use the following invocation in line (G):
int ch = in.get();
since at the end of the file the function
get()
returns
EOF
that is defined in the header
file iostream
to be—1. Chapter 6
[22]
The execution of a C++ program can also be
[23]
As we will explain in Chapter 10, this requirement applies
|
|
|

Database Management Systems

Computer Organization and Design, Fourth Edition: The Hardware/Software Interface (The Morgan Kaufmann Series in Computer Architecture and Design)

Effective Java (2nd Edition)

Scripting with Objects: A Comparative Presentation of Object-Oriented Scripting with Perl and Python