Print statement not outputting result - c

Very new to programming will appreciate the help.
I have the following program to count words that are input in the command line
for example:
> ./main cat nap dog
Given input:
cat
.
Expected output:
Looking for 3 words
Result:
cat:1
nap:0
dog:0
This is the code I've written for it :
#define LENGTH(s) (sizeof(s) / sizeof(*s))
/* Structures */
typedef struct {
char *word;
int counter;
} WordCountEntry;
int process_stream(WordCountEntry entries[], int entry_count)
{
short line_count = 0;
char buffer[30];
while (fgets(buffer,sizeof(buffer),stdin)) {
if (*buffer == '.')
break;
/* Compare against each entry */
int i = 0;
while (i < entry_count) {
if (!strcmp(entries[i].word, buffer))
entries[i].counter++;
i++;
}
line_count++;
}
return line_count;
}
void print_result(WordCountEntry entries[], int entry_count)
{
fprintf(stdout,"Result:\n");
for(int i=0; i <=entry_count;i++){
printf("%s:%d\n", entries[i].word, entries[i].counter);
}
}
void printHelp(const char *name)
{
fprintf(stderr,"usage: %s [-h] <word1> ... <wordN>\n", name);
}
int main(int argc, char **argv)
{
const char *prog_name = *argv;
WordCountEntry entries[argc];
int entryCount = 0;
while ((*argv) != NULL) {
if (**argv == '-') {
switch ((*argv)[1]) {
case 'h':
printHelp(prog_name);
break;
case'f':
freopen((*argv)[2],"w",stdout);
break;
default:
fprintf(stderr,"%s: Invalid option %s. Use -h for help.\n",
prog_name, *argv);
}
} else {
if (entryCount < argc-1) {
entries[entryCount].word = *argv;
entries[entryCount++].counter = 0;
}
}
argv++;
}
if (entryCount == 0) {
fprintf(stderr,"%s: Please supply at least one word. Use -h for help.\n",
prog_name);
return EXIT_FAILURE;
}
if (entryCount == 1) {
fprintf(stdout,"Looking for a single word\n");
} else {
fprintf(stdout,"Looking for %d words\n", entryCount);
}
process_stream(entries, entryCount);
print_result(entries, entryCount);
return EXIT_SUCCESS;
}
I can't seem to figure out why the print_result function does not output even the "Result:" statement

