File Processing
It is very common to read input from users or files. Both can be
performed using fgets, which is a safe library function. This document
starts with fgets, which can be used in any circumstance. It also
presents using fscanf, which can only be used on well-formed,
numeric data. You should never switch off between fgets and fscanf,
because there are subtle challenges in passing off between the
two.
Outline:
Click here for the full program code of these examples, not just a snippet.
Reading in a Text File
The most flexible approach is to read in a line as characters.
A line is defined as a sequence of characters ended by the '\n' character.
Once read in, the line is parsed, or divided into tokens -
short text strings that are meaningful. For example, if you were reading
in a document of text, you might split it into sentences, then words and
punctuation. Each word and punctuation character would be a token.
Here is the function prototype of fgets.
char *fgets(char *s, int size, FILE *stream);
What does fgets do?
-
fgets reads in up to (size-1) characters from stream.
-
It stops when it reaches the end of the file (EOF character),
a newline ('\n'), or when it has read size-1 characters.
-
It places the characters into array s, starting at index 0.
-
It adds '\0' after the last character read in.
How do you use fgets? The trick is that all three input
parameters need to have been set properly.
-
Declare and allocate an array to pass in as s.
fgets does NOT allocate space in s. You must have already
allocated s outside of the function. You are passing a pointer to the
beginning of the array.
-
Specify the size - the number of characters s holds.
-
To read from user input, use the file stream stdin. You do not need to
declare, allocate, or initialize stdin - the stdio takes care of that for you.
-
Declare and initialize the file stream - if you are reading from
a file. Use fopen to open the file.
To read from a file, you need to use the following function to open it:
FILE *fopen(const char *restrict pathname, const char *restrict mode)
What does fopen do?
-
Searches for and gets access to the file from the operating system.
-
Sets the location of reading to the beginning of the file (if reading)
How do you use fopen to read from a file?
-
Provide the path name of the file. If you use a filename but not the
path, then it looks in the same directory as the place from which you
ran the file.
-
Provide the mode. To read, use "r". Note that this is a char *, not a char,
so you need to use quotation marks, not apostrophes. "r", not 'r'.
Examples
Example 1: Read from the user and print out back to them.
char buffer[500];
// read in the line and make sure it was successful
// just for fun, let's read in 10 inputs from the user
int i;
for(i=0;i<10;i++)
{
// read in and check whether read was successful
if (fgets(buffer,500,stdin) != NULL)
{
printf("You inputted %s!\n",buffer);
}
}
Example 2: Read a filename from the user, use that to open
a file and print it out.
char buffer[500];
FILE *infile;
int len;
fgets(buffer,500,stdin);
// remove the '\n' at the end of the line
len = strlen(buffer);
buffer[len-1] = '\0'; // replace '\n' with '\0' to shorten line by 1.
// open the file
if ((infile = fopen(buffer,"r")) == NULL)
{
printf("Could not open %s\n", buffer);
exit(1);
}
// while not end of the file
while (!feof(infile))
{
// read in the line and make sure it was successful
if (fgets(buffer,500,infile) != NULL)
{
// print out the line
printf("%s", buffer);
}
}
// don't forget to close the file when you're done
fclose(infile);
Parsing Input
Remember that in C, strings are nothing more than a character array followed
by a '\0' character. So you should always think of strings in this way.
strtok is a string tokenizer, which, like fgets, is general functionality
provided in some way by pretty much every programming language. The
idea is that you tell the tokenizer what string you want to split up
and by what delimiters you want it split up, and it will give you each
token. There were several new vocabulary words in that sentence, so
let me give you a concrete example.
"This is a sentence, and I want to read only the words from this sentence.\n"
In the above case, the delimiters are spaces, commas, periods, and \n.
In the above case, the tokens are "this", "is", "a", "sentence", etc. Each
word is a token.
So the job of the string tokenizer is to, given the knowledge that you want
it to split by commands, periods, spaces, and \n, give you, in sequence,
the different words.
char *strtok(char *string, char *deliminators);
What does strotok do?
If you provide a pointer to a string as the first parameter:
-
Start at the beginning of the string.
-
Advances to the first instance of an item that is not a delimiter.
-
Finds the first instance of a deliminator and replaces it with '\0'.
-
Returns a pointer to the location it found in step (2).
-
Once it runs out of tokens in the string, it returns NULL.
If you use NULL as the first parameter:
-
Start immediately after the '\0' in the previous call to strtok. (We will learn later how to do that)
-
Advances to the first instance of an item that is not a delimiter.
-
Finds the first instance of a deliminator and replaces it with '\0'.
-
Returns a pointer to the location it found in step (2).
-
Once it runs out of tokens in the string, it returns NULL.
The result is that the original string has been modified from its original form.
How do you use strtok?
-
You need to have a declared, allocated, and initialized string.
-
You need to identify the delimiters to use.
-
First call it with the string and delimiters.
-
Pass NULL as the first argument for all subsequent calls on the same string.
-
Capture the char * it returns and use that for processing.
-
Stop when it returns NULL.
char str[] = "This is a sentence, and I want to read only the words from this sentence.\n";
char *tokenPtr;
// initialize the string tokenizer by passing in str
// capture the return value in tokenPtr.
tokenPtr = strtok(str, " ,.\n");
// check to make sure the previous call found a token.
while(tokenPtr != NULL)
{
// do something with the token.
printf("%s\n",tokenPtr);
// for all subsequent calls, use NULL as the first argument
tokenPtr = strtok(NULL, " ,.\n");
}
Reading in numbers
Notice that with the pair of fgets and strtok, the token is a string. Often, however, you
need the input to be a number. You can convert from a string to other types easily using
some library calls.
Integers:
-
long strtol(const char *restrict str, char **restrict endptr, int base) - convert from string to long integer
-
long long strtoll(const char *restrict str, char **restrict endptr, int base) - convert from string to long long integer
Floating-point Numbers:
-
double strtod(const char *restrict nptr, char **restrict endptr) - convert from string to double
-
float strtof(const char *restrict nptr, char **restrict endptr) - convert from string to float
-
long double strtold(const char *restrict nptr, char **restrict endptr) - convert from string to long double
What these functions do:
-
Start at nptr
-
Skip past any white-space characters (as specified by isspace())
-
Convert a sequence of numerical characters. If you are calling an integer function, then you can provide
a base, and it will treat the original string as if it was expressed in that base.
-
Set endptr, if not NULL, to the character in the string past the numerical characters used for the conversion
-
Return the result.
How to use them:
-
Pass in a pointer to the beginning of the number to nptr.
-
If you want a pointer that points after the number, provide the address of a pointer as the second parameter.
-
Capture the return value in a variable of the proper type.
char buffer[500];
// read in a line from the user
fgets(buffer,500,stdin);
// I am expecting a base 10 integer, so I convert it:
long choice = strtol(buffer, NULL, 10);
// now I can use it as an integer!!!
Reading in a well-formed numerical file
Writing to a Text File
Writing to a text file is almost identical to printing to the screen - the only
difference is that you specify a file pointer.
FILE *outfile;
if ((outfile = fopen("outfilename.txt","w")) == NULL)
{
printf("Could not open outfilename.txt\n");
return;
}
// writing to a file is like writing to the screen -
// use fprintf rather than printf and give it the outfile first
fprintf(outfile, "I am writing to a file!!!\nLook at me!!\n");
fclose(outfile);