0

I'm working on my code. If I use scanf instead fgets, It works but I want to use fgets. If I use fgets after I enter number of lines, I need to press enter. How can I fix this problem? Is this normal? I hope I can explain.

int main() {

    FILE *myfile;
    myfile = fopen("test.txt", "w");

    int line_count;

    printf("Enter number of line:");
    scanf("%d", &line_count);


    if (myfile == NULL) {
        printf("Can't create!");
        return 1;
    }

    char line[100];

    for (int i = 0;i < line_count;i++) {
        fgets(line, sizeof(line), stdin);
        fprintf(myfile, "Line %d\n",i+1);
    }

    fclose(myfile);
    return 0;
}

I tried use scanf instead fgets but i want to use fgets.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Alpiinoo
  • 11
  • 1
  • 2
    You need to check what `fgets` [*returns*](https://en.cppreference.com/w/c/io/fgets#Return_value). What if there aren't at least `line_count` number of lines in the file? – Some programmer dude Jun 12 '23 at 12:36
  • 1
    Also, the order of things is a little wrong I think. If the file can't be opened, there's no need to ask the user about the number of lines to read. – Some programmer dude Jun 12 '23 at 12:37
  • @Someprogrammerdude The file is written successfully, for example, when I want it to write 5 lines, I have to press enter 5 more times after scanf. – Alpiinoo Jun 12 '23 at 12:43
  • That's what you're code is doing: Reading `line_count` number of *lines* which needs to end in a newline. What is your assignment? What is the problem the program is supposed to solve? – Some programmer dude Jun 12 '23 at 12:46
  • 3
    Never ever use `scanf()` without checking the return value. If the user enters something other than a number, `line_count` will be uninitialized. – DevSolar Jun 12 '23 at 12:55
  • @DevSolar What am I suppouse to do now? – Alpiinoo Jun 12 '23 at 13:01
  • ...checking the return value... – DevSolar Jun 12 '23 at 13:17
  • @Alpiinoo add white space to skip '\n' in stdin after entered number with scanf, for ex `scanf("%d ", &line_count);` – yvs2014 Jun 12 '23 at 13:18
  • 3
    @yvs2014 Terrible advice. That requires that the user input something that *isn't* a newline or other whitepsace after the newline. Otherwise the `scanf` call will just continue to swallow whitespace (including newlines) forever. – Some programmer dude Jun 12 '23 at 13:20
  • @Someprogrammerdude fgets goes after scanf with terrible reading "something that isn't a newline" – yvs2014 Jun 12 '23 at 13:25
  • 3
    @yvs2014 The `scanf` call with a trailing space in the format string will *never return* unless the user inputs some non-space characters. The loop and the `fgets` call will simply never happen unless there's non-space input that is terminated by an *extra* newline (for the terminal to send the input to the program). See e.g. [What is the effect of trailing white space in a scanf() format string?](https://stackoverflow.com/questions/19499060/what-is-the-effect-of-trailing-white-space-in-a-scanf-format-string) – Some programmer dude Jun 12 '23 at 13:32
  • @Someprogrammerdude if we speak about stdin, then '\n' as an indicator is expected in user input. Talking about scanf() in general, it's better not to use it first and foremost. Suppose remaining '\n' in stdin after scanf which went into fgets() (with -1 line in resulting outputfile) was the cause of this question. – yvs2014 Jun 12 '23 at 13:44
  • 1
    Instead of working around the newline problem with `scanf` I'd just read in an entire line with `fgets` and apply `sscanf` to for finding the number of lines. – Aconcagua Jun 12 '23 at 14:05
  • I agree that "%d " is not applicable in general, and can be used only in that particular case scanf + fgets combination, which processes "number\nline\n" as a whole. Sorry, if it was confusing. – yvs2014 Jun 12 '23 at 14:11
  • 1
    Thanks for all informations. I'll use with scanf instead of fgets... – Alpiinoo Jun 12 '23 at 15:27
  • @yvs2014 For this very specific use-case, where no prompt for the input is given, then yes it will work, but only because there's no prompt being printed before the `fgets` call. Or any other processing is done after the `scanf` call. As I said: The `scanf` call with a trailing space in the format ***will not return*** until a space (*any* space!) is input, *plus* some non-space input followed by the `Enter` key (if input from a terminal). So any possible prompt will not be written until the input is already written, and the prompt will come after the input. – Some programmer dude Jun 12 '23 at 16:14
  • *'`scanf` instead of `fgets`'* – be aware that these do different things (unless you try to mimic fgets via `%x[^\n]\n` with x being a literal specifying the maximum number of characters to scan). `%s` format specifier will stop at any whitespace, so you cannot scan strings including these like e.g. `"hello world"` in one single go. – Aconcagua Jun 12 '23 at 19:26

2 Answers2

0

You need to read the new line character '\n' that corresponds to the pressed key Enter before using fgets as for example

printf("Enter number of line:");
scanf("%d", &line_count);

int c ;
while ( ( c = getchar() ) != EOF && c != '\n' );

Pay attention to that in general you need check that a call of scanf or fgets was successful.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

I personally would go with these few lines:

char line[128];
printf("Enter number of lines:");
if(!fgets(line, sizeof(line), stdin))
{
    // TODO: error handling!
}
else
{
    char* end;
    long line_count = strtol(line, &end, 10);
    // ...
}

Using fgets solves the problem with the trailing new-line, and you can use end to test if the input was valid at all (or partially at least):

  • end == line -> no conversion at all took place
  • *end != '\n' -> not the entire input has been parsed (you could additionally test for the remaining characters being whitespace only...)

Some special cases might not be covered yet, leaving that up to you.

In any case, if you accept the input as valid (I would not open the file until you determined that, by the way) you can go on for all the following lines with fgets as you intended/wished.

Aconcagua
  • 24,880
  • 4
  • 34
  • 59