-2

This is my code here, I'm trying to create a programme that counts characters using functions and then determine the average value of characters when an empty line is encountered. The programme is suppose to allow the user to enter multiple lines until an empty line is encountered but I can't seem to.

#include <stdio.h>
#include <Windows.h>

int main()
{
    char str[1000];
    int Digits, Char, SpecialChar, linecount = 0;
    int counter;
    int total;
    int average;

    Digits = Char = SpecialChar = 0;

    printf("Please type in your words here: ");
    gets(str);

    for (counter = 0; str[counter] != NULL; counter++)
    {

        if (str[counter] >= '0' && str[counter] <= '9')
            Digits++;
        else if ((str[counter] >= 'A' && str[counter] <= 'Z') || (str[counter] >= 'a' && str[counter] <= 'z'))
            Char++;
        else
            SpecialChar++;    
    }

    while (str[counter] != '\n')
    {
        if (str[counter] = '\n')
        {
            linecount ++;
        }
    }

    total = Digits + Char + SpecialChar;
    average = total / linecount;

    printf("\nDigits: %d \nCharacters: %d \nSpecial Characters: %d \nLine: %d", Digits, Char, SpecialChar, linecount);
    printf("\nTotal no. of characters = %d", total);
    printf("\nAverage no. of characters = %d", average);

    Sleep(5000000);
    return 0;
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 2
    First, don't use `gets()`. Ever. [This unsafe function](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-so-dangerous-that-it-should-not-be-used) was deprecated in C99 and completely removed from the Standard with C11. Use `fgets()` instead. Note that `fgets()` keeps the newline, so you may need to remove this. An empty line will contain a newline character in the first position. (There may be other ways to distinguish a line as empty, e.g., only whitespace characters, etc.) – ad absurdum Jul 22 '17 at 23:26
  • 1
    You call `gets()` once. Calling `gets()` is dangerous, as already pointed out. But to get more than one line of input, you'll need to loop. I'm guessing you should be looping and accumulating data until you encounter a blank line, whereupon you report the accumulated statistics and zero the statistics (if there are any statistics accumulated since the last blank line, or the start of the file). You stop the loop altogether on EOF (reporting accumulated statistics). Since you need to report the statistics in a couple of places, that code should become a function that you call to do the job. – Jonathan Leffler Jul 23 '17 at 00:20
  • Is not `while (str[counter] != '\n') { if (str[counter] = '\n') { linecount ++; } }` an infinite loop, once entered? – chux - Reinstate Monica Jul 23 '17 at 10:36
  • @chux-- with `if (str[counter] = '\n')`, this code assigns `\n` to `str[counter]`, then terminates. Infinite loop if instead `if (str[counter] == '\n')`. I have added a discussion in my answer. – ad absurdum Jul 23 '17 at 11:38
  • The variable name `Char` should not be used. It can lead to confusion and errors – Ed Heal Jul 23 '17 at 11:39

3 Answers3

0

As far as I know, the function gets is interrupted after "\n". Also, using fgets you will have to put an attention on the '\0' addition on the string. That means that

The programme is suppose to allow the user to enter multiple lines until an empty line is encountered but I can't seem to.

will never be able to be accomplished using gets this way. Because gets is not a recommended function, I edited you code a little bit in a way that you might be searching.

Something to metion, that I found out it might be a logic error, before you read this code

for (counter = 0; str[counter] != NULL; counter++)

This seems strange, because the fgets will always record the "\n" character. So, the next condition

if (str[counter] = '\n')

will never be true

I see some others errors on you code, but, not majors ones. So, I see the suggestion as a sufficient as appointing them

while (fgets(str, 1000, stdin) && str[0] != '\n'){ //I dont know if checking the first element of the string is redundancy, 
//because, the I think the fgets function will return NULL if you just press enter, as the first character

    for (counter = 0; str[counter] != '\n'; counter++{

        if (str[counter] >= '0' && str[counter] <= '9')
            Digits++;
        else if ((str[counter] >= 'A' && str[counter] <= 'Z') || (str[counter] >= 'a' && str[counter] <= 'z'))
            Char++;
        else
            SpecialChar++;    
    }

    linecount ++;  //new line is someting that you will always reach, so, 
                  //there is no reason for any condition

}
theBotelho
  • 324
  • 1
  • 3
  • 12
0

Solution is below:

#include <stdio.h>
#include <Windows.h>

int main()
{
    char str[1000];
    int Digits, Char, SpecialChar, linecount = 0;
    int counter;
    int total;
    int average;
    int flag = 1;

    Digits = Char = SpecialChar = 0;

    printf("Please type in your words here: ");

    while(flag == 1)
    {
        gets(str);
        if (str[0] == NULL || str[0] == '\n') 
            {
                flag = 0;
                break;
            }
        for (counter = 0; str[counter] != NULL; counter++)
        {

            if (str[counter] >= '0' && str[counter] <= '9')
                Digits++;
            else if ((str[counter] >= 'A' && str[counter] <= 'Z') || (str[counter] >= 'a' && str[counter] <= 'z'))
                Char++;
            else
                SpecialChar++;   
        }
        linecount ++;
    }




    total = Digits + Char + SpecialChar;
    average = total / linecount;

    printf("\nDigits: %d \nCharacters: %d \nSpecial Characters: %d \nLine: %d", Digits, Char, SpecialChar, linecount);
    printf("\nTotal no. of characters = %d", total);
    printf("\nAverage no. of characters = %d", average);

    Sleep(5000000);
    return 0;
}
Sakir Sherasiya
  • 1,562
  • 1
  • 17
  • 31
0

The gets() function is unsafe and extremely susceptible to buffer overflows. Never use this function. A better alternative is fgets(), though the non-standard (but widely available) POSIX getline() function is also a good option.

Both fgets() and gets() fetch a line of input, but gets() discards the newline character, while fgets() keeps it and stores it in the input buffer (if there is room). This means that you may need to remove the newline character after fetching a line of input with fgets(). It also means that when the user simply presses ENTER, the input buffer contains only a \n character, followed by a \0 null terminator; fgets() always null-terminates the input buffer.

To read multiple lines of user input, stopping only when the user presses ENTER, fgets() should be called in a loop. One way of doing this is to use a for(;;) {} loop construction, which never terminates (equivalent to while(1) {}). If the first character in a line of input is a \n character, then a break; statement exits the loop.

Notes on the Posted Code

The posted code is comparing input characters with character constants to determine whether the input is numeric, alphabetic, or otherwise. This is better, and more portably, accomplished by using Standard Library functions from ctype.h. Using library functions here means that the code does not need to explicitly consider the current locale or character encoding (which may not be ASCII or UTF-8).

The posted code contains the line:

for (counter = 0; str[counter] != NULL; counter++) {}

Note that NULL is the null pointer constant, equivalent to (void *) 0, but not equivalent to 0. The goal of this loop appears to be to iterate over a string, terminating when the null terminator (\0) is reached. So, the controlling expression should be changed:

for (counter = 0; str[counter] != '\0'; counter++) {}

Also, the purpose of this loop in the posted code is unclear:

while (str[counter] != '\n')
{
    if (str[counter] = '\n')
    {
        linecount ++;
    }
}

If str[counter] is a newline character, then the loop is not entered; otherwise the if statement in the loop body assigns '\n' to str[counter], evaluating to true and incrementing linecount. On the next iteration str[counter] == '\n', so the loop is terminated. After the previous loop (with NULL changed to '\0' in the controlling expression), counter is the index of the \0 character in str, so this loop replaces the null terminator with a newline character, making str a string no longer. This will lead to undefined behavior if the code later attempts to treat str as a string.

If the line if (str[counter] = '\n') is a typo, meant to be if (str[counter] == '\n'), then this is an infinite loop once entered.

An Example Program

Here is a heavily modified of the posted code that uses fgets() to get user input, and Standard Library functions to classify input characters.

The fgets() function returns a null pointer in the event of an error, so this is checked for and handled in about the simplest way possible. After input has been stored in the str[] buffer array, the first character is checked; if it is \n, then the user entered an empty line (probably: see the next paragraph), and the loop is terminated. Otherwise, the next step is to see if the input line contains a newline character at all. The strchr() (from string.h) function is used here for this. If the \n is not found, then a null pointer is returned, otherwise a pointer to the \n character is returned. This is used to write over the \n with \0, effectively removing the newline character. Then linecount is incremented. Thus, the line counter is incremented only when a newline character is encountered in the input.

Note that when input is too large for the input buffer, at least the newline character will remain in the input stream waiting for the next I/O function call. It is possible that only the newline character remains in the input stream, so on the next loop iteration the first character in the buffer is \n, interpreted by this program as an empty line. If there is a possibility that input will be larger than the buffer allocation, more subtlety will be required to handle this situation. One solution is to use a flag to indicate whether the start of a line is being read. Here, line_start is initialized to 1, set to 1 whenever linecount is incremented, and set to 0 whenever a newline character is not found in the input buffer. In order for a newline to indicate an empty line of input, line_start must be set to 1, and the first character in the input buffer must be a \n character. With this modification, the program will reliably read lines of input even longer than the allocated 1000 characters. You can test this out by making the allocation for str[] smaller; try char str[2];.

Here is the complete program:

#include <stdio.h>
#include <ctype.h>            // for isdigit(), isalpha()
#include <string.h>           // for strchr()

int main(void)
{
    char str[1000];
    int Digits = 0;
    int Char = 0;
    int SpecialChar = 0;
    int linecount = 0;
    int counter;
    int total;
    int average;

    puts("Please type in your words here:");

    int line_start = 1;
    for (;;) {
        if (fgets(str, sizeof str, stdin) == NULL) {
            /* Handle error */
            fprintf(stdin, "I/O error\n");
            return 1;
        }

        /* Terminate loop on empty line */
        if (line_start && str[0] == '\n') {
            break;
        }
    
        /* If newline present, remove and increment linecount */
        char *cptr = strchr(str, '\n');
        if (cptr != NULL) {
            *cptr = '\0';
            ++linecount;
            line_start = 1;
        } else {
            line_start = 0;         // complete line not read
        }

        /* update character counters */
        for (counter = 0; str[counter] != '\0'; counter++) {
            unsigned char uc = str[counter];
            if (isdigit(uc)) {
                Digits++;
            } else if (isalpha(uc)) {
                Char++;
            } else {
                SpecialChar++;
            }
        }
    }

    total = Digits + Char + SpecialChar;
    average = total / linecount;  // integer division

    printf("Digits: %d \nCharacters: %d \nSpecial Characters: %d \nLine: %d\n",
           Digits, Char, SpecialChar, linecount);
    printf("Total no. of characters = %d\n", total);
    printf("Average no. of characters = %d\n", average);

    return 0;
}
Community
  • 1
  • 1
ad absurdum
  • 19,498
  • 5
  • 37
  • 60