Reputation: 81
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
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
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
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