Zora
Zora

Reputation: 81

Number of Lines Mystery?

My goal is to implement a function that calculates the number of lines in a file. And empty file is considered to have no lines. If the last line of the given file is not empty, it should be counted as a line despite not ending with a newline character.

I've come up with the following code:

   int linecount(const char *filename)
{
    FILE *f = fopen(filename, "r");
    if(!f)
        return -1;
    int lines = 0;
    int c = 0;
    int n = 0;
    while((c = fgetc(f)) != EOF){
        if(c == '\n')
            lines++;
        n++;
    }
    if(n==0)
        return 0; //return 0 if the file is empty
    if(c!='\n' && !isspace(c))
        lines++; //count the last line if it's not empty
    fclose(f);
    return lines;
}

However, even after playing with it for over an hour I can't figure out why its return value lines is one too large in some cases...

Upvotes: 2

Views: 86

Answers (3)

Yasir Majeed
Yasir Majeed

Reputation: 741

A simple solution can be

int linecount(const char *filename)
{
    FILE *stream;
    char *line = NULL;
    size_t len = 0;
    ssize_t read;
    int numOfLines = 0;

    stream = fopen(filename, "r");
    if (stream == NULL)
         exit(EXIT_FAILURE);

     while ((read = getline(&line, &len, stream)) != -1) {
         numOfLines++;
     }
    free(line);
    fclose(stream);
    return numOfLines;
}

Upvotes: 1

gsamaras
gsamaras

Reputation: 73366

You were close, here how you could do it:

int linecount(const char *filename) {
  FILE *f = fopen(filename, "r");
  if (!f)
    return -1;
  int lines = 0;
  int c = 0;
  int n = 0;
  int read_line = 0;
  while ((c = fgetc(f)) != EOF) {
    read_line = 1;
    if (c == '\n') {
      lines++;
      read_line = 0;
    }
    n++;
  }
  if (n == 0)
    return 0;  //return 0 if the file is empty
  if(read_line)
    lines++;
  fclose(f);
  return lines;
}

The idea is that we want to know if we started reading a line AND if we met a newline, at end of this line. So, we use another variable, called read_line and we use it as a flag.

We set it to 1 (true) if we just started reading a line and we set it to 0 (false) if we just met a newline (end of the line).

Now, if we have something like:

1[newline]
2[newline]
3

we will be OK, since we need to check if read_line after we read the file. Is so, we have to increment our line counter by one.

This is also OK:

1[newline]
2[newline]
3[newline]

since we saw three newlines and the read_line is 0 after we read the file.

Same goes for this case:

1[newline]
2[newline]
3[newline]
[nothing here]

since our flag is going to be equal to 0 after reading the file, since the 3rd newline should set it to 0 and we never actually enter the 4th line in our loop, since there is nothing to read.

With your previous implementation, as stated in the comments, this line:

if(c!='\n' && !isspace(c))

would be executed with c being equal to EOF.


Or you could just use fgets() and you are done. Check the example:

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

#define bufSize 1024

int main(int argc, char *argv[])
{
  FILE *fp;
  char buf[bufSize];

  if ((fp = fopen("test.txt", "rb")) == NULL)
  { /* Open source file. */
    perror("fopen source-file");
    return 1;
  }
  int lines = 0;
  while (fgets(buf, sizeof(buf), fp) != NULL)
  { /* While we don't reach the end of source. */
    /* Read characters from source file to fill buffer. */
    /* fgets will stop when it finds a newline. */
    lines++;
  }
  printf("lines = %d\n", lines);
  fclose(fp);
  return 0;
}

Upvotes: 3

BLUEPIXY
BLUEPIXY

Reputation: 40145

Modify sample

int linecount(const char *filename)
{
    FILE *f = fopen(filename, "r");
    if(!f)
        return -1;
    int lines = 0;
    int c = 0;
    int flag = 1;
    while((c = fgetc(f)) != EOF){
        if(flag = (c == '\n'))
            lines++;
    }
    if(!flag)
        lines++; //count the last line if it's not empty
    fclose(f);
    return lines;
}

Upvotes: 2

Related Questions