copy
2009-02-05
Introduction
This program copies a file. It shows how to open files and ensure that they are closed afterwards, and it shows haw to do binary I/O.
Ada
with Ada.Streams.Stream_IO;
procedure Copy is
procedure Copy(From_File : String;
To_File : String) is
use Ada.Streams.Stream_IO;
package Streams renames Ada.Streams;
use type Streams.Stream_Element_Offset;
In_Stream : File_Type;
Out_Stream : File_Type;
Buffer : Streams.Stream_Element_Array(1 .. 4096);
Bytes_Read : Streams.Stream_Element_Offset;
begin
Open(In_Stream, In_File, From_File);
Create(Out_Stream, Out_File, To_File);
loop
Read(In_Stream, Buffer, Bytes_Read);
exit when Bytes_Read = 0;
Write(Out_Stream, Buffer, Count(Bytes_Read));
end loop;
Close(In_Stream);
Close(Out_Stream);
exception
when others =>
if(Is_Open(In_Stream)) then Close(In_Stream); end if;
if(Is_Open(Out_Stream)) then Close(Out_Stream); end if;
raise;
end Copy;
begin
Copy("copy.adb", "cp.adb");
end Copy;
The program contains 81 words.
C
The C version of the program is displayed below.
#include <stdio.h>
int copy(const char *source, const char *dest) {
FILE *src, *dst;
char buf[128];
int n;
src = fopen(source, "rb");
if(!src) return -1;
dst = fopen(dest, "wb");
if(!dst) {
fclose(src);
return -1;
}
while(!feof(src)) {
n = fread(buf, 1, sizeof(buf), src);
if(ferror(src)) goto error;
if(n > 0) {
if(fwrite(buf, 1, n, dst) != n) goto error;
}
}
fclose(dst);
fclose(src);
return 0;
error:
fclose(dst);
fclose(src);
return -1;
}
int main(int argc, char **argv) {
return copy("copy.c", "cp.c");
}
There are several interesting bits to note about this
program. First, the note of const
in the definition of
copy
, signifying that the function won't modify these
arguments. Secondly, the way fopen
is used: the first
argument is the filename, the second the mode: r
for
reading, w
for writing, and b
for binary
mode.
After every file operation, a check is made to see if an error
occured. If so, any open files are closed, and -1
is
returned. Since there are several points where both files would have to
be closed, this code is put at the end of the function and branched to
using goto
, if necessary.
The program contains 79 words.
Common Lisp
An implementation in Common Lisp is shown below.
(defun copy (source dest)
(with-open-file (src source)
(with-open-file (dst dest :direction :output)
(loop with buffer = (make-array '(4096))
with n = 0
do (setf n (read-sequence buffer src))
(if (= n 0) (return))
(write-sequence buffer dst :end n)))))
(copy "copy.lisp" "cp.lisp")
This program makes use of the
with-open-file
constructs, which opens a file and makes
sure that it is closed when control leaves its body, whether through
successful completion or because a condition is signalled.
The buffer for copying the data is created using
make-array
which is passed a list of sizes, one for every
dimension of the array. In this case, the array has only one dimension,
so there is only one value in the list.
This program also demonstrates the use of keyword arguments:
:direction
for the second with-open-file
and
:end
in the call to write-sequence
. Note
that the value given for :direction
is itself a
keyword.
There are 41 words in the program.
OCaml
The program can be implemented in OCaml as follows:
let copy source dest =
let src = open_in_bin source in
try let dst = open_out_bin dest in
let len = 4096 in
let buf = String.create len in
let more = ref true in
try while !more do
let n = input src buf 0 len in
if n = 0 then more := false
else output dst buf 0 n
done with err ->
close_out_noerr dst;
raise err;
close_out dst
with err ->
close_in_noerr src;
raise err;
close_in src
let _ = copy "copy.ml" "cp.ml"
The most obvious thing about this program is the use of exceptions: the construct
try expr with exn -> handler
evaluates expr
and returns its result,
unless an exception occurs: in that case, it matches the exception
against the with
clauses (there can be more than one). In
the program above, both try
blocks have a single
with
clause that matches any exception, closes the opened
file, and raises the caught exception again.
Another thing to notice is the use of references. let more =
ref true …
makes more
a reference to
the boolean true
. The reason to use references is that
normal bindings are constants in OCaml. In this case, we want to set
more
to false
when the copying is done, so we
need something that can be modified. That's what references are for.
more := false
makes more
refer to
false
. !more
returns the value
more
currently refers to.
The program contains 87 words.
Ruby
For Ruby, we have the following program:
def copy source, dest
File.open(source, 'rb') do |src|
File.open(dest, 'wb') do |dst|
data = src.read 4096
break unless data
dst.print data
end
end
end
copy 'copy.rb', 'cp.rb'
Again, we see the use of blocks. Calling
File.open
with a block opens the file and calls the block
with the file handle as an argument. It ensures that the file is closed
when the block exits (whether normally or through an exception). The
'rb'
and 'wb'
arguments are just like in
C. src.read 4096
reads up to 4096 bytes from
src
, returning nil
when the end has been
reached.
This version contains 27 words.