0

So I am trying to print the number of lines in a file. The catch is that I cannot use fgets. Is there any other alternative to this which involves only fscanf? I tried running the following:

while (1) {
  ret = fscanf(fin, "%[^\n]", string);
  if (ret == EOF) break;
  line_count++;
}

But it does not work. It is giving me an infinite loop. Can anyone please tell me what the issue is ?

  • 1
    Does this answer your question? [How to read from input until newline is found using scanf()?](https://stackoverflow.com/questions/8097620/how-to-read-from-input-until-newline-is-found-using-scanf) – kaylum Feb 02 '20 at 04:37
  • Unfortunately it doesn't. Could you point out the flaw in my program? –  Feb 02 '20 at 04:47
  • You are scanning for everything except newline. Which means the scan gets stuck on the newline. The linked answer tells you how to scan for a line including the newline. You just need to adapt that to count the lines and do it in a loop. – kaylum Feb 02 '20 at 04:49
  • 1
    Why would you use anything but `fgetc`? `scanf` is not the right tool for this. – William Pursell Feb 02 '20 at 05:32

2 Answers2

0

this statement:

ret = fscanf(fin, "%[^\n]", string);

should be:

ret = fscanf(fin, " %[^\n]", string);

Notice the leading space, which will consume any leading 'white space'

I agree with the comments about newlines. Therefore, suggest:

size_t lineCount = 0;
int ch;
while( ( ch = getchar() ) != EOF )
{
    if( '\n' == ch )
    {
        lineCount++;
    }
}
user3629249
  • 16,402
  • 1
  • 16
  • 17
  • 1
    It will consume consecutive newlines which defeats the objective of counting them. – Jonathan Leffler Feb 02 '20 at 07:00
  • 1
    You should also save the last character read before `EOF` (e.g. `int ch, last;`) as the last command in your `while` set `last = ch;`, then at the end check `if (last != '\n') lineCount++;` to handle the case where the file doesn't contain a POSIX line-ending on the final line. (**note:** `ch` and `last` should be type `int` to detect `EOF`) – David C. Rankin Feb 03 '20 at 02:03
0

You must also be aware of a corner-case not covered by the other answers. For a file to be POSIX compliant, the final character must be a '\n' character, or the file may be empty to begin with. For example, a two line file that contains a POSIX end-of-file could be:

my dog has fleas\n
my cat has none\n

If you were counting lines in the file above by counting the number of '\n' characters, your number of lines for the file would be as expected -- 2. However, there are many editors today, that do no create POSIX compliant files because they fail to write the final '\n' after the last line of text. So many editors would leave the file as:

my dog has fleas\n
my cat has none

It is a perfectly legal way to save the file, it is just a non-POSIX way of doing so. Now, what would your program output if you were simply counting the '\n' characters to determine the lines in the file? (1 -- you would count one-too-few lines)

To address the issue of a non-POSIX eof to ensure your line count is correct either way, you need to check the last character before EOF and see if it was a '\n', and if it wasn't you need to add +1 to your line count.

You can do that fairly simply just by saving the current character at the end of you loop so that it is preserved for comparison after EOF is encountered. You can do something like the following:

/* count lines in file from open file-stream.
 * returns number of lines in file, including files
 * with non-POSIX end-of-file.
 */
size_t linecount (FILE *fp)
{
    int c, last = 0;                    /* current and last char read */
    size_t nlines = 0;                  /* line counter */
    
    rewind(fp);                         /* prevent UB if EOF already set on fp */
    
    while ((c = fgetc(fp)) != EOF) {    /* loop reading each char */
        if (c == '\n')                  /* if '\n' increment line counter */
            nlines++;
        last = c;                       /* save current as last */
    }
    /* if not empty-file and not POSIX eof, add 1 to nlines */
    return last && last != '\n' ? nlines + 1 : nlines;
}

The loop can also be written as:

    do {
        last = c;
        if ((c = fgetc(fp)) == '\n')
            nlines++;
    } while (c != EOF);

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85