make a program that count lines in a text

B

Ben Bacarisse

Malcolm McLean said:
You need to start thinking in more of a structured programming way.

Counting the lines in a text file is a natural unit. Many programs
might need to do this. The name of the file isn't important to the
function.

so

int countlines(char *filename)
{
/* code goes here */
}

A small point... On many systems it helps to separate the opening from
the processing. I.e. I'd almost always provide

int countlines(FILE *);

and then (if it seems helpful) layer your prototype on top of that.

The reason is that on Unix-like systems it is normal to process stdin as
a default so a function that operates on a FILE * is often the best
starting point.

<snip>
 
B

Ben Bacarisse

Rui Maciel said:
<snip/>

Just as a side note, if you happen to use a unix-like operating system
then you already have a program that does that, which is wc (short for
word count).

Later on (in the program text) the OP suggested that they want to ignore
blank lines. If blanks are indeed to be ignored, then a simple fscanf
solution presents itself:

int count_nbl(FILE *fp)
{
int count = 0;
char nbl[2];
while (fscanf(fp, "%1s%*[^\n]", nbl) == 1)
++count;
return count;
}

<snip>
 
N

Nick Keighley

Subject: "make a program that count lines in a text"



#include <stdio.h>

int main(void)
{
long unsigned boot_count = 0;
char *fn = "log.txt";
FILE *fp = fopen(fn,"r");

if (fp != NULL) {
int rc;

do{
rc = getc(fp);
if (rc == '.') {

why are you counting dots? What if someone changes the file format?

Tue 08-03-2010 4:27:21.81p...
Wed 08-04-2010 4:19:29.12p...


Thu 08-05-2010 6:17:31.65p...
Fri 08-06-2010 3:10:21.59p...
Fri 08-06-2010 4:42:51.31p...
 
N

Nick Keighley

I dont know about C...just stooped here to search somthing and find
your post. (L)Unix has the exact command wc. the source code is also
available. so you may search for it

how do you know this is going to run on a Unix?
 
B

BartC

Malcolm McLean said:
You need to start thinking in more of a structured programming way.

Counting the lines in a text file is a natural unit. Many programs
might need to do this. The name of the file isn't important to the
function.

so

int countlines(char *filename)
{
/* code goes here */
}

I think the OP wanted to ignore blank lines. Then a readline() function is
better.
 
M

Malcolm McLean

I think the OP wanted to ignore blank lines. Then a readline() function is
better.

This is the full function. You can write it far more tersely, but
generally it's better to break things down and make them clear.


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

int blankstr(const char *str);
int fcountlines(FILE *fp);
int countlines(char *filename);

/*
is a string empty or composed entirely of blanks?
Params: str - the string
Returns: 1 if balnk, else 0
*/
int blankstr(const char *str)
{
while(*str)
{
if(!isspace( (unsigned) *str) )
return 0;
str++;
}
return 1;
}

/*
count the remaining lines in an open file
Params: fp - pointer to file
Returns: number of non-blank lines, -1 on read error
*/
int fcountlines(FILE *fp)
{
char buff[1024];
int graphflag = 0;
int answer = 0;

while(fgets(buff, 1024, fp))
{
if(!strchr(buff, '\n'))
{
if(!blankstr(buff))
graphflag = 1;
continue;
}
if(!blankstr(buff) || graphflag)
answer++;
graphflag = 0;
}
if(ferror(fp))
return -1;
return answer;
}

/*
count the non-blank lines in a file
Params: filename - path to file
Returns: number of non-blank lines
*/
int countlines(char *filename)
{
FILE *fp;
int answer;

fp = fopen(filename, "r");
if(!fp)
return -1;
answer = fcountlines(fp);
fclose(fp);

return answer;
}

#include <stdlib.h>

int main(int argc, char **argv)
{
int Nlines;

Nlines = countlines(argv[1]);
if(Nlines < 0)
{
fprintf(stderr, "Error opening or reading file\n");
exit(EXIT_FAILURE);
}
printf("%d non-blank lines\n", Nlines);
return 0;
}
 
G

Geoff

Any suggestion would be nice!

After considering all the input and discussion about command line
arguments, error returns, infinite loops, proper prototypes and
headers, I think this might be an acceptable minimal implementation of
your goal.

bootcount.c

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

#define BUF_LEN 96 /* max expected length of a line */

/*
Is a string empty or composed entirely of blanks?
Params: str - the string
Returns: 1 if blank, else 0
*/
int blankstr(const char *str)
{
while(*str)
{
if(!isspace( (unsigned) *str) )
return 0;
str++;
}
return 1;
}

/*
Open a log file for read, count the number of non-blank
lines and output the result.
Params: The name of the log file from the command line
Returns: 0 on success, otherwise EXIT_FAILURE
*/
int main(int argc, char *argv[])
{
FILE *f;
int line_count = 0;
char bfr[BUF_LEN];

if(argc != 2) {
puts("Usage: bootcount [logfilename]");
return EXIT_FAILURE;
}

if ((f = fopen (argv[1],"r")) == NULL)
return EXIT_FAILURE;

while (fgets(bfr, BUF_LEN, f) != NULL) {
if(!blankstr(bfr))
line_count++;
}
printf("Welcome, the system has been booted %d times!\n",
line_count);
fclose(f);
return 0;
}
 
B

Ben Bacarisse

Malcolm McLean said:
#include <ctype.h>
/*
is a string empty or composed entirely of blanks?
Params: str - the string
Returns: 1 if balnk, else 0
*/
int blankstr(const char *str)
{
while(*str)
{
if(!isspace( (unsigned) *str) )

Did you mean (unsigned char)*str? The cast to unsigned just adds a
problem rather than solving one.
return 0;
str++;
}
return 1;
}

<snip>
 
B

Ben Bacarisse

Geoff said:
After considering all the input and discussion about command line
arguments, error returns, infinite loops, proper prototypes and
headers, I think this might be an acceptable minimal implementation of
your goal.

I have no problem with your strategy, but my fscanf solution is a couple
of lines and needs no line buffer. I don't see how using an extra
function and a line buffer is minimal!
bootcount.c

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

#define BUF_LEN 96 /* max expected length of a line */

/*
Is a string empty or composed entirely of blanks?
Params: str - the string
Returns: 1 if blank, else 0
*/
int blankstr(const char *str)
{
while(*str)
{
if(!isspace( (unsigned) *str) )

(unsigned char) please!
return 0;
str++;
}
return 1;
}

<snip>
 
R

Ralf Damaschke

Malcolm said:
int countlines(char *filename)
{ [...]
fp = fopen(filename, "r"); [...]
[...]
int main(int argc, char **argv)
{ [...]
Nlines = countlines(argv[1]); [...]
}

You know that passing what might be a null pointer to fopen()
results in undefined behavior, don't you?

-- Ralf
 
G

Geoff

I have no problem with your strategy, but my fscanf solution is a couple
of lines and needs no line buffer. I don't see how using an extra
function and a line buffer is minimal!

Well, I rather missed your original post on the fscanf technique,
sorry . My use of minimal wasn't meant to mean smallest, fastest or
most economical but to minimally meet the stated requirements.
 
N

Nick Keighley

Well, I rather missed your original post on the fscanf technique,
sorry . My use of minimal wasn't meant to mean smallest, fastest or
most economical but to minimally meet the stated requirements.

an fscanf() is unlikely to be faster and massivly less clear. I prefer
programming in C to programming in scanf() directives.

--

The fscanf equivalent of fgets is so simple
that it can be used inline whenever needed:-
char s[NN + 1] = "", c;
int rc = fscanf(fp, "%NN[^\n]%1[\n]", s, &c);
if (rc == 1) fscanf("%*[^\n]%*c);
if (rc == 0) getc(fp);
 
M

Malcolm McLean

Did you mean (unsigned char)*str?  The cast to unsigned just adds a
problem rather than solving one.
Yes, it should be unsigned char.

The problem with bugs like that is that they don't show up on testing,
unless you can toggle the compiler between signed and unsigned chars
and enter non-Ascii text.
 
G

Geoff

an fscanf() is unlikely to be faster and massivly less clear. I prefer
programming in C to programming in scanf() directives.

For a novice it is definitely not clear what fscanf is doing but using
it here does "simplify" the structure of the overall program
considerably.

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

/*
Open a log file for reading, count the number of non-blank
lines and output the result.
Params: The name of the log file from the command line
Returns: 0 on success, otherwise EXIT_FAILURE
*/
int main(int argc, char *argv[])
{
FILE *fp;
unsigned long line_count = 0;
char bfr[2];

if(argc != 2)
{
puts("Usage: bootcount [logfilename]");
return EXIT_FAILURE;
}

if ((fp = fopen (argv[1],"r")) == NULL)
return EXIT_FAILURE;

/* count the lines of text, skipping blank lines */
while (fscanf(fp, "%1s%*[^\n]", bfr) == 1)
line_count++;

printf("Welcome, the system has been booted %d times!\n",
line_count);

fclose(fp);
return 0;
}
 
B

Ben Bacarisse

Malcolm McLean said:
Yes, it should be unsigned char.

The problem with bugs like that is that they don't show up on testing,
unless you can toggle the compiler between signed and unsigned chars
and enter non-Ascii text.

They may still not show up because there are so many buggy uses of the
isXXXX functions that every implementation I've seen (that's only three,
so you may decide that's not significant) guards against it.
 
I

Ian Collins

On 08/18/10 09:53 AM, Ike Naar wrote:

<snip>

Why do your posts always show up a day late?
 
M

Malcolm McLean

They may still not show up because there are so many buggy uses of the
isXXXX functions that every implementation I've seen (that's only three,
so you may decide that's not significant) guards against it.
I suppressed the issue in Basic Algorithms. The code just passes a
plain char to the isXXXX functions. I decided that to deal with the
matter would distract from the purpose of the book.
 
B

Ben Bacarisse

Malcolm McLean said:
I suppressed the issue in Basic Algorithms. The code just passes a
plain char to the isXXXX functions. I decided that to deal with the
matter would distract from the purpose of the book.

That's one reason that I would not want to explain algorithms using C --
there are too many details that distract from the big picture.

If wanted to keep the code clean (after explaining this issue) I'd use:

#define is(class, c) is##class((unsigned char)(c))

E.g. while (is(alpha, *sp)) sp += 1;
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top