Text Format for Numeric Variables

Many programs, of course, use numeric as well as character data. When you wish to save numbers, you have two choices: text mode or binary mode. The SVTEXT.C program below illustrates the less desirable way of creating files for numeric variables.

/* SVTEXT.C: Save integer variables as text. */

#include <stdio.h>

#include <io.h>

int list[] = { 53, -23456, 50, 500, 5000, -99 };

extern int errno;

char fname[] = "numtext";

char temp[81];

main()

{

FILE *fptr;

int i;

if( (fptr = fopen( "numtext","wt" )) != NULL )

{

for( i=0; i<6; i++ )

fprintf( fptr, "Item %d: %6d \n", i, list[i] );

fclose( fptr );

}

else

printf( "Error: Couldn't create file.\n" );

if( (fptr = fopen( "badname", "rt" )) != NULL )

{

/* do nothing */

}

else

{

printf( "Error number: %d\n\t", errno );

perror( "Couldn't open file BADNAME\n\t" );

}

if( (fptr = fopen( fname, "rt" )) != NULL )

{

list[0] = 0;

fscanf( fptr, "Item %d: %d \n", &i, &list[0] );

printf( "Values read from file:\t %d %d\n", i, list[0] );

fgets( temp, 80, fptr );

printf( "String from file: \t%s\n", temp );

while( (i = fgetc( fptr )) != '\n' )

printf( "char: %c \t ASCII: %d \n", i, i );

rewind( fptr );

printf( "Rewind to start -->\t%s", fgets( temp, 80, fptr ) );

fclose( fptr );

}

else printf( "Trouble opening %s \n", fname );

}

The SVTEXT.C program does three things:

1.First, it creates a text file called NUMTEXT. If you display NUMTEXT from the DOS prompt or load the NUMTEXT file into a word processor, it looks like this:

Item 0: 53

Item 1: -23456

Item 2: 50

Item 3: 500

Item 4: 5000

Item 5: -99

2.Next, SVTEXT.C deliberately attempts to open a nonexistent file called BADNAME, to cause a disk error. This section serves no purpose except to illustrate error handling.

3.Finally, it reads parts of NUMTEXT, using several file-input functions.

Opening the File for Writing

By now, the fopen function should look familiar to you. The only change in the block below is the "wt" mode. The fopen function returns a NULL if any errors occur, so the block after the if should execute if fopen succeeds.

if( (fptr = fopen( "numtext","wt" )) != NULL )

{

for( i=0; i<6; i++ )

fprintf( fptr, "Item %d: %6d \n", i, list[i] );

fclose( fptr );

}

else

printf( "Error: Couldn't create file.\n" );

The for loop counts from 0 to 5, printing 6 strings to the file. The fprintf function works the same as printf with one change. You must place the FILE pointer before the format string.

Error Handling

To illustrate what happens when something goes wrong, the next line creates a disk error (as long as you don't have a file called BADNAME in your working directory).

if( (fptr = fopen( "badname", "rt" )) != NULL )

The if block is empty, because we expect the program to drop through to the else clause that handles errors:

else

{

printf( "Error number: %d\n\t", errno );

perror( "Couldn't open file BADNAME\n\t" );

}

The else block shows two ways you can deal with errors. Note that the errno variable, which was declared as an external integer, has never been assigned a value. QuickC automatically puts error numbers into errno. In this program, the error number is printed to the screen. In your own programs, you might wish to branch to various error-handling routines, based on the value in the system variable errno. For a list of values for errno, see the individual online help entries for file-handling functions.

It's important to remember that the standard output device is the screen and that printf sends messages to stdout. However, if you redirect output to a disk file, using a command line such as SVTEXT > MYFILE, the printf statement prints the error message to MYFILE. In most cases, you'd prefer to see the error message on the screen.

The second, and better, way to handle I/O errors is the perror function, which prints two strings: one that you pass to it and one that spells out—in English—the error message. This message goes to the standard error stream (stderr), which is always the screen, regardless of whether you've redirected output or not. For this reason, perror is preferable to printf for printing error messages.

The error messages should look like this on your screen:

Error number: 2

Couldn't open file BADNAME

: No such file or directory

Reading Text with fscanf

The final fopen in SVTEXT.C opens the file created earlier:

if( (fptr = fopen( fname, "rt" )) != NULL )

Note that we passed the name of a string rather than a literal string.

Below, fscanf reads in two numeric variables from the first string in the file. Note that it works the same as scanf, but you add the FILE pointer as the first argument:

fscanf( fptr, "Item %d: %d \n", &i, &list[0] );

printf( "Values read from file:\t %d %d\n", i, list[0] );

Reading Text with fgets and fgetc

At this point, the first line in the file has been read and converted to two integer values. The file is straight text, so you can treat the second line as a string:

fgets( temp, 80, fptr );

printf( "String from file: \t%s\n", temp );

The fgets function requires three arguments: a pointer to a string, the maximum number of characters to read, and the FILE pointer. The function stops reading characters when it encounters a newline character or when it reaches the maximum number of characters or the end of the file.

If you prefer, you can input the characters one by one:

while( (i = fgetc( fptr )) != '\n' )

printf( "char: %c \t ASCII: %d \n", i, i );

The printf inside the while loop prints each character as a character (%c) and also as a decimal value (%d). The while loop continues reading characters until it finds the end of the line.

Back to the Beginning

The rewind function resets the position pointer to the beginning of the file. In the program line below, the first line from the file is printed:

rewind( fptr );

printf( "Rewind to start -->\t%s", fgets( temp, 80, fptr ) );

The screen output looks like this:

Error number: 2

Couldn't open file BADNAME

: No such file or directory

Values read from file: 0 53

String from file: Item 1: -23456

char: I ASCII: 73

char: t ASCII: 116

char: e ASCII: 101

char: m ASCII: 109

char: ASCII: 32

char: 2 ASCII: 50

char: : ASCII: 58

char: ASCII: 32

char: ASCII: 32

char: ASCII: 32

char: ASCII: 32

char: ASCII: 32

char: 5 ASCII: 53

char: 0 ASCII: 48

char: ASCII: 32

Rewind to start --> Item 0: 53

Summary: It is inefficient to store numeric data in text format.

There seem to be quite a few white-space characters in the text file. Text files are great for text, but they store numeric values in a wasteful way. Binary format offers several advantages.