Hi I'm trying to write a program that can compare two files line by line, word by word, or character by character in C. It has to be able to read in command line options "-l -w -i or --"... if the option is -l it compares the files line by line. if the option is -w it compares the files word by word. if the options is -- it automatically assumes that the next arg is the first filename. and if the option is -i it compares them in a case insensitive manner. Otherwise it defaults to comparing the files character by character
It's not supposed to matter how many time the options are input as long as -w and -l aren't inputted at the same time and there are no more or less than 2 files.
I don't even know where to begin with parsing the command line arguments. PLEASE HELP :(
So this is the code that I came up with for everything. I haven't error checked it quite yet, but I was wondering if I'm writing things in an overcomplicated manner?
/*
* Functions to compare files.
*/
int compare_line();
int compare_word();
int compare_char();
int case_insens();
/*
* Program to compare the information in two files and print message saying
* whether or not this was successful.
*/
int main(int argc, char* argv[])
{
/*Loop counter*/
size_t i = 0;
/*Variables for functions*/
int caseIns = 0;
int line = 0;
int word = 0;
/*File pointers*/
FILE *fp1, *fp2;
/*
* Read through command-line arguments for options.
*/
for (i = 1; i < argc; i++) {
printf("argv[%u] = %s\n", i, argv[i]);
if (argv[i][0] == '-') {
if (argv[i][1] == 'i')
{
caseIns = 1;
}
if (argv[i][1] == 'l')
{
line = 1;
}
if (argv[i][1] == 'w')
{
word = 1;
}
if (argv[i][1] == '-')
{
fp1 = argv[i][2];
fp2 = argv[i][3];
}
else
{
printf("Invalid option.");
return 2;
}
} else {
fp1(argv[i]);
fp2(argv[i][1]);
}
}
/*
* Check that files can be opened.
*/
if(((fp1 = fopen(fp1, "rb")) == NULL) || ((fp2 = fopen(fp2, "rb")) == NULL))
{
perror("fopen()");
return 3;
}
else{
if (caseIns == 1)
{
if(line == 1 && word == 1)
{
printf("That is invalid.");
return 2;
}
if(line == 1 && word == 0)
{
if(compare_line(case_insens(fp1, fp2)) == 0)
return 0;
}
if(line == 0 && word == 1)
{
if(compare_word(case_insens(fp1, fp2)) == 0)
return 0;
}
else
{
if(compare_char(case_insens(fp1,fp2)) == 0)
return 0;
}
}
else
{
if(line == 1 && word == 1)
{
printf("That is invalid.");
return 2;
}
if(line == 1 && word == 0)
{
if(compare_line(fp1, fp2) == 0)
return 0;
}
if(line == 0 && word == 1)
{
if(compare_word(fp1, fp2) == 0)
return 0;
}
else
{
if(compare_char(fp1, fp2) == 0)
return 0;
}
}
}
return 1;
if(((fp1 = fclose(fp1)) == NULL) || (((fp2 = fclose(fp2)) == NULL)))
{
perror("fclose()");
return 3;
}
else
{
fp1 = fclose(fp1);
fp2 = fclose(fp2);
}
}
/*
* Function to compare two files line-by-line.
*/
int compare_line(FILE *fp1, FILE *fp2)
{
/*Buffer variables to store the lines in the file*/
char buff1 [LINESIZE];
char buff2 [LINESIZE];
/*Check that neither is the end of file*/
while((!feof(fp1)) && (!feof(fp2)))
{
/*Go through files line by line*/
fgets(buff1, LINESIZE, fp1);
fgets(buff2, LINESIZE, fp2);
}
/*Compare files line by line*/
if(strcmp(buff1, buff2) == 0)
{
printf("Files are equal.\n");
return 0;
}
printf("Files are not equal.\n");
return 1;
}
/*
* Function to compare two files word-by-word.
*/
int compare_word(FILE *fp1, FILE *fp2)
{
/*File pointers*/
FILE *fp1, *fp2;
/*Arrays to store words*/
char fp1words[LINESIZE];
char fp2words[LINESIZE];
if(strtok(fp1, " ") == NULL || strtok(fp2, " ") == NULL)
{
printf("File is empty. Cannot compare.\n");
return 0;
}
else
{
fp1words = strtok(fp1, " ");
fp2words = strtok(fp2, " ");
if(fp1words == fp2words)
{
fputs(fp1words);
fputs(fp2words);
printf("Files are equal.\n");
return 0;
}
}
return 1;
}
/*
* Function to compare two files character by character.
*/
int compare_char(FILE *fp1,FILE *fp2)
{
/*Variables to store the characters from both files*/
int c;
int d;
/*Buffer variables to store chars*/
char buff1 [LINESIZE];
char buff2 [LINESIZE];
while(((c = fgetc(fp1))!= EOF) && (((d = fgetc(fp2))!=EOF)))
{
if(c == d)
{
if((fscanf(fp1, "%c", buff1)) == (fscanf(fp2, "%c", buff2)))
{
printf("Files have equivalent characters.\n");
return 1;
break;
}
}
}
return 0;
}
/*
* Function to compare two files in a case-insensitive manner.
*/
int case_insens(FILE *fp1, FILE *fp2, size_t n)
{
/*Pointers for files.*/
FILE *fp1, *fp2;
/*Variable to go through files.*/
size_t i = 0;
/*Arrays to store file information.*/
char fp1store[LINESIZE];
char fp2store[LINESIZE];
while(!feof(fp1) && !feof(fp2))
{
for(i = 0; i < n; i++)
{
fscanf(fp1, "%s", fp1store);
fscanf(fp2, "%s", fp2store);
fp1store = tolower(fp1store);
fp2store = tolower(fp2store);
return 1;
}
}
return 0;
}
Instructional template for parsing command line arguments in C.
C:>programName -w -- fileOne.txt fileTwo.txt
Use
getopt()
, or perhapsgetopt_long()
.Note that you need to determine which headers to include (I make it 4 that are required), and the way I wrote the
op_mode
type means you have a problem in the functionprocess()
- you can't access the enumeration down there. It's best to move the enumeration outside the function; you might even makeop_mode
a file-scope variable without external linkage (a fancy way of sayingstatic
) to avoid passing it to the function. This code does not handle-
as a synonym for standard input, another exercise for the reader. Note thatgetopt()
automatically takes care of--
to mark the end of options for you.I've not run any version of the typing above past a compiler; there could be mistakes in it.
For extra credit, write a (library) function:
which encapsulates the logic for processing file name options after the
getopt()
loop. It should handle-
as standard input. Note that using this would indicate thatop_mode
should be a static file scope variable. Thefilter()
function takesargc
,argv
,optind
and a pointer to the processing function. It should return 0 (EXIT_SUCCESS) if it was able to open all the files and all invocations of the function reported 0, otherwise 1 (or EXIT_FAILURE). Having such a function simplifies writing Unix-style 'filter' programs that read files specified on the command line or standard input.I've found Gengetopt to be quite useful - you specify the options you want with a simple configuration file, and it generates a .c/.h pair that you simply include and link with your application. The generated code makes use of getopt_long, appears to handle most common sorts of command line parameters, and it can save a lot of time.
A gengetopt input file might look something like this:
Generating the code is easy and spits out
cmdline.h
andcmdline.c
:The generated code is easily integrated:
If you need to do any extra checking (such as ensuring flags are mutually exclusive), you can do this fairly easily with the data stored in the
gengetopt_args_info
struct.Okay that's the start of long story - made short 'bort parsing a command line in C ...
Note that this version will also support combining arguments: So instead of writing /h /s -> /hs will also work.
Sorry for being the n-th person posting here - however I wasn't really satisfied with all the stand-alone-versions I saw here. Well the lib ones are quit nice. So I would prefere libUCW option parser, Arg or Getopt over a home-made ones.
Note you may change:
*++argv[i]
->(++argv*)[0]
longer less cryptic but still cryptic.Okay let's break it down: 1. argv[i]-> access i-th element in the argv-char pointer field
++*... -> will forward the argv-pointer by one char
... [0]-> will follow the pointer read the char
++(...) -> bracket are there so we'll increase the pointer and not the char value itself.
So nice that In C## the pointers 'died' - long live the pointers !!!