1

enter image description hereI'm trying to do the average of words per line in an array.

I was trying to do a program that reads words until EOF is entered (by ctrl + z), then it displays the amount of words that were entered (words defined by space or tab) and the average of words per sentence (sentences defined by . or \n).

This is my code, and is not working very well. Does anybody know how can I do to at least make the average part more efficient? It displays weird numbers.

#include <string.h>
#include <stdlib.h>
int main()
{
    char words[100]; int count=0, i, line=0, average;
    puts("enter words or CTRL + z");
    gets(words);
    do
    {
        for (i=0; i<strlen(words); i++)
        {
            if (words[i]== ' '|| words[i]=='\t' )
            {
                count = count + 1;
            }
            else
            {
                if (words[i]=='\n'||words[i]== '.')
                {
                    line = line + 1;
                    average = count/line;

                }
            }
        }
    }
    while (scanf("%99s",  words) != EOF);

    printf("total words = %d\n", count);
    printf("total sentences = %d\n", line);
    printf("average = %d\n", average);
    return 0;
}
N4ti
  • 11
  • 2
  • 1
    Did you compile with optimizations on, at least -O1? 1) Don't use `gets()`. It has been removed from the C language. 2) Move `strlen()` out of the loop. 3) Consecutive string literals are concatenated, make a single call to `printf()`. – Harith May 25 '23 at 18:10
  • For starters, dump `gets()` which is obsolete and is no longer part of the standard C library. Please read [Why is the gets function so dangerous that it should not be used?](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-so-dangerous-that-it-should-not-be-used) – Weather Vane May 25 '23 at 18:11
  • 1
    Why are you worried about optimizations when you haven't been able to debug the program yet? *It doesn't work* is unhelpful. What was the input given and output received? Did it successfully compile? How does it not work? – Harith May 25 '23 at 18:16
  • `char average` ==> `int average = 0;` – Weather Vane May 25 '23 at 18:16
  • You are also mixing `gets` with `scanf`. What is that supposed to do? Please show the input, the desired output, and the actual output. – Weather Vane May 25 '23 at 18:18
  • 1
    @Weather There's a `;` after `char words[100];`. But you're correct that `average` is uninitialized. – Harith May 25 '23 at 18:18
  • 1
    @Haris a good example why not to cram two statements on one line. – Weather Vane May 25 '23 at 18:20
  • `scanf("%99s", words)` stops scanning at a white space. So it reads a single word. But it looks like you are trying to read an entire line. Did you want `fgets` instead? – 001 May 25 '23 at 18:29
  • @WeatherVane thanks about the gets()! I'll get more informed about that. The desired output and the actual output only differ in the average part but I think I'll first implement all of you advices and made it better from scratch. Thank you so much! – N4ti May 25 '23 at 19:11
  • @Haris yeah you're right i'm focused on something that fails from the origin. Thanks for your helpful advice! – N4ti May 25 '23 at 19:12
  • As [mentioned](https://stackoverflow.com/questions/76335120/how-can-i-improve-efficiency-in-my-c-code-for-calculating-the-average-of-words-p?noredirect=1#comment134609228_76335120) please add a short example input text, expected output and actual output. – Weather Vane May 25 '23 at 19:15
  • You can delay computing the average until the end, right before printing it. Also, you do not account for successive whitespace characters. Words at the end of a line are not being counted. – Emilio Silva May 25 '23 at 19:25
  • _Side note:_ `i < strlen(words)` scans the line in quadratic (O(n^2)) time. Change to: `str[i] != 0` and it scans in linear (O(n)) time. Note that, in C, `strlen` must _rescan_ the string (looking for the 0x00 terminator). It's _not_ a "property" of a "string" like some other languages (e.g. `python`, `perl`, `java`, etc.). – Craig Estey May 25 '23 at 19:27
  • @Craig My suggestion was to move `strlen()` out of the loop and call it just once. But that's still quite inefficient. `str[i]` is nice indeed. – Harith May 25 '23 at 19:32

2 Answers2

1

This is my code, and is not working very well.

Changes needed

Do not use gets()

Use fgets() to read a line.

scanf("%99s", words) fails to read a line

Suggest

while (fgets(words, sizeof words, stdin)) {
  ...
}

Test first for '\n' to count lines

Use isspace() to test for all white-space

Consider floating point for average

Only needed after reading in all data.

double average = 1.0*count/line;

Enable all compiler warnings


Likely other issues too.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

Never, ever use gets(). It's accessible to you at all only because C library purveyors prefer to avoid breaking legacy programs that rely on it. Prefer fgets() or [f]scanf. Or in some cases, fread might be as good.

You are counting words as the number of word separators. This will ordinarily result in a count that is one too few, because it effectively ignores the first word. In your particular case, it will also miss counting if there is a sentence separator that is not accompanied by a word separator. I suggest instead counting transitions from non-word to word, where non-word includes not only word separators, but also sentence separators. That would also fix the problem of mis-counting words if multiple separators appear in a row.

Your scanf() call will ignore leading whitespace, and stop reading at the first whitespace it finds afterward. This will totally screw up your word counts for multiline inputs, and your sentence counts as well (since newlines are whitespace and therefore will be ignored). fgets() would be a better choice.

You don't need to update the average every time you meet a new line, and that will give you the wrong result in the event that the input ends in the middle of a line. You should probably just wait until the whole input has been read, then compute the average.

You are recalculating the length of the input string every time you test i < strlen(words). Unless your compiler happens to optimize that for you, it is dreadfully inefficient. Instead, either pre-compute the length, once per line, or test instead whether you have reached the string terminator (for example, words[i] != '\0').

Your sentence count will be wrong (arguably) if a newline is entered after a period -- it will count two sentences, one with zero words. You could consider detecting this and correcting it, but maybe the grader will assume that you should not.

An if / else tree with all the conditions testing for specific values of the same object is probably a tiny bit less efficient than a switch statement. IMO, the switch is easier to read, too.

You say you want to compute the average number of words per line but you are actually computing the number per sentence. Your specifications say that you are to counting line breaks as sentence breaks, but as far as I can see, they don't say the reverse, and that would not be a natural reading.

You don't need stdlib.h for what you show. Nor string.h if you take out the strlen() call. You do, however, need stdio.h, and you're not including it.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157