jimo
jimo

Reputation: 602

Can't find the bug in my 'writing a file' program

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

    int main()

    {

        char file_name[100];
        char insert[100];

        printf("Enter the file name:");
        scanf("%s", file_name);
        FILE *f = fopen((strcat(file_name, ".txt")), "a");
        printf("Insert:");
        scanf ("%[^\n]%*s", insert);
        fprintf(f, "%s", insert);

    }

The above stated code is supposed to write/insert data to the file, however, the program is terminating right after printing 'Insert:'. I cant seem to find the problem here.

Upvotes: 0

Views: 84

Answers (2)

Spikatrix
Spikatrix

Reputation: 20244

The scanf:

scanf("%s", file_name);

will not consume the newline which you press after entering the file name. It stays in the standard input stream(stdin). The scanf:

scanf ("%[^\n]%*s", insert);

scans everything until a newline character, and then scans and discards a string. %[^\n] will fail if the next character is \n. So, scanf, in your case fails and returns as the next character is \n. insert remains uninitialized and contains garbage.

fprintf(f, "%s", insert);

writes the garbage into f.


How to fix the problem? Two ways of fixing the issue:

  1. Use a getchar(); just after the first scanf to get rid of the newline character left over by the first scanf.
  2. Use

    scanf (" %[^\n]%*s", insert);
    

    instead of

    scanf ("%[^\n]%*s", insert);
    

    The space before %[^\n] scans and discards all whitespace characters(like \n, space etc),if any, until the first non-whitespace character as seen in the quote of the C11 standard below:

    7.21.6.2 The fscanf function

    [...]

    1. A directive composed of white-space character(s) is executed by reading input up to the first non-white-space character (which remains unread), or until no more characters can be read. The directive never fails

I prefer the second solution as it takes care of any number of whitespace characters.


Both these solutions work, but then you have another problem. Your program doesn't stop scanning after you give data for the second scanf! Fixing this is easy. Just remove the %*s from the second scanf.

Wondering why? Quoting the C11 standard,

7.21.6.2 The fscanf function

[...]

  1. The conversion specifiers and their meanings are:
    [...]
    s Matches a sequence of non-white-space characters. 286
    [...]

So, %s will skip leading whitespace characters and will stop scanning when it encounters a whitespace character or, if the length field is present, until the specified length or until a whitespace character, whichever occurs first. The bolded part explains it.


For improving security, limit the number of characters that scanf reads for insert and file_name so that you can avoid buffer overflows. Also, check the return values of both the scanfs and fopen to see if they were successful. Both the scanfs, in your case, returns 1 if successful. If not 1, an error occured. fopen, returns NULL on failure. If not NULL, fopen is successful.


Final program:

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

int main()        
{        
    char file_name[100];
    char insert[100];

    printf("Enter the file name:");
    if(scanf("%94s", file_name) != 1)  //Scans a maximum of 94 chars,+1 for \0 at the end,+4 for ".txt"
    {
        printf("scanf failed\n");
        exit(-1); //Exit the program with a negative return value
    }

    strcat(file_name, ".txt"); //Improves readability,do one thing in one line 
    FILE *f = fopen(filename, "a");

    if(f == NULL) //if fopen failed to open the filename,
    {
        printf("fopen failed\n");
        exit(-1); 
    }

    printf("Insert:");
    if(scanf (" %99[^\n]", insert) != 1) // Note the space before %[^\n],99 scans a maximum of 99 chars,+1 for the \0 at the end
    {
        printf("scanf failed\n");
        fclose(f); //Free resources before exiting
        exit(-1);
    }
    fprintf(f, "%s", insert);
    fclose(f); //Close the file after use
}


If you want to discard the newline character left over by the second scanf, use

scanf (" %99[^\n]%*c", insert)

instead of

scanf (" %99[^\n]", insert)

It doesn't make a difference in this program, though.


If you are wondering why I'm stating quotes from chapter "7.21.6.2 The fscanf function" it is because, fscanf is exactly the same as scanf when the first argument of it is stdin. Quoting once again from the C11 standard,

7.21.6.4 The scanf function

[...]

  1. The scanf function is equivalent to fscanf with the argument stdin interposed before the arguments to scanf.

Upvotes: 3

David Hoelzer
David Hoelzer

Reputation: 16379

The trouble is in your first scanf. The carriage return is not being dealt with, which means that the next scanf deals with it immediately upon being called since it's still in the buffer.

Perhaps try, scanf("%s\n", filename);

Upvotes: 0

Related Questions