Title: Introduction to C Lecture 9: File inputoutput and the C compiler
1Introduction to CLecture 9 File
input/outputand the C compiler
2Files
- Before you can read from and write to a file, you
need to carry out certain procedures, and you
need information about the file and its state.
For example, you need to open the file and you
need to know what sort of file it is and while
you are looking at it, you need to keep track of
whereabouts in the file you are. -
- All of this information is kept lumped together
in a structure. This structure has the generic
name FILE, and it is defined for you as part of
the information that you bring in with the header
stdio.h. -
- You do not need to know details of this
structure you simply assign a pointer to a file
structure, open the file (analogy a file is like
a closed book you cant do anything with it
until you open it), read from/write to it, and
then close it. - Example
3File i/o
- include ltstdio.hgt
-
- int main(void)
-
- int a, sum 0
- FILE ifp, ofp / input file pointer, output
file pointer / -
- ifp fopen(my_file, r) / open for
reading / - ofp fopen(outfile, w) / open for
writing / - .....
-
- fclose(ifp)
- fclose(ofp)
-
4File i/o
- include ltstdio.hgt
-
- int main(void)
-
- int a, sum 0
- FILE ifp, ofp / input file pointer, output
file pointer / -
- ifp fopen(my_file, r) / open for
reading / - ofp fopen(outfile, w) / open for
writing / - .....
-
- fclose(ifp)
- fclose(ofp)
-
Pointers to file structure
5File i/o
- include ltstdio.hgt
-
- int main(void)
-
- int a, sum 0
- FILE ifp, ofp / input file pointer, output
file pointer / -
- ifp fopen(my_file, r) / open for
reading / - ofp fopen(outfile, w) / open for
writing / - .....
-
- fclose(ifp)
- fclose(ofp)
-
The modes for opening files are r (read), w
(write), a (append), rb (read from binary file
not appropriate for Unix, which doesnt
distinguish between binary and text files), wb,
ab. You may also add a to give, for example,
r for both reading and writing. If the open
fails (e.g. file to be read doesnt exist) the
pointer returned is NULL. Open for writing causes
the file to be created if it doesnt exist, and
overwritten if it does.
6fprintf(), fscanf()
- For output to/input from files, you can use the
functions fprintf( ) and fscanf( ), which operate
in exactly the same manner as printf and scanf,
except that a file pointer has to be specified.
Usage -
- fprintf(file_ptr, control_string, other_args)
- fscanf(file_ptr, control_string, other_args)
-
7fprintf(), fscanf()
- For output to/input from files, you can use the
functions fprintf( ) and fscanf( ), which operate
in exactly the same manner as printf and scanf,
except that a file pointer has to be specified.
Usage -
- fprintf(file_ptr, control_string, other_args)
- fscanf(file_ptr, control_string, other_args)
-
control string things like s12.2f\n
8fprintf(), fscanf()
- For output to/input from files, you can use the
functions fprintf( ) and fscanf( ), which operate
in exactly the same manner as printf and scanf,
except that a file pointer has to be specified.
Usage -
- fprintf(file_ptr, control_string, other_args)
- fscanf(file_ptr, control_string, other_args)
-
- In fact, printf and scanf are special cases of
these there are three file pointers defined,
stdin, stdout and stderr, the first of which
points to the keyboard and the other two of which
point to the screen. printf( ) is therefore
equivalent to fprintf(stdout, ....). -
- Note that, when you read from/write to a file,
the system will keep track of where in the file
you have reached, so that subsequent calls to
fprintf( ) or fscanf( ) can carry on where you
left off rather than starting again from the
beginning.
9getc(), putc()
- Functions getc(file_ptr), putc(file_ptr) are just
like getchar and putchar, except that they read
from/write to files. However, you should note
that the EOF (end-of-file) character is not in
the usual Ascii character set, and you need an
int not a char if you are going to use it - int c
- ....
- c getchar(inpt_file)
-
10Moving around in files
- When reading or writing from a file, fprintf( )
etc. will normally begin where they left off
record is kept of the current position in a file.
However it is often useful to access a file at
some random position instead. Useful commands
are -
- rewind(file_ptr) resets the position indicator
to the start of the file - ftell(file_ptr) returns the
current value of the file position
indicator i.e. no. of bytes from start of
file - fseek(file_ptr, offset, place) moves file
position indicator to specified position -
- The latter command moves the indicator to offset
bytes from place place can be SEEK_SET, SEEK_CUR
or SEEK_END (actually 0, 1 or 2) for beginning,
current position or end of file respectively.
Offset can be negative as well as positive.
Example
11Example of moving within files
- / Write a file backwards /
- include ltstdio.hgt
- define MAXSTRING 100
- int main(void)
-
- char fnameMAXSTRING
- int c
- FILE ifp
- fprintf(stdout, "\nInput a filename ")
- scanf("s", fname)
- ifp fopen(fname, "r") / open for
reading / - fseek(ifp, 0, SEEK_END) / move to end of
file / - fseek(ifp, -1, SEEK_CUR) / back up one
char / - while (ftell(ifp) gt 0)
- c getc(ifp) / move ahead one
char / - putchar(c)
- fseek(ifp, -2, SEEK_CUR) / back up 2
chars / -
- return 0
12Example of moving within files
- / Write a file backwards /
- include ltstdio.hgt
- define MAXSTRING 100
- int main(void)
-
- char fnameMAXSTRING
- int c
- FILE ifp
- fprintf(stdout, "\nInput a filename ")
- scanf("s", fname)
- ifp fopen(fname, "r") / open for
reading / - fseek(ifp, 0, SEEK_END) / move to end of
file / - fseek(ifp, -1, SEEK_CUR) / back up one
char / - while (ftell(ifp) gt 0)
- c getc(ifp) / move ahead one
char / - putchar(c)
- fseek(ifp, -2, SEEK_CUR) / back up 2
chars / -
- return 0
Notice we have to back up 2 chars, because the
act of reading in one char increments the file
position indicator by 1.
13Executing system commands
- You can execute system commands by simple use of
system( ). Example - system(date)passes the string date to the
operating system, which executes it before
returning control to the program. - The following example, from KP, does a directory
listing in uppercase to the screen. To understand
it, you need to know a couple of things - The function tmpnam(NULL) gives a name that you
can use for a temporary file (one that doesnt
exist already) - If you do ls gt junk.dat(on Unix) you get a
directory listing written to the file junk.dat.
(Remember the redirection operator, gt ) - As you will see, this is rather a clumsy method
it asks the system to write the directory to a
temporary file, and then reads in from the
temporary file and writes out to the screen.
14Example
- include ltctype.hgt
- include ltstdio.hgt
- include ltstdlib.hgt
- int main(void)
-
- char command_string100, tmp_filename
- int c
- FILE ifp
- tmp_filename tmpnam(NULL)
sprintf(command_string, "ls gt s",
tmp_filename) - system(command_string) ifp
fopen(tmp_filename, "r")while ((c getc(ifp))
! EOF) - putchar(toupper(c))remove(tmp_filename)
- return 0
15Example
- include ltctype.hgt
- include ltstdio.hgt
- include ltstdlib.hgt
- int main(void)
-
- char command_string200, tmp_filename
- int c
- FILE ifp
- tmp_filename tmpnam(NULL)
sprintf(command_string, "ls gt s",
tmp_filename) - system(command_string) ifp
fopen(tmp_filename, "r")while ((c getc(ifp))
! EOF) - putchar(toupper(c))remove(tmp_filename)
- return 0
Number hard-coded in is not very nice but for
command-line, probably ok as long as you set a
limit unlikely to be exceeded!
16Example
- include ltctype.hgt
- include ltstdio.hgt
- include ltstdlib.hgt
- int main(void)
-
- char command_string200, tmp_filename
- int c
- FILE ifp
- tmp_filename tmpnam(NULL)
sprintf(command_string, "ls gt s",
tmp_filename) - system(command_string) ifp
fopen(tmp_filename, "r")while ((c getc(ifp))
! EOF) - putchar(toupper(c))remove(tmp_filename)
- return 0
Notice the character is of type int not char
because the end-of-file character isnt in the
ASCII set, so we need a larger range than 0-255
17Example
- include ltctype.hgt
- include ltstdio.hgt
- include ltstdlib.hgt
- int main(void)
-
- char command_string200, tmp_filename
- int c
- FILE ifp
- tmp_filename tmpnam(NULL)
sprintf(command_string, "ls gt s",
tmp_filename) - system(command_string) ifp
fopen(tmp_filename, "r")while ((c getc(ifp))
! EOF) - putchar(toupper(c))remove(tmp_filename)
- return 0
Here we make a temporary file temp.dat or
something.
18Example
- include ltctype.hgt
- include ltstdio.hgt
- include ltstdlib.hgt
- int main(void)
-
- char command_string200, tmp_filename
- int c
- FILE ifp
- tmp_filename tmpnam(NULL)
sprintf(command_string, "ls gt s",
tmp_filename) - system(command_string) ifp
fopen(tmp_filename, "r")while ((c getc(ifp))
! EOF) - putchar(toupper(c))remove(tmp_filename)
- return 0
ls gt s,tmp_filename becomes equivalent to
ls gt temp.dat (or whatever the file is
called)....
19Example
- include ltctype.hgt
- include ltstdio.hgt
- include ltstdlib.hgt
- int main(void)
-
- char command_string200, tmp_filename
- int c
- FILE ifp
- tmp_filename tmpnam(NULL)
sprintf(command_string, "ls gt s",
tmp_filename) - system(command_string) ifp
fopen(tmp_filename, "r")while ((c getc(ifp))
! EOF) - putchar(toupper(c))remove(tmp_filename)
- return 0
... which sprintf then writes IN TO the string
called command_string
20Example
- include ltctype.hgt
- include ltstdio.hgt
- include ltstdlib.hgt
- int main(void)
-
- char command_string200, tmp_filename
- int c
- FILE ifp
- tmp_filename tmpnam(NULL)
sprintf(command_string, "ls gt s",
tmp_filename) - system(command_string) ifp
fopen(tmp_filename, "r")while ((c getc(ifp))
! EOF) - putchar(toupper(c))remove(tmp_filename)
- return 0
The command string ls gt temp.dat (or whatever)
is now sent to the system
21Example
- include ltctype.hgt
- include ltstdio.hgt
- include ltstdlib.hgt
- int main(void)
-
- char command_string200, tmp_filename
- int c
- FILE ifp
- tmp_filename tmpnam(NULL)
sprintf(command_string, "ls gt s",
tmp_filename) - system(command_string) ifp
fopen(tmp_filename, "r")while ((c getc(ifp))
! EOF) - putchar(toupper(c))remove(tmp_filename)
- return 0
Now we open the temporary file for reading, so we
can read the directory information from it
22Example
- include ltctype.hgt
- include ltstdio.hgt
- include ltstdlib.hgt
- int main(void)
-
- char command_string200, tmp_filename
- int c
- FILE ifp
- tmp_filename tmpnam(NULL)
sprintf(command_string, "ls gt s",
tmp_filename) - system(command_string) ifp
fopen(tmp_filename, "r")while ((c getc(ifp))
! EOF) - putchar(toupper(c))remove(tmp_filename)
- return 0
read from file one character at a time, convert
to uppercase and put out to the screen
23Example
- include ltctype.hgt
- include ltstdio.hgt
- include ltstdlib.hgt
- int main(void)
-
- char command_string200, tmp_filename
- int c
- FILE ifp
- tmp_filename tmpnam(NULL)
sprintf(command_string, "ls gt s",
tmp_filename) - system(command_string) ifp
fopen(tmp_filename, "r")while ((c getc(ifp))
! EOF) - putchar(toupper(c))remove(tmp_filename)
- return 0
delete the temporary file
24Pipes
- In Unix, you can use pipes to communciate
directly with the operating system this avoids
the need for the temporary file in the previous
example. This example, uppercase2.c, is also from
KP. - include ltctype.hgt
- include ltstdio.hgt
- int main(void)
-
- int c
- FILE ifp
-
- ifp popen("ls", "r") / open pipe /
- while ((c getc(ifp)) ! EOF)
- putchar(toupper(c))
- pclose(ifp)
- return 0
-
25Pipes
- In Unix, you can use pipes to communciate
directly with the operating system this avoids
the need for the temporary file in the previous
example. This example, uppercase2.c, is also from
KP. - include ltctype.hgt
- include ltstdio.hgt
- int main(void)
-
- int c
- FILE ifp
-
- ifp popen("ls", "r") / open pipe /
- while ((c getc(ifp)) ! EOF)
- putchar(toupper(c))
- pclose(ifp)
- return 0
-
Open a pipe to the operating system...
26Pipes
- In Unix, you can use pipes to communciate
directly with the operating system this avoids
the need for the temporary file in the previous
example. This example, uppercase2.c, is also from
KP. - include ltctype.hgt
- include ltstdio.hgt
- int main(void)
-
- int c
- FILE ifp
-
- ifp popen("ls", "r") / open pipe /
- while ((c getc(ifp)) ! EOF)
- putchar(toupper(c))
- pclose(ifp)
- return 0
-
... only for reading ...
27Pipes
- In Unix, you can use pipes to communciate
directly with the operating system this avoids
the need for the temporary file in the previous
example. This example, uppercase2.c, is also from
KP. - include ltctype.hgt
- include ltstdio.hgt
- int main(void)
-
- int c
- FILE ifp
-
- ifp popen("ls", "r") / open pipe /
- while ((c getc(ifp)) ! EOF)
- putchar(toupper(c))
- pclose(ifp)
- return 0
-
... and ask it to send a directory listing
28Pipes
- In Unix, you can use pipes to communciate
directly with the operating system this avoids
the need for the temporary file in the previous
example. This example, uppercase2.c, is also from
KP. - include ltctype.hgt
- include ltstdio.hgt
- int main(void)
-
- int c
- FILE ifp
-
- ifp popen("ls", "r") / open pipe /
- while ((c getc(ifp)) ! EOF)
- putchar(toupper(c))
- pclose(ifp)
- return 0
-
read from the pipe one character at a time,
convert to uppercase and put out to the screen
29Pipes
- In Unix, you can use pipes to communciate
directly with the operating system this avoids
the need for the temporary file in the previous
example. This example, uppercase2.c, is also from
KP. - include ltctype.hgt
- include ltstdio.hgt
- int main(void)
-
- int c
- FILE ifp
-
- ifp popen("ls", "r") / open pipe /
- while ((c getc(ifp)) ! EOF)
- putchar(toupper(c))
- pclose(ifp)
- return 0
-
... and close the pipe
30Arguments to main( )
- Two arguments, conventionally called argc and
argv, can be used with main() to communicate with
the operating system. argc counts the arguments
(including the program name itself), and argv
is an array of strings corresponding to the
arguments. Filenames are often passed in this
way. Example echoinput.c - / Echoing command line arguments... from
KP/include ltstdio.hgtint main(int argc, char
argv) int i printf("argc d\n",
argc) for (i 0 i lt argc i)
printf("argvd s\n", i, argvi) return
0
31Arguments to main( )
- Two arguments, conventionally called argc and
argv, can be used with main() to communicate with
the operating system. argc counts the arguments
(including the program name itself), and argv
is an array of strings corresponding to the
arguments. Filenames are often passed in this
way. Example echoinput.c - / Echoing command line arguments... from
KP/include ltstdio.hgtint main(int argc, char
argv) int i printf("argc d\n",
argc) for (i 0 i lt argc i)
printf("argvd s\n", i, argvi) return
0
argc is the number of arguments
32Arguments to main( )
- Two arguments, conventionally called argc and
argv, can be used with main() to communicate with
the operating system. argc counts the arguments
(including the program name itself), and argv
is an array of strings corresponding to the
arguments. Filenames are often passed in this
way. Example echoinput.c - / Echoing command line arguments... from
KP/include ltstdio.hgtint main(int argc, char
argv) int i printf("argc d\n",
argc) for (i 0 i lt argc i)
printf("argvd s\n", i, argvi) return
0
argc is the number of arguments
argv are the arguments themselves e.g. if you
run program as echoinput file1 file2 then argc
will be 3 argv0 will be echoinput argv1
will be file1 argv2 will be file2
33The C compiler
- You are used to
- cc filename.cto compile your programs.
This uses the systems own C compiler. However,
there are other compilers around one good one is
gcc, the Gnu C compiler, provided by the Free
Software Foundation. -
- Options include
- -o output file name, which you are used to
- -lm to include the maths library, which you
normally do by default. - -E to see output from preprocessor only i.e.
dont compile. - The compiler works in three stages it
preprocesses (replacing the include, define
etc) it compiles and then it links all of the
pieces of code together into the executable
program. - For a large program you often want to compile a
small piece at a time, and link it to larger,
pre-compiled parts. You can avoid the link, i.e.
compile only, with - cc -c filename.cThis creates object files
(whose names end in .o), which can then be linked
as desired.
34C Compiler Libraries
- You can also create libraries, which are
(generally large) collections of useful object
code ready for linking, so you dont have to
write the same bits of code over and over again
for different programs that might use them. To
do this with your set of .o files, - ar ruv g_lib.a gfopen.o gfclose.o
gcalloc.o....... - This makes a library file called g_lib.a.
35C Compiler Libraries
- You can also create libraries, which are
(generally large) collections of useful object
code ready for linking, so you dont have to
write the same bits of code over and over again
for different programs that might use them. To
do this with your set of .o files, - ar ruv g_lib.a gfopen.o gfclose.o
gcalloc.o....... - This makes a library file called g_lib.a.
ar means archive
36C Compiler Libraries
- You can also create libraries, which are
(generally large) collections of useful object
code ready for linking, so you dont have to
write the same bits of code over and over again
for different programs that might use them. To
do this with your set of .o files, - ar ruv g_lib.a gfopen.o gfclose.o
gcalloc.o....... - This makes a library file called g_lib.a.
ruv means replace, update, verbose
37C Compiler Libraries
- You can also create libraries, which are
(generally large) collections of useful object
code ready for linking, so you dont have to
write the same bits of code over and over again
for different programs that might use them. To
do this with your set of .o files, - ar ruv g_lib.a gfopen.o gfclose.o
gcalloc.o....... - This makes a library file called g_lib.a.
g_lib.a is the output archive file name
38C Compiler Libraries
- You can also create libraries, which are
(generally large) collections of useful object
code ready for linking, so you dont have to
write the same bits of code over and over again
for different programs that might use them. To
do this with your set of .o files, - ar ruv g_lib.a gfopen.o gfclose.o
gcalloc.o....... - This makes a library file called g_lib.a.
your object files to go in the library
39C Compiler Libraries
- You can also create libraries, which are
(generally large) collections of useful object
code ready for linking, so you dont have to
write the same bits of code over and over again
for different programs that might use them. To
do this with your set of .o files, - ar ruv g_lib.a gfopen.o gfclose.o
gcalloc.o....... - This makes a library file called g_lib.a. Follow
this by - ranlib g_lib.a
- which organises the file in a form that is useful
for the linker. - To make the functions available to other
programs, just compile as usual cc -o pgm
main.c file1.c g_lib.a -
40C Compiler Libraries
- You can also create libraries, which are
(generally large) collections of useful object
code ready for linking, so you dont have to
write the same bits of code over and over again
for different programs that might use them. To
do this with your set of .o files, - ar ruv g_lib.a gfopen.o gfclose.o
gcalloc.o....... - This makes a library file called g_lib.a. Follow
this by - ranlib g_lib.a
- which organises the file in a form that is useful
for the linker. - To make the functions available to other
programs, just compile as usual cc -o pgm
main.c file1.c g_lib.a -
g_lib.a is the output archive file that youve
just made.
41C Compiler Profiler
- The option -p activates the profiler when the
compiled program is run, a file called mon.out is
created, containing details of how much time was
spent in each function, and how many times each
function was called. You then use gprof prog.
name to write these details onto the screen cc
-p -o quicksort main.c quicksort.c - quicksort
- gprof quicksort
- Some of the functions that appear will be system
calls.
42Preprocessor
- The preprocessor is very useful and powerful. A
simple exampledefine DEBUG 1 /set debug to
true at top of file/......if
DEBUG printf(debug a d\n,
a)endifBecause DEBUG has value 1, the printf
statements will be compiled. When weve finished
our debug procedure, we can turn off all such
statements by setting DEBUG 0 at the start.
You can deactivate and reactivate other chunks of
code in the same way. - An alternative is ifdef DEBUG ....endifwhich
will include the lines if DEBUG (or whatever) is
defined at all at the top. There is also an
ifndef, which includes lines if (whatever) is
not defined. There is also an undef function to
remove previous definitions, in order to prevent
clashes. - To match the if, there is an else there is
also elif, which is short for else if.
43Preprocessor
- Two more examples of the use of the
preprocessordefine SQ(x) ((x) (x))will
replace, e.g., d/SQ(a b) by d/((a b) (a
b)) . Note here that all of the parentheses are
needed! - Anddefine min(x, y) (((x) lt (y)) ? (x) (y))
will replace an expression such asm min(u,
v)bym (((u) lt (v)) ? (u) (v))The arguments
of m can be arbitrary expressions of comparable
type. -
- Be warned, though debugging code that contains
macros with arguments can be very difficult!
44Writing large programs
- A large program will normally be stored as a
collection of .h and .C files in its own
directory. If we are developing a program called
pgm, we make a header file called pgm.h, and put
it in a directory called pgm with all of the .C
files. The .C files should then have - include pgm.h
- at the top. pgm.h will contain includes,
defines and a list of function prototypes. - Note that, when you make your own files to be
included, the name goes in quotes rather than in
lt gt. The compiler then knows that it should look
in the current directory rather than the place
that it expects to find the standard C header
files. - There should also be a README file with text
information about the program. - You then compile with the usual command, listing
all of the .C files (but not the .h files, which
are included automatically by the include
preprocessor commands). - However...
45Writing large programs, contd
- Header files can create a problem. You often
want to include the same header files in several
different sourcecode files. When you compile
them together, though, it complains because you
have multiple definitions of the same functions
etc. How do we get around this? - Let us assume we have a header file called
MyHeaderFile1.h. You use the preprocessor
directives define, ifndef if not defined as
follows - ifndef MYHEADERFILE1define MYHEADERFILE1....
everything you want goes here function
prototypes, definitions and so on.... and at the
end of the fileendif - Thus, the material in the file is only included
if it hasnt been included already.
46Next Lecture
- Next weeks session will be essentially all
exercise class rather than lecture. However, as
an inducement to come, I plan to show you how to
include some simple graphics...