0

i'm new here and i'm trying to solve a FILE problem in c. Basically i have to create a program that lets the user input how many lines he wants to write in a file, create a new file, write those lines and the reading it and establish how many lines where written and print the number of lines.

 int main() {
  int x, lc=0;
  char str[100];
  FILE *fp=fopen("test.txt","w");
  if (fp==NULL) {
    printf("\nOpening failed");
  }else{
    printf("\nOpened correctly");
  }
  printf("\nStrings to write:\n");
  scanf("%d",&x);
  for (int i = 0; i < x; i++) {
    fgets(str, sizeof str, stdin);
    fputs(str,fp);
  }
  fclose(fp);
  FILE *fr=fopen("test.txt", "r");
  while (fgets(str, 100, fr)!=NULL) {
      lc++;
    }
  fclose(fr);
  printf("\nThere are %d lines",lc);
  return 0;
 }

If i leave the code like this it messes up with my for cycle and it only lets me write 3 lines because it does put a free line at the start of the file. Can you explain how do i solve that? or if it's just how fgets and fputs behave and i have to remember that blank line at the start. Thank you in advance. (i'll leave a file output as follows with numbers for the lines)

1)
2)it seems to work
3)dhdhdh dhdh
4)random things 
  • Try changing `scanf("%d",&x);` to `scanf("%d\n",&x);`. or `scanf("%d%c",&x);` – Fiddling Bits Aug 17 '22 at 14:58
  • 1
    @FiddlingBits `scanf("%d\n",&x);` is a terrible idea. It *might* work in this case, but in general, it tends to make things even worse. – Steve Summit Aug 17 '22 at 15:07
  • 1
    @FrancescoLucariello `scanf` and `fgets` don't mix. – Steve Summit Aug 17 '22 at 15:08
  • Whoever gave you this assignment is a sadist. This is like an elementary school science teacher giving two chemicals to students, asking them to mix them together and see what happens, and the mixture (quite predictably) blows up in their faces, and the poor students don't know why. – Steve Summit Aug 17 '22 at 15:10
  • 1
    There's a rule _ although no one ever actually teaches this rule — that you should never use `scanf` and `fgets` in the same program, because they don't play well together. Unfortunately it's not obvious how to solve the problem you've been given while using only `fgets` or only `scanf` — the problem really seems to want both. – Steve Summit Aug 17 '22 at 15:12
  • Here is a [more complete list of the unwritten rules about `scanf`](https://stackoverflow.com/questions/72178518#72178652), although (fair warning) not everyone seems to agree with me about these rules. – Steve Summit Aug 17 '22 at 15:13
  • 1
    To fix this, you need to either (a) use `scanf` instead of `fgets` to read the lines of input, or (b) use `fgets` to initially read the number of strings, then use `atoi` to convert that string` into the integer `x`, or (c) try to somehow "flush" the unwanted `\n` from the input after calling `scanf` and before calling `fgets`. My preference is (b), although I concede it's not the most obvious or the initially easiest solution. – Steve Summit Aug 17 '22 at 15:17
  • To do (a) you have to either settle for reading strings that don't contain whitespace, or you have to use `%[…]`, which carries with it a whole 'nother set of problems. And to do (c) is to descend down into a [whole hole of madness](https://stackoverflow.com/questions/18170410) from which there's [practically no escape](https://stackoverflow.com/questions/2979209). – Steve Summit Aug 17 '22 at 15:19
  • Francesco Lucariello, First call to `fgets(str, sizeof str, stdin);` read in the left-over `'\n'` from the prior `scanf("%d",&x);` call. Stop using `scanf()` until you know why it is bad. – chux - Reinstate Monica Aug 17 '22 at 15:20
  • 1
    @chux Contradictory advice: once you know how bad `scanf` is, you'll never use it! :-) – Steve Summit Aug 17 '22 at 15:24
  • @SteveSummit , `scanf()` does have its place, once its weaknesses are well understood. Something like `strncpy(), atoi(), ...`. – chux - Reinstate Monica Aug 17 '22 at 15:31
  • @FrancescoLucariello And you may now be thinking, "C is pretty useless, if it makes it this hard to solve a simple problem like this!" And I will certainly agree that `scanf`, at least, is pretty useless. But here's another angle: the problem, as stated, as pretty useless, too. No actual user of a computer program wants to have to announce, in advance, how many strings they're going to type. (When you open a blank document in Microsoft Word, does it ask you how many lines of text the document is going to contain?) – Steve Summit Aug 17 '22 at 15:33
  • Imagine stating the alternative problem, "Write a program to read lines of tet from the user, writing them to a file, and stopping when the user enters a blank line". That'd be friendlier for the user, *and* easier to write, because you wouldn't be forced into the whole `scanf` versus `fgets` quagmire. – Steve Summit Aug 17 '22 at 15:33
  • @chux We will have to agree to disagree on this. Once its weaknesses are understood, I believe that `scanf` has no place at all. Its only possible use is during one's first or second week of learning C, strictly limited to reading in simple integers, since the more proper alternatives are all a bit more involved, and perhaps more than you're ready for during that first or second week. – Steve Summit Aug 17 '22 at 15:37
  • But, during that first or second week, the instructor ought to admit that `scanf` is a temporary stopgap, full of quirks and foibles, not for long-term use. And the instructor ought not to goad students into mixing `scanf` and `fgets`, using problem statements like the one in this question. – Steve Summit Aug 17 '22 at 15:37
  • okay so i didnt know nothing about this, i am studying C for my first engineering exam and well, my professor didnt tell me how bad scanf could be and what a rabbit hole is trying to solve a problem with both scanf and fgets – Francesco Lucariello Aug 17 '22 at 15:40
  • @SteveSummit I find `char buf[1000]; if (scanf("%999[^\n]%n", buf, &len) == ...` useful for reading a _line_ (except for the `'\n'`) that may have embedded _null characters_ and knowing its length. `fgets()` is problematic to determine its length read when embedded null characters exist. I guess we will have to agree to disagree. – chux - Reinstate Monica Aug 17 '22 at 15:45
  • @chux Perhaps I have led a sheltered life. In 40 years of C programming, I have never once worried about reading a line of text that might contain null characters. :-) – Steve Summit Aug 17 '22 at 15:46
  • @SteveSummit if i want to change the for cycle and use scanf instead of a fgets i cannot use %s because it reads until it encounters a blank space. so how do i use a scanf? – Francesco Lucariello Aug 17 '22 at 15:51
  • Try changing these lines: `fgets(str, sizeof str, stdin); fputs(str,fp);` to `scanf("%s",str); fputs(str,fp); fputs("\n",fp);` or include `string.h` and replace these lines: `scanf("%s",str); strcat(str, "\n"); fputs(str,fp);` – AliMo Aug 17 '22 at 15:51
  • @SteveSummit because i tried changing with scanf but if the cycle has to be repeated 4 times i basically can write to the file 4 words, not 4 lines – Francesco Lucariello Aug 17 '22 at 15:53
  • @SteveSummit The usefulness of reading text files with potential null characters is more recent over my 46 years of coding. "Foreign" (not originating locally) text files are more common to be encountered than before. UTF16 has lots on null characters. Defensive programming (e. g. nefarious text files) oblige better detection. Simply assuming no `'\0'` does not well handle detection of these and others. – chux - Reinstate Monica Aug 17 '22 at 15:54
  • @FrancescoLucariello Quick/dirty fix: `scanf("%d",&x);` --> `scanf("%d%*c",&x);`. Better to read all with `fgets()` and drop `scanf()`. – chux - Reinstate Monica Aug 17 '22 at 15:55
  • If your back is to the wall, and you must read whole lines of text — possibly containing whitespace — using `scanf`, you can use `%[^\n]`. (This is basically my option (a) up above, modified to address the whitespace issue. Personally, I don't recommend it.) – Steve Summit Aug 17 '22 at 17:31

1 Answers1

-1

As you can tell from the comments, there are a lot of ways to approach this task. The usage of "scanf" and "fgets" can get complex especially if mixed within the same reading task. But, just to give you one option as to deriving a solution, following is a snippet of code to offer one of many possible routes.

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

int main()
{
    int x, lc=0;
    char str[101];
    FILE *fp=fopen("test.txt","w");

    if (fp==NULL)
    {
        printf("Opening failed\n");
    }
    else
    {
        printf("Opened correctly\n");
    }

    printf("Strings to write: ");
    scanf("%d",&x);

    for (int i = 0; i < x; i++)
    {
        printf("Enter string: ");
        scanf("%s", str);
        fprintf(fp, "%s\n", str);
    }
    fclose(fp);

    FILE *fr=fopen("test.txt", "r");

    while (fgets(str, 100, fr)!=NULL)
    {
        lc++;
    }
    fclose(fr);

    printf("\nThere are %d lines\n",lc);

    return 0;
}

You will note that both "scanf" and "fgets" are being used in this example, but not in reference to the same file. For user input, "scanf" is getting used. Once the file is closed and then reopened for reading, "fgets" is being used for that portion of the task.

Testing this program snippet out resulted in matching up the same quantity of lines read from the file as were entered.

@Una:~/C_Programs/Console/FileWrite/bin/Release$ ./FileWrite 
Opened correctly
Strings to write: 4
Enter string: Welcome
Enter string: to
Enter string: Stack
Enter string: Overflow

There are 4 lines

Give it a try and see if it meets the spirit of your project.

NoDakker
  • 3,390
  • 1
  • 10
  • 11