Reputation: 13
What is the best way to read a file and then compare a string to what is in that file? I have been trying to figure this out for days now and I am just stumped on how to do it. I would greatly appreciate some help.
Upvotes: 0
Views: 1537
Reputation: 84551
Your question outlines in broad terms reading a string from a file, and making a string comparison with another string. To read from your file, you must first open the file for reading. For reading text from a file, your normal choice in C is fopen
to open a stream and asign the return to a FILE
pointer which can be used with the collection of stream reading functions found in the standard library. (e.g. fgetc
, fgets
, fscanf
, getline
, etc..)
Your job is to open a file, validate it is open, read the needed information from the file, and then close the file. To accomplish this, you will need to prepare a buffer (character array, or allocated block of memory) with sufficient space to hold the information read from the file (+1 char
for the nul-terminating character). Without the nul-terminating character, you have an array of characters and not a valid string) A short example could be:
char buf[256] = {0}; /* char array to hold 255 chars + nul-terminating char */
FILE *fp = NULL; /* FILE pointer to hold return of fopen */
fp = fopen (filename, "r"); /* open file for reading */
if (fp == NULL) { /* if not open throw error */
fprintf (stderr, "error: file open failed 'filename'.\n");
exit (EXIT_FAILURE);
}
If you are reading a single string without included spaces from the file, you can simply read the first word with fscanf
into buf
. fscanf
returns the number of successful conversion that match the given format specifier. Use the field width option with your format specifier to insure you read no more than your buffer will hold (e.g. "%255s"
) saving room for the nul-terminating char at the end. Use the return from fscanf
to validate your read:
if (fscanf (fp, "%255s", buf) != 1) {
fprintf (stderr, "error: fscanf read failure from 'filename'.\n");
exit (EXIT_FAILURE);
}
If the string you are reading from the file can include whitespace, then you either need to modify your fscanf
format specifier, (e.g. "%255[...]"
, where the character class [...]
describes an expression to match your needed information) or use one of line-oriented stream functions (fgets
or getline
) to read an entire line from the file into buf
and then parse buf
as needed within the file. For example if the first line in the file included text separated by a comma:
The quick brown fox, jumps over the lazy dog.
and you need to compare the information appearing before the comma with a test string, you could do something like:
char str[256] = {0}; /* string to hold chars parsed from buf */
if (fgets (buf, 256, fp) == NULL) {
fprintf (stderr, "error: fgets read failure from 'filename'.\n");
exit (EXIT_FAILURE);
}
if (sscanf (buf, "%[^,]", str) != 1) {
fprintf (stderr, "error: sscanf no conversion made.\n");
exit (EXIT_FAILURE);
}
Finally, to compare if the strings are equal, the normal choice is to use strcmp
. Two strings are equal if the return of the comparison by strcmp
is 0
. You can implement the test like:
if (strcmp (str, teststr) == 0) /* test if strings are equal */
printf ("\n string : %s\n teststr : %s EQUAL\n", str, teststr);
else
printf ("\n string : %s\n teststr : %s DIFFER\n", str, teststr);
Which would perform the test with strcmp
and print the string read from the file and the teststr and whether the strings are EQUAL or DIFFER.
You can put all the pieces together as follows. (note: the program takes the filename as the first argument, but by using a simple ternary
operator, will use a default filename of data.txt
if no filename is given -- stdin
can be substituted for "data.txt"
to read from stdin
by default):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char **argv) {
char buf[256] = {0}; /* char array to hold 255 chars + null-terminating char */
char str[256] = {0}; /* string to hold chars parsed from buf */
char *teststr = "The quick brown fox"; /* string literal for comparison */
FILE *fp = NULL; /* FILE pointer to hold return of fopen */
fp = fopen (argc > 1 ? argv[1] : "data.txt", "r"); /* open file for reading */
if (fp == NULL) { /* if not open throw error */
fprintf (stderr, "error: file open failed 'filename'.\n");
exit (EXIT_FAILURE);
}
if (fgets (buf, 256, fp) == NULL) { /* read 1 line from file */
fprintf (stderr, "error: fgets read failure from 'filename'.\n");
exit (EXIT_FAILURE);
}
fclose (fp); /* close file when reading complete */
if (sscanf (buf, "%[^,]", str) != 1) { /* parse up to ',' into str */
fprintf (stderr, "error: sscanf no conversion made.\n");
exit (EXIT_FAILURE);
}
if (strcmp (str, teststr) == 0) /* test if strings are equal */
printf ("\n string : %s\n teststr : %s EQUAL\n", str, teststr);
else
printf ("\n string : %s\n teststr : %s DIFFER\n", str, teststr);
return 0;
}
Input File
$ cat dat/qbfcomma.txt
The quick brown fox, jumps over the lazy dog.
Use/Output
$ ./bin/fgets_strcmp dat/qbfcomma.txt
string : The quick brown fox
teststr : The quick brown fox EQUAL
Let me know if you have any additional questions.
Note: you could simply use fscanf
with the format specifier "%255[^,]"
to read the same information into buf
and then simply test buf
against teststr
without the need to parse the line into str
using sscanf
. But it is important to note that the normal approach is to use a line-oriented (reading a line at a time) and then parsing the needed information from the line within your code. This will avoid a number of pitfalls that arise from using fscanf
or when trying to shoehorn all pattern matching into a fscanf
format specifier. It also allows additional methods of parsing the needed information from the buffer (such as using pointer arithmetic, etc..).
Upvotes: 3