You are making things much harder on yourself than it need be. While you are free to use pointer arithmetic to iterate over the strings in argv until you reach the sentinel nul, you are far better served iterating for (i = 1; i < argc; i++), using array indexing for that purpose. (don't forget that the first entry, index 0 is the program name).
Your use of freopen as is will completely obscure all output for your program. Instead of calling freopen when you test for -f, it would be better to set a save filename variable. Then when you output your results, you simply test whether there is an output filename and if so, make a simple call to fopen. I'll leave you to think about the implementation.
As mentioned in comments, fgets reads up to and including the '\n' and includes the newline in the buffer. In order to compare entries[x].word with buffer, you need to remove the trailing '\n' before the comparison because your current comparison will never match, e.g.:
strcmp ("foo", "foo\n")
Lastly the loop logic is just a bit muddled. You are free to do it any way that works, but keeping it simple and clean will greatly help you keep the indexing straight in your mind.
Putting those pieces of the puzzle together, you could make a relative few changes and do the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Structures */
typedef struct {
char *word;
int counter;
} WordCountEntry;
int process_stream (WordCountEntry entries[], int entry_count)
{
short line_count = 0;
char buffer[30];
while (fgets (buffer, sizeof (buffer), stdin)) {
if (*buffer == '.')
break;
/* strip trailing '\n' from buffer */
size_t len = strlen (buffer); /* get word length */
if (buffer[len - 1] == '\n') /* check for '\n' */
buffer[--len] = 0; /* overwrite with nul-byte */
/* Compare against each entry */
if (!strcmp (entries[line_count].word, buffer))
entries[line_count].counter++;
if (++line_count == entry_count)
break;
}
return line_count;
}
void print_result (WordCountEntry entries[], int entry_count)
{
fprintf (stdout, "\nResult:\n");
for (int i = 0; i < entry_count; i++) {
printf ("%s:%d\n", entries[i].word, entries[i].counter);
}
}
void printHelp (const char *name)
{
fprintf (stderr, "usage: %s [-h] <word1> ... <wordN>\n", name);
}
int main (int argc, char **argv)
{
const char *prog_name = *argv;
WordCountEntry entries[argc];
int entryCount = 0;
for (int i = 1; i < argc; i++) {
if (*argv[i] == '-') {
switch (argv[i][1]) {
case 'h':
printHelp (prog_name);
break;
default:
fprintf (stderr, "%s: Invalid option %s. Use -h for help.\n",
prog_name, *argv);
}
}
else {
entries[entryCount].word = argv[i];
entries[entryCount++].counter = 0;
}
}
if (!entryCount) {
fprintf (stderr,
"%s: Please supply at least one word. Use -h for help.\n",
prog_name);
return EXIT_FAILURE;
}
if (entryCount == 1) {
fprintf (stdout, "Looking for a single word\n");
}
else {
fprintf (stdout, "Looking for %d words\n", entryCount);
}
process_stream (entries, entryCount);
print_result (entries, entryCount);
return EXIT_SUCCESS;
}
note: if you using C89, then you will need to independently declare i outside:
for (int i = 0; i < entry_count; i++) {
Example Use/Output
$ ./bin/printfissue one two three "four and a half" five
Looking for 5 words
one
two
three
four and a half
five
Result:
one:1
two:1
three:1
four and a half:1
five:1
Look things over and let me know if you have further questions. There are many different ways to do the loops and indexing, so this isn't meant to be the only way you can do it, or even the most right way to do it for that matter.

Put fflush(stdout); at the end of your main function. That should flush out everything to the standard output.

Related

how to extract numbers from char array and add them up

i want to get input from the user to get the name, then check the input against a char array filled by an external text file, then store the number associated with the name to a variable and be able to add up the number with any more that is associated with the same name
code:
int cnt_space(int i, int count, char input[], char strIndex)
{
strIndex = input[0];
while (strIndex != '\0'){
strIndex = input[i];
if (isspace(strIndex))
count++;
i++;
}
return (count);
}
/*bool divisable5()
{
}*/
char * dispChange(char name[])
{
enum { MAXL = 40, MAXC = 50 };
char (*lines)[MAXC] = NULL; /* pointer to array of type char [MAXC] */
int i, n = 0, index;
FILE *fp = fopen("coins.txt", "r");
if (!fp) { /* valdiate file open for reading */
printf ("failed to open file");
}
if (!(lines = malloc (MAXL * sizeof *lines))) { /* allocate MAXL arrays */
fprintf (stderr, "error: virtual memory exhausted 'lines'.\n");
}
while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */
char *p = lines[n]; /* assign pointer */
for (; *p && *p != '\n'; p++) {} /* find 1st '\n' */
*p = 0, n++; /* nul-termiante */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
int *change = 0, finalChange = 0;
char changeStr[2];
changeStr[0] = 0;
/* print lines */
for (i = 0; i < n; i++){
if (strstr(lines[i], name) != NULL){
for (index = 0; index < strlen(lines[i]); index++){
if(isdigit(lines[i][index])){
printf("test %s %d", lines[i], lines[i][index]);
if(changeStr[0] == 0){
changeStr[1] = "1";
printf("%s", changeStr);
strcpy(changeStr, lines[i][index]);
}
else{
strcat(changeStr, lines[i][index]);
}
}
}
sscanf(changeStr, "%d", change);
finalChange += change;
main();
}
else if (i == n-1 && strstr(lines[i], name) == NULL){
printf("Name not found, please try again\n\n");
dispChange(name);
}
}
printf ("Customer:\n%s%dcents\n\n", name, finalChange);
free (lines); /* free allocated memory */
}
char * getName()
{
char ch;
int i = 0, count = 0;
char name[100];
printf("Enter a name(spaces not accepted): ");
fgets(name, sizeof name, stdin);
count = cnt_space(i, count, name, ch);
if (count > 1){
printf("Error: Spaces not accepted!\n\n");
free(name);
getName();
}
else{
dispChange(name);
}
}
int main()
{
int choice;
char option[100];
printf("1. Enter Name\n2. Exit\n------------------\nEnter option: ");
fgets(option, sizeof option, stdin);
if (option[1] != '\n'){
printf("Invalid entry!\n\n");
main();
}
else{
choice = (int)option[0];
if (isdigit(choice)){
if (choice == 49){
getName();
}
else if (choice == 50){
printf("Program has ended\n");
exit(0);
}
else{
printf("Invalid option!\n\n");
main();
}
}
else{
printf("Invalid entry!\n\n");
main();
}
}
return 0;
}
somehow doing lines[i][index] does not get the number? it should get number 3 but its getting number 51
text file contains:
Justin 60
Jane 30
Jared 90
MinZhan 95
Andreas 80
GuoCong 95
David 45
Ngiap 35
Teng 10
Teow 95
To answer your question, it IS getting 3 as an ASCII character, which has a decimal value of 51. You are printing it with %d, which is the decimal formatter.
There are, however, a lot of things going on in this program.
The first is cnt_space:
int cnt_space(int i, int count, char input[], char strIndex)
{
strIndex = input[0];
while (strIndex != '\0'){
strIndex = input[i];
if (isspace(strIndex))
count++;
i++;
}
return (count);
}
You are always passing 0 for i, the start of the string, so you do not need to pass i at all. You are also always passing 0 for count, and thus do not need to pass count at all. You are also passing ch, which is never read but only written to, so should be declared only in cnt_space. But you do not even need an actual count, you just need to know if the name contains a space character at all, which you can determine with a function like strpbrk. I would remove your cnt_space function entirely and replace it with:
// these values came from the man page for isspace
if (strpbrk(name, " \f\n\r\t\v")) {
printf("Error: Spaces not accepted!\n\n");
continue;
}
You'll notice I put continue in the code, which doesn't quite fit with what you've got because you recursively call main. That's typically frowned upon, and I changed it to continue in anticipation of replacing this with some kind of loop.
Going to skip over dispChange for a minute and look at your getName function:
char * getName()
{
char ch;
int i = 0, count = 0;
char name[100];
printf("Enter a name(spaces not accepted): ");
fgets(name, sizeof name, stdin);
count = cnt_space(i, count, name, ch);
if (count > 1){
printf("Error: Spaces not accepted!\n\n");
free(name);
getName();
}
else{
dispChange(name);
}
}
Good practice would be to make this function static, as it is only called within this file. You also specify that it returns a character pointer, but it does not actually return anything. If you want to keep this as the caller to dispChange, I would recommend changing the return type to void since there is no return, and changing the function name to reflect this. The recursive call should be replaced with a loop in this case to prevent a literal stackoverflow. Also, you free name but free is to return memory to the heap (from malloc or some other memory allocation). You are calling it on the stack, which I would imagine crashes. I would rewrite it to look more like this:
static void getNameAndDisplayChange()
{
char name[100];
do {
printf("Enter a name(spaces not accepted): ");
fgets(name, sizeof(name), stdin);
// these values came from the man page for isspace
if (strpbrk(name, " \f\n\r\t\v")) {
printf("Error: Spaces not accepted!\n\n");
continue;
}
dispChange(name);
} while (0);
}
Now let's take a look at main, before we get into the weeds of dispChange. You had:
int main()
{
int choice;
char option[100];
printf("1. Enter Name\n2. Exit\n------------------\nEnter option: ");
fgets(option, sizeof option, stdin);
if (option[1] != '\n'){
printf("Invalid entry!\n\n");
main();
}
else{
choice = (int)option[0];
if (isdigit(choice)){
if (choice == 49){
getName();
}
else if (choice == 50){
printf("Program has ended\n");
exit(0);
}
else{
printf("Invalid option!\n\n");
main();
}
}
else{
printf("Invalid entry!\n\n");
main();
}
}
return 0;
}
The C standard allows void arguments to main, though you may make your program more flexible by taking argc and *argv[] to accept a name on input. There are better ways to validate the input here, but we can clean it up a lot just by changing the recursion. I would also recommend using defines such as EXIT_SUCCESS and EXIT_FAILURE instead of return 0. Instead of converting the input to an integer and using the ASCII values to determine the action, you can use strcmp to keep it more readable. I would also recommend creating a "readline" function that strips the newline from user input to make it even more readable, but you can do that later. The refactored main would look something like:
int main()
{
char option[100] = "\0";
while (strcmp(option, "2") != 0) {
printf("1. Enter Name\n2. Exit\n------------------\nEnter option: ");
fgets(option, sizeof(option), stdin);
if (strcmp(option, "1\n") != 0 && strcmp(option, "2\n") != 0) {
printf("Invalid entry: %s!\n\n", option);
continue;
}
if (strcmp(option, "2\n") == 0) {
printf("Program has ended\n");
break;
}
getNameAndDisplayChange();
}
return EXIT_SUCCESS;
}
For dispChange, typically with a function this big I would recommend breaking it into smaller functions, but I think we can shrink it quite a bit:
char * dispChange(char name[])
{
enum { MAXL = 40, MAXC = 50 };
char (*lines)[MAXC] = NULL; /* pointer to array of type char [MAXC] */
int i, n = 0, index;
FILE *fp = fopen("coins.txt", "r");
if (!fp) { /* valdiate file open for reading */
printf ("failed to open file");
}
if (!(lines = malloc (MAXL * sizeof *lines))) { /* allocate MAXL arrays */
fprintf (stderr, "error: virtual memory exhausted 'lines'.\n");
}
while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */
char *p = lines[n]; /* assign pointer */
for (; *p && *p != '\n'; p++) {} /* find 1st '\n' */
*p = 0, n++; /* nul-termiante */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
int *change = 0, finalChange = 0;
char changeStr[2];
changeStr[0] = 0;
/* print lines */
for (i = 0; i < n; i++){
if (strstr(lines[i], name) != NULL){
for (index = 0; index < strlen(lines[i]); index++){
if(isdigit(lines[i][index])){
printf("test %s %d", lines[i], lines[i][index]);
if(changeStr[0] == 0){
changeStr[1] = "1";
printf("%s", changeStr);
strcpy(changeStr, lines[i][index]);
}
else{
strcat(changeStr, lines[i][index]);
}
}
}
sscanf(changeStr, "%d", change);
finalChange += change;
main();
}
else if (i == n-1 && strstr(lines[i], name) == NULL){
printf("Name not found, please try again\n\n");
dispChange(name);
}
}
printf ("Customer:\n%s%dcents\n\n", name, finalChange);
free (lines); /* free allocated memory */
}
Again, I would make it static as it is confined to this file. It also does not return anything, so I would change the return type to void. Since we aren't changing name, I would let the caller know that by defining it as const as well.
There are some flaws in this function, you read from fp even if it is determined the file could not be opened. You compare fp to stdin, which I don't see a way for that to happen right now (maybe a feature enhancement?). If you have to read from stdin, it makes sense that you would want to load it all into memory, but you'll definitely want to do that once and outside of this function. strstr is used in such a way that it may match unintended names, such as "Charley" will match "O'Charley". To fix that, you will want to make sure that the name matches the start of the line, and that it ends in a space. strlen is computed inside of a loop, which is just wasted CPU cycles, since the length won't change.
You can reduce a lot of complexity and increase scalability by operating on the file one line at a time, instead of loading it all into memory. Again, if you have to support stdin, you will not want to make this change, but rather have a separate function that loads a FILE * into an array, something like loadData(FILE *fp, lines). This refactor is based on how your sample works, iterating through the file for every name.
static void dispChange(const char name[])
{
enum { MAXC = 50 };
char line[MAXC];
int nameLen = strlen(name);
FILE *fp = fopen("coins.txt", "r");
if (!fp) { /* valdiate file open for reading */
printf ("failed to open file\n");
return;
}
int finalChange = 0;
while (fgets(line, MAXC, fp)) { /* read each line */
if (strstr(line, name) == line && line[nameLen] == ' ') {
int change;
sscanf(&line[nameLen + 1], "%d", &change);
finalChange += change;
}
}
fclose(fp);
printf ("Customer:\n%s %d cents\n\n", name, finalChange);
}
I hope this is helpful, and not just me doing the assignment for you.

Can't eliminate one character in my array while parsing it even though I handle that character

So this is my second time adapting my code to fscanf to get what I want. I threw some comments next to the output. The main issue I am having is that the one null character or space is getting added into the array. I have tried to check for the null char and the space in the string variable and it does not catch it. I am a little stuck and would like to know why my code is letting that one null character through?
Part where it is slipping up "Pardon, O King," output:King -- 1; -- 1
so here it parses king a word and then ," goes through the strip function and becomes \0, then my check later down the road allows it through??
Input: a short story containing apostrophes and commas (the lion's rock. First, the lion woke up)
//Output: Every unique word that shows up with how many times it shows up.
//Lion -- 1
//s - 12
//lion -- 8
//tree -- 2
//-- 1 //this is the line that prints a null char?
//cub -- //3 it is not a space! I even check if it is \0 before entering
//it into the array. Any ideas (this is my 2nd time)?
//trying to rewrite my code around a fscanf function.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
//Remove non-alpha numeric characters
void strip_word(char* string)
{
char* string_two = calloc(80, sizeof(char));
int i;
int c = 0;
for(i = 0; i < strlen(string); i++)
{
if(isalnum(string[i]))
{
string_two[c] = string[i];
++c;
}
}
string_two[i] = '\0';
strcpy(string, string_two);
free(string_two);
}
//Parse through file
void file_parse(FILE* text_file, char*** word_array, int** count_array, int* total_count, int* unique_count)
{
int mem_Size = 8;
int is_unique = 1;
char** words = calloc(mem_Size, sizeof(char *)); //Dynamically allocate array of size 8 of char*
if (words == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
int* counts = calloc(mem_Size, sizeof(int)); //Dynamically allocate array of size 8 of int
if (counts == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
printf("Allocated initial parallel arrays of size 8.\n");
fflush(stdout);
char* string;
while('A')
{
is_unique = 1;
fscanf(text_file, " ,");
fscanf(text_file, " '");
while(fscanf(text_file, "%m[^,' \n]", &string) == 1) //%m length modifier
{
is_unique = 1;
strip_word(string);
if(string == '\0') continue; //if the string is empty move to next iteration
else
{
int i = 0;
++(*total_count);
for(i = 0; i < (*unique_count); i++)
{
if(strcmp(string, words[i]) == 0)
{
counts[i]++;
is_unique = 0;
break;
}
}
if(is_unique)
{
++(*unique_count);
if((*unique_count) >= mem_Size)
{
mem_Size = mem_Size*2;
words = realloc(words, mem_Size * sizeof(char *));
counts = realloc(counts, mem_Size * sizeof(int));
if(words == NULL || counts == NULL)
{
fprintf(stderr, "ERROR: realloc() failed!");
}
printf("Re-allocated parallel arrays to be size %d.\n", mem_Size);
fflush(stdout);
}
words[(*unique_count)-1] = calloc(strlen(string) + 1, sizeof(char));
strcpy(words[(*unique_count)-1], string);
counts[(*unique_count) - 1] = 1;
}
}
free(string);
}
if(feof(text_file)) break;
}
printf("All done (successfully read %d words; %d unique words).\n", *total_count, *unique_count);
fflush(stdout);
*word_array = words;
*count_array = counts;
}
int main(int argc, char* argv[])
{
if(argc < 2 || argc > 3) //Checks if too little or too many args
{
fprintf(stderr, "ERROR: Invalid Arguements\n");
return EXIT_FAILURE;
}
FILE * text_file = fopen(argv[1], "r");
if (text_file == NULL)
{
fprintf(stderr, "ERROR: Can't open file");
}
int total_count = 0;
int unique_count = 0;
char** word_array;
int* count_array;
file_parse(text_file, &word_array, &count_array, &total_count, &unique_count);
fclose(text_file);
int i;
if(argv[2] == NULL)
{
printf("All words (and corresponding counts) are:\n");
fflush(stdout);
for(i = 0; i < unique_count; i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
else
{
printf("First %d words (and corresponding counts) are:\n", atoi(argv[2]));
fflush(stdout);
for(i = 0; i < atoi(argv[2]); i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
for(i = 0; i < unique_count; i++)
{
free(word_array[i]);
}
free(word_array);
free(count_array);
return EXIT_SUCCESS;
}
I'm not sure quite what's going wrong with your code. I'm working on macOS Sierra 10.12.3 with GCC 6.3.0, and the local fscanf() does not support the m modifier. Consequently, I modified the code to use a fixed size string of 80 bytes. When I do that (and only that), your program runs without obvious problem (certainly on the input "the lion's rock. First, the lion woke up").
I also think that the while ('A') loop (which should be written conventionally while (1) if it is used at all) is undesirable. I wrote a function read_word() which gets the next 'word', including skipping blanks, commas and quotes, and use that to control the loop. I left your memory allocation in file_parse() unchanged. I did get rid of the memory allocation in strip_word() (eventually — it worked OK as written too).
That left me with:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
static void strip_word(char *string)
{
char string_two[80];
int i;
int c = 0;
int len = strlen(string);
for (i = 0; i < len; i++)
{
if (isalnum(string[i]))
string_two[c++] = string[i];
}
string_two[c] = '\0';
strcpy(string, string_two);
}
static int read_word(FILE *fp, char *string)
{
if (fscanf(fp, " ,") == EOF ||
fscanf(fp, " '") == EOF ||
fscanf(fp, "%79[^,' \n]", string) != 1)
return EOF;
return 0;
}
static void file_parse(FILE *text_file, char ***word_array, int **count_array, int *total_count, int *unique_count)
{
int mem_Size = 8;
char **words = calloc(mem_Size, sizeof(char *));
if (words == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
int *counts = calloc(mem_Size, sizeof(int));
if (counts == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
printf("Allocated initial parallel arrays of size 8.\n");
fflush(stdout);
char string[80];
while (read_word(text_file, string) != EOF)
{
int is_unique = 1;
printf("Got [%s]\n", string);
strip_word(string);
if (string[0] == '\0')
continue;
else
{
int i = 0;
++(*total_count);
for (i = 0; i < (*unique_count); i++)
{
if (strcmp(string, words[i]) == 0)
{
counts[i]++;
is_unique = 0;
break;
}
}
if (is_unique)
{
++(*unique_count);
if ((*unique_count) >= mem_Size)
{
mem_Size = mem_Size * 2;
words = realloc(words, mem_Size * sizeof(char *));
counts = realloc(counts, mem_Size * sizeof(int));
if (words == NULL || counts == NULL)
{
fprintf(stderr, "ERROR: realloc() failed!");
exit(EXIT_FAILURE);
}
printf("Re-allocated parallel arrays to be size %d.\n", mem_Size);
fflush(stdout);
}
words[(*unique_count) - 1] = calloc(strlen(string) + 1, sizeof(char));
strcpy(words[(*unique_count) - 1], string);
counts[(*unique_count) - 1] = 1;
}
}
}
printf("All done (successfully read %d words; %d unique words).\n", *total_count, *unique_count);
fflush(stdout);
*word_array = words;
*count_array = counts;
}
int main(int argc, char *argv[])
{
if (argc < 2 || argc > 3)
{
fprintf(stderr, "ERROR: Invalid Arguements\n");
return EXIT_FAILURE;
}
FILE *text_file = fopen(argv[1], "r");
if (text_file == NULL)
{
fprintf(stderr, "ERROR: Can't open file");
return EXIT_FAILURE;
}
int total_count = 0;
int unique_count = 0;
char **word_array = 0;
int *count_array = 0;
file_parse(text_file, &word_array, &count_array, &total_count, &unique_count);
fclose(text_file);
if (argv[2] == NULL)
{
printf("All words (and corresponding counts) are:\n");
fflush(stdout);
for (int i = 0; i < unique_count; i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
else
{
printf("First %d words (and corresponding counts) are:\n", atoi(argv[2]));
fflush(stdout);
for (int i = 0; i < atoi(argv[2]); i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
for (int i = 0; i < unique_count; i++)
free(word_array[i]);
free(word_array);
free(count_array);
return EXIT_SUCCESS;
}
When run on the data file:
the lion's rock. First, the lion woke up
the output was:
Allocated initial parallel arrays of size 8.
Got [the]
Got [lion]
Got [s]
Got [rock.]
Got [First]
Got [the]
Got [lion]
Got [woke]
Got [up]
All done (successfully read 9 words; 7 unique words).
All words (and corresponding counts) are:
the -- 2
lion -- 2
s -- 1
rock -- 1
First -- 1
woke -- 1
up -- 1
When the code was run on your text, including double quotes, like this:
$ echo '"Pardon, O King,"' | cw37 /dev/stdin
Allocated initial parallel arrays of size 8.
Got ["Pardon]
Got [O]
Got [King]
Got ["]
All done (successfully read 3 words; 3 unique words).
All words (and corresponding counts) are:
Pardon -- 1
O -- 1
King -- 1
$
It took a little finnagling of the code. If there isn't an alphabetic character, your code still counts it (because of subtle problems in strip_word()). That would need to be handled by checking strip_word() more carefully; you test if (string == '\0') which checks (belatedly) whether memory was allocated where you need if (string[0] == '\0') to test whether the string is empty.
Note that the code in read_word() would be confused into reporting EOF if there were two commas in a row, or an apostrophe followed by a comma (though it handles a comma followed by an apostrophe OK). Fixing that is fiddlier; you'd probably be better off using a loop with getc() to read a string of characters. You could even use that loop to strip non-alphabetic characters without needing a separate strip_word() function.
I am assuming you've not yet covered structures yet. If you had covered structures, you'd use an array of a structure such as struct Word { char *word; int count; }; and allocate the memory once, rather than needing two parallel arrays.

Read from a text file and use each line to compare if they are anagrams

I must modify my program to accept input from
a file called anagrams.txt.This file should have two strings per line, separated by the # character. My program should read
each pair of strings and report back if each pair of strings is an anagram. For example consider the following content of anagrams.txt:
hello#elloh
man#nam
Astro#Oastrrasd
Your program should print out the following:
hello#elloh - Anagrams!
man#nam - Anagrams!
Astro#Oastrrasd- Not anagrams!
I should compile in g++
Here is the code to read from text:
int main()
{
char input[30];
if(access( "anagrams.txt", F_OK ) != -1) {
FILE *ptr_file;
char buf[1000];
ptr_file =fopen("anagrams.txt","r"); if (!ptr_file)
return 1;
while (fgets(buf,1000, ptr_file)!=NULL)
printf("%s",buf);
fclose(ptr_file);
printf("\n");
}
else{ //if file does not exist
printf("\nFile not found!\n");
}
return 0;
}
Code to find if the text are anagrams:
#include <stdio.h>
int find_anagram(char [], char []);
int main()
{
char array1[100], array2[100];
int flag;
printf("Enter the string\n");
gets(array1);
printf("Enter another string\n");
gets(array2);
flag = find_anagram(array1, array2);
if (flag == 1)
printf(" %s and %s are anagrams.\n", array1, array2);
else
printf("%s and %s are not anagrams.\n", array1, array2);
return 0;
}
int find_anagram(char array1[], char array2[])
{
int num1[26] = {0}, num2[26] = {0}, i = 0;
while (array1[i] != '\0')
{
num1[array1[i] - 'a']++;
i++;
}
i = 0;
while (array2[i] != '\0')
{
num2[array2[i] -'a']++;
i++;
}
for (i = 0; i < 26; i++)
{
if (num1[i] != num2[i])
return 0;
}
return 1;
}
You can try something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXLINE 1000
#define MAXLETTER 256
int is_anagram(char *word1, char *word2);
void check_lines(FILE *filename);
int cmpfunc(const void *a, const void *b);
void convert_to_lowercase(char *word);
int
main(int argc, char const *argv[]) {
FILE *filename;
if ((filename = fopen("anagram.txt", "r")) == NULL) {
fprintf(stderr, "Error opening file\n");
exit(EXIT_FAILURE);
}
check_lines(filename);
fclose(filename);
return 0;
}
void
check_lines(FILE *filename) {
char line[MAXLINE];
char *word1, *word2, *copy1, *copy2;
while (fgets(line, MAXLINE, filename) != NULL) {
word1 = strtok(line, "#");
word2 = strtok(NULL, "\n");
copy1 = strdup(word1);
copy2 = strdup(word2);
convert_to_lowercase(copy1);
convert_to_lowercase(copy2);
if (is_anagram(copy1, copy2)) {
printf("%s#%s - Anagrams!\n", word1, word2);
} else {
printf("%s#%s - Not Anagrams!\n", word1, word2);
}
}
}
void
convert_to_lowercase(char *word) {
int i;
for (i = 0; word[i] != '\0'; i++) {
word[i] = tolower(word[i]);
}
}
int
is_anagram(char *word1, char *word2) {
qsort(word1, strlen(word1), sizeof(*word1), cmpfunc);
qsort(word2, strlen(word2), sizeof(*word2), cmpfunc);
if (strcmp(word1, word2) == 0) {
return 1;
}
return 0;
}
int
cmpfunc(const void *a, const void *b) {
if ((*(char*)a) < (*(char*)b)) {
return -1;
}
if ((*(char*)a) > (*(char*)b)) {
return +1;
}
return 0;
}
Since this looks like a University question, I won't provide a full solution, only a hint.
All you have to do is replace the stdin input part of the anagram-finding file with the code you wrote to read from a file: it's as simple as changing
printf("Enter the string\n");
gets(array1);
printf("Enter another string\n");
gets(array2);
to
// before program:
#define SIZE 1000
// inside main
if (access("anagrams.txt", F_OK) == -1){
printf("\nFile not found!\n");
return 1; // Abort the program early if we can't find the file
}
FILE *ptr_file;
char buf[1000];
ptr_file = fopen("anagrams.txt","r");
if (!ptr_file)
return 1;
char array1[SIZE], array2[SIZE];
while (fgets(buf, 1000, ptr_file)!=NULL){
// do all your anagram stuff here!
// there is currently one line of the input file stored in buf
// Hint: You need to split buf into array_1 and array_2 using '#' to separate it.
}
fclose(ptr_file);
printf("\n");
Additional comments:
Don't ever ever ever use gets. gets doesn't check that the string it writes to can hold the data, which will cause your program to crash if it gets input bigger than the array size. Use fgets(buf, BUF_SIZE, stdin) instead.
Beautiful code is good code. People are more likely to help if they can read your code easily. (fix your brackets)
Just for interest, a more efficient algorithm for checking anagrams is to use qsort to sort both arrays, then a simple string matcher to compare them. This will have cost O(mnlog(m+n)), as opposed to O(m^2 n^2), awith the current algorithm
You need to split every line you read by fgets (as you did) in to two strings, and pass them to your find_anagram function. You can do that using strtok:
int main()
{
int flag;
char buf[1000];
FILE *ptr_file;
//Check file existence
//Open the file for reading
while (fgets (buf, 1000, ptr_file) != NULL)
{
char *array1 = strtok(buf, "#");
char *array2 = strtok(NULL, "\n");
flag = find_anagram (array1, array2);
//Check flag value to print your message
}
return 0;
}
//put your find_anagram function
Don't forget to #include <string.h> to use strtok().

How to use command line arguments to change a bool value [duplicate]

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 option is --, it automatically assumes that the next argument is the first filename.
if the option is -i, it compares them in a case insensitive manner.
defaults to comparing the files character by character.
It's not supposed to matter how many times 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 two files.
I don't even know where to begin with parsing the command line arguments.
So this is the code that I came up with for everything. I haven't error checked it quite yet, but am I 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;
}
To my knowledge, the three most popular ways how to parse command line arguments in C are:
Getopt (#include <unistd.h> from the POSIX C Library), which can solve simple argument parsing tasks. If you're a bit familiar with bash, the getopt built-in of bash is based on Getopt from the GNU libc.
Argp (#include <argp.h> from the GNU C Library), which can solve more complex tasks and takes care of stuff like, for example:
-?, --help for help message, including email address
-V, --version for version information
--usage for usage message
Doing it yourself, which I don't recommend for programs that would be given to somebody else, as there is too much that could go wrong or lower quality. The popular mistake of forgetting about '--' to stop option parsing is just one example.
The GNU C Library documentation has some nice examples for Getopt and Argp.
http://www.gnu.org/software/libc/manual/html_node/Getopt.html
http://www.gnu.org/software/libc/manual/html_node/Argp.html
Example for using Getopt
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
bool isCaseInsensitive = false;
int opt;
enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE;
while ((opt = getopt(argc, argv, "ilw")) != -1) {
switch (opt) {
case 'i': isCaseInsensitive = true; break;
case 'l': mode = LINE_MODE; break;
case 'w': mode = WORD_MODE; break;
default:
fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
// Now optind (declared extern int by <unistd.h>) is the index of the first non-option argument.
// If it is >= argc, there were no non-option arguments.
// ...
}
Example for using Argp
#include <argp.h>
#include <stdbool.h>
const char *argp_program_version = "programname programversion";
const char *argp_program_bug_address = "<your#email.address>";
static char doc[] = "Your program description.";
static char args_doc[] = "[FILENAME]...";
static struct argp_option options[] = {
{ "line", 'l', 0, 0, "Compare lines instead of characters."},
{ "word", 'w', 0, 0, "Compare words instead of characters."},
{ "nocase", 'i', 0, 0, "Compare case insensitive instead of case sensitive."},
{ 0 }
};
struct arguments {
enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode;
bool isCaseInsensitive;
};
static error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct arguments *arguments = state->input;
switch (key) {
case 'l': arguments->mode = LINE_MODE; break;
case 'w': arguments->mode = WORD_MODE; break;
case 'i': arguments->isCaseInsensitive = true; break;
case ARGP_KEY_ARG: return 0;
default: return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 };
int main(int argc, char *argv[])
{
struct arguments arguments;
arguments.mode = CHARACTER_MODE;
arguments.isCaseInsensitive = false;
argp_parse(&argp, argc, argv, 0, 0, &arguments);
// ...
}
Example for Doing it Yourself
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
bool isCaseInsensitive = false;
enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE;
size_t optind;
for (optind = 1; optind < argc && argv[optind][0] == '-'; optind++) {
switch (argv[optind][1]) {
case 'i': isCaseInsensitive = true; break;
case 'l': mode = LINE_MODE; break;
case 'w': mode = WORD_MODE; break;
default:
fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
argv += optind;
// *argv points to the remaining non-option arguments.
// If *argv is NULL, there were no non-option arguments.
// ...
}
Disclaimer: I am new to Argp, the example might contain errors.
Use getopt(), or perhaps getopt_long().
int iflag = 0;
enum { WORD_MODE, LINE_MODE } op_mode = WORD_MODE; // Default set
int opt;
while ((opt = getopt(argc, argv, "ilw") != -1)
{
switch (opt)
{
case 'i':
iflag = 1;
break;
case 'l':
op_mode = LINE_MODE;
break;
case 'w':
op_mode = WORD_MODE;
break;
default:
fprintf(stderr, "Usage: %s [-ilw] [file ...]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
/* Process file names or stdin */
if (optind >= argc)
process(stdin, "(standard input)", op_mode);
else
{
int i;
for (i = optind; i < argc; i++)
{
FILE *fp = fopen(argv[i], "r");
if (fp == 0)
fprintf(stderr, "%s: failed to open %s (%d %s)\n",
argv[0], argv[i], errno, strerror(errno));
else
{
process(fp, argv[i], op_mode);
fclose(fp);
}
}
}
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 function process() - you can't access the enumeration down there. It's best to move the enumeration outside the function; you might even make op_mode a file-scope variable without external linkage (a fancy way of saying static) to avoid passing it to the function. This code does not handle - as a synonym for standard input, another exercise for the reader. Note that getopt() 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:
int filter(int argc, char **argv, int idx, int (*function)(FILE *fp, const char *fn));
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 that op_mode should be a static file scope variable. The filter() function takes argc, 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:
version "0.1"
package "myApp"
purpose "Does something useful."
# Options
option "filename" f "Input filename" string required
option "verbose" v "Increase program verbosity" flag off
option "id" i "Data ID" int required
option "value" r "Data value" multiple(1-) int optional
Generating the code is easy and spits out cmdline.h and cmdline.c:
$ gengetopt --input=myApp.cmdline --include-getopt
The generated code is easily integrated:
#include <stdio.h>
#include "cmdline.h"
int main(int argc, char ** argv) {
struct gengetopt_args_info ai;
if (cmdline_parser(argc, argv, &ai) != 0) {
exit(1);
}
printf("ai.filename_arg: %s\n", ai.filename_arg);
printf("ai.verbose_flag: %d\n", ai.verbose_flag);
printf("ai.id_arg: %d\n", ai.id_arg);
int i;
for (i = 0; i < ai.value_given; ++i) {
printf("ai.value_arg[%d]: %d\n", i, ai.value_arg[i]);
}
}
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.
You can use James Theiler's "opt" package.
And a flattering post with some examples of how it is so much simpler than other approaches is here:
Opt 3.19 review and upgrades
Docopt has a C implementation that I thought was quite
nice:
From a man-page standardized format describing command line options, docopt infers and creates an argument parser. This got started in Python; the Python version literally just parses the docstring and returns a dict. To do this in C takes a little more work, but it's clean to use and has no external dependencies.
There is a great general-purpose C library, libUCW, which includes neat command-line option parsing and configuration file loading.
The library also comes with good documentation and includes some other useful stuff (fast I/O, data structures, allocators, ...) but this can be used separately.
Example libUCW option parser (from the library docs)
#include <ucw/lib.h>
#include <ucw/opt.h>
int english;
int sugar;
int verbose;
char *tea_name;
static struct opt_section options = {
OPT_ITEMS {
OPT_HELP("A simple tea boiling console."),
OPT_HELP("Usage: teapot [options] name-of-the-tea"),
OPT_HELP(""),
OPT_HELP("Options:"),
OPT_HELP_OPTION,
OPT_BOOL('e', "english-style", english, 0, "\tEnglish style (with milk)"),
OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "<spoons>\tAmount of sugar (in teaspoons)"),
OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"),
OPT_STRING(OPT_POSITIONAL(1), NULL, tea_name, OPT_REQUIRED, ""),
OPT_END
}
};
int main(int argc, char **argv)
{
opt_parse(&options, argv+1);
return 0;
}
Tooting my own horn if I may, I'd also like to suggest taking a look at an option parsing library that I've written: dropt.
It's a C library (with a C++ wrapper if desired).
It's lightweight.
It's extensible (custom argument types can be easily added and have equal footing with built-in argument types).
It should be very portable (it's written in standard C) with no dependencies (other than the C standard library).
It has a very unrestrictive license (zlib/libpng).
One feature that it offers that many others don't is the ability to override earlier options. For example, if you have a shell alias:
alias bar="foo --flag1 --flag2 --flag3"
and you want to use bar but with--flag1 disabled, it allows you to do:
bar --flag1=0
I wrote a tiny library that parses arguments similar to POpt, which I had several issues with, called XOpt. It uses GNU-style argument parsing and has a very similar interface to POpt.
I use it from time to time with great success, and it works pretty much anywhere.
I wrote a command-line parser library called cmdparser
https://github.com/XUJINKAI/cmdparser/
It's fully tested and support nested sub-commands.
An example for question:
static cmdp_action_t callback(cmdp_process_param_st *params);
static bool g_line_by_line = false;
static bool g_word_by_word = false;
static bool g_case_insensitive = false;
static cmdp_command_st cmdp = {
.options = {
{'l', NULL, "line by line", CMDP_TYPE_BOOL, &g_line_by_line },
{'w', NULL, "word by word", CMDP_TYPE_BOOL, &g_word_by_word },
{'i', NULL, "case insensitive", CMDP_TYPE_BOOL, &g_case_insensitive },
{0},
},
.fn_process = callback,
};
int main(int argc, char **argv) {
return cmdp_run(argc - 1, argv + 1, &cmdp);
}
static cmdp_action_t callback(cmdp_process_param_st *params) {
if (g_line_by_line && g_word_by_word) {
return CMDP_ACT_FAIL | CMDP_ACT_SHOW_HELP;
}
// your code here...
return CMDP_ACT_OVER;
}
#include <stdio.h>
int main(int argc, char **argv)
{
size_t i;
size_t filename_i = -1;
for (i = 0; i < argc; i++)
{
char const *option = argv[i];
if (option[0] == '-')
{
printf("I am a flagged option");
switch (option[1])
{
case 'a':
/*someting*/
break;
case 'b':
break;
case '-':
/* "--" -- the next argument will be a file.*/
filename_i = i;
i = i + 1;
break;
default:
printf("flag not recognised %s", option);
break;
}
}
else
{
printf("I am a positional argument");
}
/* At this point, if -- was specified, then filename_i contains the index
into argv that contains the filename. If -- was not specified, then filename_i will be -1*/
}
return 0;
}
Instructional template for parsing command line arguments in C.
C:>programName -w -- fileOne.txt fileTwo.txt
BOOL argLine = FALSE;
BOOL argWord = FALSE;
BOOL argChar = FALSE;
char * fileName1 = NULL;
char * fileName2 = NULL;
int main(int argc, char * argv[]) {
int i;
printf("Argument count=%d\n",argc);
for (i = 0; i < argc; i++) {
printf("Argument %s\n",argv[i]);
if (strcmp(argv[i],"-l")==0) {
argLine = TRUE;
printf(" argLine=TRUE\n");
}
else if (strcmp(argv[i],"-w")==0) {
argWord = TRUE;
printf(" argWord=TRUE\n");
}
else if (strcmp(argv[i],"-c")==0) {
argChar = TRUE;
printf(" argChar=TRUE\n");
}
else if (strcmp(argv[i],"--")==0) {
if (i+1 <= argc) {
fileName1 = argv[++i];
printf(" fileName1=%s\n",fileName1);
}
if (i+1 <= argc) {
fileName2 = argv[++i];
printf(" fileName2=%s\n",fileName2);
}
}
}
return 0;
}
/*
Here's a rough one not relying on any libraries.
Example:
-wi | -iw //word case insensitive
-li | -il //line case insensitive
-- file //specify the first filename (you could just get the files
as positional arguments in the else statement instead)
PS: don't mind the #define's, they're just pasting code :D
*/
#ifndef OPT_H
#define OPT_H
//specify option requires argument
#define require \
optarg = opt_pointer + 1; \
if (*optarg == '\0') \
{ \
if (++optind == argc) \
goto opt_err_arg; \
else \
optarg = argv[optind]; \
} \
opt_pointer = opt_null_terminator;
//start processing argv
#define opt \
int optind = 1; \
char *opt_pointer = argv[1]; \
char *optarg = NULL; \
char opt_null_terminator[2] = {'\0','\0'}; \
if (0) \
{ \
opt_err_arg: \
fprintf(stderr,"option %c requires argument.\n",*opt_pointer); \
return 1; \
opt_err_opt: \
fprintf(stderr,"option %c is invalid.\n",*opt_pointer); \
return 1; \
} \
for (; optind < argc; opt_pointer = argv[++optind]) \
if (*opt_pointer++ == '-') \
{ \
for (;;++opt_pointer) \
switch (*opt_pointer) \
{
//stop processing argv
#define done \
default: \
if (*opt_pointer != '\0') \
goto opt_err_opt; \
else \
goto opt_next; \
break; \
} \
opt_next:; \
}
#endif //opt.h
#include <stdio.h>
#include "opt.h"
int
main (int argc, char **argv)
{
#define by_character 0
#define by_word 1
#define by_line 2
int cmp = by_character;
int case_insensitive = 0;
opt
case 'h':
puts ("HELP!");
break;
case 'v':
puts ("fileCMP Version 1.0");
break;
case 'i':
case_insensitive = 1;
break;
case 'w':
cmp = by_word;
break;
case 'l':
cmp = by_line;
break;
case '-':required
printf("first filename: %s\n", optarg);
break;
done
else printf ("Positional Argument %s\n", argv[optind]);
return 0;
}
Okay, that's the start of long story - made short
abort parsing a command line in C ...
/**
* Helper function to parse the command line
* #param argc Argument Counter
* #param argv Argument Vector
* #param prog Program Instance Reference to fill with options
*/
bool parseCommandLine(int argc, char* argv[], DuplicateFileHardLinker* prog) {
bool pathAdded = false;
// Iterate over all arguments...
for (int i = 1; i<argc; i++) {
// Is argv a command line option?
if (argv[i][0] == '-' || argv[i][0] == '/') {
// ~~~~~~ Optionally Cut that part vvvvvvvvvvvvv for sake of simplicity ~~~~~~~
// Check for longer options
if (stricmp( &argv[i][1], "NoFileName") == 0 ||
strcmp( &argv[i][1], "q1" ) == 0 ) {
boNoFileNameLog = true;
} else if (strcmp( &argv[i][1], "HowAreYou?") == 0 ) {
logInfo( "SECRET FOUND: Well - wow I'm glad ya ask me.");
} else {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Now here comes the main thing:
//
// Check for one-character options
while (char option = *++argv[i]) {
switch (option) {
case '?':
// Show program usage
logInfo(L"Options:");
logInfo(L" /q\t>Quite mode");
logInfo(L" /v\t>Verbose mode");
logInfo(L" /d\t>Debug mode");
return false;
// Log options
case 'q':
setLogLevel(LOG_ERROR);
break;
case 'v':
setLogLevel(LOG_VERBOSE);
break;
case 'd':
setLogLevel(LOG_DEBUG);
break;
default:
logError(L"'%s' is an illegal command line option!"
" Use /? to see valid options!", option);
return false;
} // switch one-char-option
} // while one-char-options
} // else one vs longer options
} // if isArgAnOption
//
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ So that's it! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// What follows now is are some useful extras...
//
else {
// The command line options seems to be a path...
WCHAR tmpPath[MAX_PATH_LENGTH];
mbstowcs(tmpPath, argv[i], sizeof(tmpPath));
// Check if the path is existing!
//...
prog->addPath(tmpPath); // Comment or remove to get a working example
pathAdded = true;
}
}
// Check for parameters
if (!pathAdded) {
logError("You need to specify at least one folder to process!\n"
"Use /? to see valid options!");
return false;
}
return true;
}
int main(int argc, char* argv[]) {
try {
// Parse the command line
if ( !parseCommandLine(argc, argv, prog) ) {
return 1;
}
// I know that sample is just to show how the nicely parse command-line arguments
// So Please excuse more nice useful C-glatter that follows now...
}
catch ( LPCWSTR err ) {
DWORD dwError = GetLastError();
if ( wcslen(err) > 0 ) {
if ( dwError != 0 ) {
logError(dwError, err);
}
else {
logError(err);
}
}
return 2;
}
}
#define LOG_ERROR 1
#define LOG_INFO 0
#define LOG_VERBOSE -1
#define LOG_DEBUG -2
/** Logging level for the console output */
int logLevel = LOG_INFO;
void logError(LPCWSTR message, ...) {
va_list argp;
fwprintf(stderr, L"ERROR: ");
va_start(argp, message);
vfwprintf(stderr, message, argp);
va_end(argp);
fwprintf(stderr, L"\n");
}
void logInfo(LPCWSTR message, ...) {
if ( logLevel <= LOG_INFO ) {
va_list argp;
va_start(argp, message);
vwprintf(message, argp);
va_end(argp);
wprintf(L"\n");
}
}
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 library ones are quiet nice. So I would prefer the libUCW option parser, Arg or Getopt over a home-made ones.
Note you may change:
*++argv[i] -> (++argv*)[0]
It is longer and less cryptic, but still cryptic.
Okay, let's break it down:
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.
It is so nice that in C#, the pointers 'died' - long live the pointers!!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int is_arg(int ac, char **argv, char *arg) {
if (ac < 2) {
return 0;
}
for(int x=1; x < ac; x++) {
if (0 == strcmp(argv[x], arg)) {
return x; // return position of arg
}
}
return 0; // arg not present
}
int main (int argc, char *argv[]) {
int z = 0;
if (argc < 2) {
printf("no args present, aborting.\n");
exit(1);
}
(z=is_arg(argc, argv, "bar")) ? printf("TRUE %d\n", z) : printf("FALSE\n");
(z=is_arg(argc, argv, "one bar")) ? printf("TRUE %d\n", z) : printf("FALSE\n");
(z=is_arg(argc, argv, "foo")) ? printf("TRUE %d\n", z) : printf("FALSE\n");
/* testing:
run: ./getopt two bar "one bar" foo
TRUE 2
TRUE 3
TRUE 4
run: ./getopt two bar one bar foo
TRUE 2
FALSE
TRUE 5
*/
return 0;
}

Segmentation fault when passing pointer to function

I am getting a segmentation fault when I call my getField(char *line, int field) function in my while loop and I'm not sure why. I'm trying to pass a line to the function and a column number so that I can grab specific columns from each line in a csv file and print them to the screen. Thanks for input.
void getField(char *line, int field);
int main(int argc, char *argv[]) {
if(argc < 3) {
fprintf(stderr, "Too few arguments \"%s\".\n", argv[0]);
}
if(atoi(argv[1]) < 1) {
fprintf(stderr, "First argument must be >= 1 \"%s\".\n", argv[1]);
}
FILE *fp = fopen(argv[2], "r");
if(fp == NULL)
fprintf(stderr, "Cannot open file %s\n", argv[0]);
char buf[80];
while(fgets(buf, 80, fp) != NULL) {
getField(buf, atoi(argv[1]); // seg fault is happening here
}
return 0;
}
void getField(char *line, int field) {
printf("here2");
//char *ln = line;
int column = field - 1;
int idx = 0;
while(column) {
//printf("here");
if(line[idx] == ',') field--;
idx++;
}
for(int j = idx; ; ++j) {
if(line[j] == ',') break;
printf("%s", line[j]);
}
printf("\n");
printf("%d", idx);
}
One obvious error is that you have an infinite loop here, and you will eventually access illegal memory.
while(column) {
//printf("here");
if(line[idx] == ',') field--;
idx++;
}
You are not modifying column at all, so your loop cannot possibly end.
column will not update itself when you update field, so you will have to update it if you want it to update.
while(column) {
//printf("here");
if(line[idx] == ',') field--;
idx++;
column = field - 1;
}
Note on debugging segfaults using printf.
The function printf prints to stdout and stdout likes to buffer output. This means that sometimes if you try to find a segfault by moving a print statement down your code until it fails to print, you will misunderstand where the segfault it happening. In particular, a printf line that appears before the line that actually contains the segfault may not print even if you might expect it to.
If you want to use this strategy (instead of gdb), you can force it to print by using fflush(stdout); immediately after your debugging printf.
while(column) {
//printf("here");
if(line[idx] == ',') column--; // Changed field-- to column--
idx++;
}
In following line:
printf("%s", line[j]);
you are using the %s format specifier but you are passing a char as argument.
You probably want this (%c format specifier fot printing a char):
printf("%c", line[j]);
You are accessing out of bounds of the array in the function getField because the while loop never exits. This invokes undefined behaviour and most likely program crash due to segfault which is what is happening in your case. I suggest the following changes to your program.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void getField(char *line, int field);
int main(int argc, char *argv[]) {
if(argc < 3) {
fprintf(stderr, "Too few arguments \"%s\".\n", argv[0]);
return 1; // end the program
}
if(atoi(argv[1]) < 1) {
fprintf(stderr, "First argument must be >= 1 \"%s\".\n", argv[1]);
return 1; // end the program
}
FILE *fp = fopen(argv[2], "r");
if(fp == NULL) {
fprintf(stderr, "Cannot open file %s\n", argv[0]);
return 1; // end the program
}
char buf[80];
while(fgets(buf, 80, fp) != NULL) {
getField(buf, atoi(argv[1])); // seg fault is happening here
}
return 0;
}
void getField(char *line, int field) {
int len = strlen(line);
char temp[len + 1];
strcpy(temp, line);
int count = 0;
char ch = ',';
char *p = temp;
char *q = NULL;
while(count < field - 1) {
q = strchr(p, ch);
if(q == NULL) {
printf("error in the value of field\n");
return;
}
count++;
p = q + 1;
}
q = strchr(p, ch);
if(q != NULL)
*q = '\0';
else
temp[len-1] = '\0';
printf("%s\n", p);
}

Resources