2
votes

As the title states I'm trying to write to a file with fprintf, and I've searched on Stack Overflow for why it wouldn't be writing to file. What I've found is that it holds it in memory until you close the file(right?), hence the usage of fclose(). However, it's still not working for me.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>


void printHelp()
{
    printf("Usage: fstring [ char ] [ amount ] Optional:[ outfile ]\n"
           "Example: fstring A 100 out.txt\n");
    exit(1);
}

char *f_string(const char *s, int t)
{
    int i; char *dst = malloc(t * strlen(s) + 1);
    for(i = 0; i < t; i++) {
        strcat(dst, s);
    }
    return dst;
}


int main(int argc, char *argv[])
{
    char c; char *file;

    while((c = getopt(argc, argv, "f:")) != -1)
        switch(c) {
        case 'f':
            file = optarg;
            break;
        default:
            printHelp();
            return 1;
        }

    if(argc < 3) {
        printf("You need at least two arguments!\n");
        return 1;
    }

    char *res = f_string(argv[1], atoi(argv[2]));
    FILE *f = fopen(file, "w+");
    if(!f) {
        puts(res);
        exit(0);
    } else {
        fprintf(f, "%s", res);
    }

    fclose(f);
    free(res);
    return 0;
}

What I really don't understand is why the above code doesn't work, but this does just fine:

int main(int argc, char *argv[])
{
    char line[80];

    if(argc != 6) {
        fprintf(stderr, "You need to give 5 arguments!\n");
        return 1;
    }

    FILE *in;
    if(!(in = fopen("spooky.csv", "r"))) {
        fprintf(stderr, "File %s doesn't exist!", in);
        return 1;
    }

    FILE *file1 = fopen(argv[2], "w");
    FILE *file2 = fopen(argv[4], "w");
    FILE *file3 = fopen(argv[5], "w");

    while(fscanf(in, "%79s", line) == 1) {
        if(strstr(line, argv[1]))
            fprintf(file1, "%s\n", line);
        else if (strstr(line, argv[3]))
            fprintf(file2, "%s\n", line);
        else
            fprintf(file3, "%s\n", line);
    }

    fclose(file1);
    fclose(file2);
    fclose(file3);
    return 0;
}

The second code works just fine writing to files. Hoping someone can enlighten me. Thanks.

3
which input arguments are you using? And always check if malloc fails: if(!dst){ handle error } - H_squared
File writing is working fine for me. But what are you trying to write into file? - niyasc
you could try using fflush(f) after fprintf - H_squared
you need to initialize you allocated memory to 0x00 using memset, since dst can be containing anything, and strcat will try to find the 1st null byte to append/concatenate. - H_squared
it is an essential skill to learn how to use a debugger. - AndersK

3 Answers

2
votes

The root cause is that the arguments are messed up.

After you called getopt, the point to arguments argv has been manipulated and no more pointed to the original argument. So afterwards when you use argv[1], argv[2] for fstring function, they are not the arguments you passed to the main function.

The simplest correction is to store the arguments in temporary variables before passing argv to getopt.

int main(int argc, char *argv[])
{
    char c; char *file;

    char *v1 = argv[1];
    int v2 = atoi(argv[2]);


    while((c = getopt(argc, argv, "f:")) != -1)
        switch(c) {
        case 'f':
            file = optarg;
            break;
        default:
            printHelp();
            return 1;
        }

    if(argc < 3) {
        printf("You need at least two arguments!\n");
        return 1;
    }

    char *res = f_string(v1, v2);
    FILE *f = fopen(file, "w+");
    if(!f) {
        puts(res);
        exit(0);
    } else {
        fprintf(f, "%s", res);
    }

    fclose(f);
    free(res);
    return 0;
}
2
votes

From man page of getopt(): By default, getopt() permutes the contents of argv as it scans, so that eventually all the nonoptions are at the end.

If your call your executable with options Abc 10 -f out.txt then after getopt processing argv[1] will be -f and argv[2] will be out.txt. So you have to change line

char *res = f_string(argv[1], atoi(argv[2]));

to

char *res = f_string(argv[3], atoi(argv[4]));

Otherwise, help usage is completely wrong because according to code nothing is optional.

Function f_string() has to be corrected:

char *f_string(const char *s, int t)
{
    int i; 
    char *dst = malloc(t * strlen(s) + 1);
    dst[0] = '\0';  // <-----

    for(i = 0; i < t; i++) {
        strcat(dst, s);
    }
    return dst;
}

Without proper initialization strcat() could start at the middle of dst.

1
votes

Use optind to find out where non-optional arguments start.

int main(int argc, char *argv[])
{
    char c; char *file;

    while((c = getopt(argc, argv, "f:")) != -1)
        switch(c) {
        case 'f':
            file = optarg;
            break;
        default:
            printHelp();
            return 1;
        }


    int index = optind;
    if(argc - index + 1 < 3) {
        printf("You need at least two arguments!\n");
        return 1;
    }
    char *res = f_string(argv[index], atoi(argv[index + 1]));
    FILE *f = fopen(file, "w+");
    if(!f) {
        puts(res);
        exit(0);
    } else {
        fprintf(f, "%s", res);
    }

    fclose(f);
    free(res);
    return 0;
}