Files and more using printf, fscanf and fprintf

printf("%[flags][width].[precision][size][type]", variable);
printf format specifiers
FlagsWidthPrecisionSize Typeexample
-+0#Leading zeroes and justification
-Left align the result within the max width.
+Sign the output, add + for positive, - for negative.
0Pad floating point numbers with zeroes to max width.
#Type o, x or X: prefix with '0','0x' or 'X'.
Type e,E or F - force output to contain the decimal point.
Type g or G - force decimal point and do not remove trailing zeroes.
 2minimum characters to fill
 .3max characters to fill
 lhmodify size of the type
 lType d,i,o,x or X - long int
 hType d,i,o,x or X - short int
 lType u - long unsigned int
 hType u - short unsigned int
 cCdiouxXeEfgGpsSdata type
 csingle character
 Csingle multi-byte character
 dsigned decimal integer
 iequivalent to d
 ounsigned octal integer
 uunsigned decimal integer
 xlower case unsigned hexadecimal integer
 Xupper case unsigned hexadecimal integer
 esigned floating point exponential
 Eas e with upper case E in exponent
 fsigned floating point double
 ge or f, whichever is most compact
 GE of F, whichever is most compact
 phexadecimal segmented memory address
 snull terminated string
 Sas S with multi-byte characters.

Simple file I/O using the standard library
Formatting ASCII output to the filesystem is simply an extension of output to the screen. Binary file interaction is more complex, but reading and writing simple text files is a matter of opening a file and printing the formatted output.

#include <stdio.h>
int main() {
   char quote[] = "GNU";
   FILE* textfile = NULL;
// use fopen() to create an empty file
   textfile = fopen("linux.txt","w");
   if(!textfile) { return -1; }
   fprintf(textfile, "%s is not Unix",quote);
   return 0;
  1. char quote[]; creates an array of characters that can be used as a string. quote[0] = "G"; - each character in the array/string can be accessed individually in the normal manner of an array.
  2. textfile is the first encounter with pointers. Be very careful with pointers - pointers are one of the easiest ways to create security vulnerabilities in your code! This line defines textfile as a pointer of type FILE - textfile points to a space in memory that is large enough to accomodate this special data type. textfile is initially defined as NULL (a special version of zero), simply to allow the compiler to allocate enough memory for the FILE type that holds details of how to access the file.
  3. Now that textfile is pointing to sufficient memory, the linux.txt file can be opened. If it does not already exist, the "w" switch will create it. If it does exist, "w" will erase all content upon opening.
  4. Another first - very basic error checking. If the file could not be opened for any reason, the FILE data type will be empty. This will mean that textfile points to the same blank memory that it was assigned in the second line. The ! means if_not_exist, so the if conditional is true only when textfile points to an empty FILE structure in memory. If true, execution moves to the right side of the statement - return -1; which will cause the program to exit with an error code of -1.
  5. Always close your files - it may be true that compilers will attempt to close the files on your behalf but you cannot rely on the data being intact next time you want the file. Close the file as soon as you have finished with file operations on that specific file.
  6. The final act is to return a successful result. Note that main() was defined to return an int value. This allows the program to return a failure result of -1 but it means that you must return a value on success as well.

Reading a file
Using fread() in place of fwrite() and a change from "w" mode to "r" for read, the standard library provides access to existing files. It is inefficient to read the file only one byte at a time, so this example uses a block (blocked I/O) and stops when the file no longer contains sufficient data to fill the block. It is vital that the buffer is created large enough for this block.

#include <stdio.h>
#define BUFFER_LENGTH 1024
int main() {
   char contents[BUFFERLENGTH + 1];
   int readcount;
   FILE* inputfile = NULL;
   inputfile = fopen("linux.txt","r");
   if(!inputfile) return -1;
   do {
      readcount = fread(contents, 1, BUFFERLENGTH, inputfile);
      printf("%s", contents);
   } while (readcount == BUFFERLENGTH);
   return 0;
  1. As well as including headers, the # syntax (preprocessor commands) also allows the definition of static values. This is very common in header files when used to hold critical details like the ID numbers of error messages, hardware identifiers and other fixed values. The text string (or macro), by convention, is always upper case and the text will be replaced by the value in the first stage of compiling the program. Notice also that C is capable of arithmetic within assignments - contents if defined as of size 1024 + 1, once the macro is expanded.
  2. Note: readcount is not assigned a value when created. By using do{}, readcount is always assigned a value before reaching the conditional while - otherwise the result would be unpredictable.
  3. fread() starts at byte 1 and puts the data into the contents[] buffer returning the number of bytes read successfully into readcount. When reading blocked I/O, the data in the buffer is split by our program, independently of the data being read. When you copy a set number of letters from a book, you cannot expect to read the complete sentence, or even complete words. As a result, the contents[] buffer must be terminated properly by adding the special null value '\0' to the end. Strings and arrays start at zero, so if a string contains 6 letters, the last letter is in the fifth position string[5]: 0,1,2,3,4,5 is 6 digits long. The end of the data from the file is in the last position, if the buffer was full this would be: contents[1023]. With readcount starting at 1, readcount therefore contains 1024. contents[], defined as length 1025, has one remaining position: contents[1024], ready for the terminating null. By using the value from readcount instead of a fixed value, we can be sure to only read valid data from the file, not reading duff memory space from beyond the real data in contents[] when the buffer is not full.

This is part of www.codehelp.co.uk Copyright © 1998-2004 Neil Williams
See the file about.html for copying conditions.