0

I'm trying to convert a while loop that counts characters, words, and lines in a string, into a do-while loop.

Here is my while loop:

#include <stdio.h>
#include <string>
#include <typeinfo>
using namespace std;

int main()
{
    int c;
    int characters = 0;
    int words = 1;
    int newlines = 0;
    printf("Input a string.  Press enter, then ctrl+Z, then enter once more to end string.\n");

    while ((c = getchar()) != EOF)
    {
        if (c >= 'a' && c <= 'z' || c>= 'A' && c<= 'Z')
            characters++;
        else if (c == ' ')
            words++;
        else if (c == '\n')
            newlines++;
    }

    printf("The number of characters is %d\n", characters);
    printf("The number of words is %d\n", words);
    printf("The number of newlines is %d\n", newlines);


    return 0;
}

I've been trying for hours to repeat the above process using a do-while loop, but to no avail.

Here is what I have so far:

#include <stdio.h>
#include <string>
#include <typeinfo>
using namespace std;  

int main()
{
    int c;
    int characters = 0;
    int words = 0;
    int newlines = 0;
    printf("Input a string.  Press enter, then ctrl+Z, then enter once more to end string.\n");
    
    do 
    {
        c = getchar();
        if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
            characters++;
        else if (c == ' ')
            words++;
        else if (c == '\n')
            newlines++;
    } while (c = getchar() != EOF);
        

    printf("The number of characters is %d\n", characters);
    printf("The number of words is %d\n", words);
    printf("The number of newlines is %d\n", newlines);

    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
CPKEN
  • 11
  • 3
  • 2
    With the `do/while` you're skipping every other character. Count the `getchar` calls and the logic that follows each. – Retired Ninja May 18 '21 at 22:17
  • 1
    [Why is “using namespace std;” considered bad practice?](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) – Gary Strivin' May 18 '21 at 22:21
  • BTW with an empty string the word count would be 1. Additionally, in a string with multiple adjacent spaces the count will not be correct. – Gary Strivin' May 18 '21 at 22:26
  • Thank you all for the responses. I've learned a ton from this community but this was my very first post. I'm glad I suck cause that just means I get to learn more from you all ;) – CPKEN May 19 '21 at 13:35
  • It's also good to know when you might suck. [It means you don't REALLY suck](https://en.wikipedia.org/wiki/Dunning%E2%80%93Kruger_effect). – user4581301 May 19 '21 at 23:37

3 Answers3

3

There are two main problems with the posted do-while loop.

The first one is that you are reading two characters but processing only character in each iteration of the loop.

The second one is that while (c = getchar() != EOF) does not do what you are hoping it would do. Due to operator precedence, that is equivalent to while (c = (getchar() != EOF)).

do 
{
    c = getchar(); // OK the first time, not after that.
    if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
        characters++;
    else if (c == ' ')
        words++;
    else if (c == '\n')
        newlines++;
} while (c = getchar() != EOF); // This is bad.

Even if you fix the second problem by using

while ((c = getchar()) != EOF);

it's stil not good because that line is good for detecting EOF but the character gets ignored for further processing.


You'll have to change the do-while loop to:

do 
{
    c = getchar();
    if ( c == EOF )
    {
        break;
    }

    if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
        characters++;
    else if (c == ' ')
        words++;
    else if (c == '\n')
        newlines++;
} while (true);

As you can see, it is not an improvement on the while loop. It is worse than the while loop from a cleanliness point of view.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
2

As others have mentioned, the situation you have described is far better suited to using a normal while loop, rather than a do ... while. However, that fact notwithstanding, and without addressing other issues in your code (such as those mentioned in the comments), you can change many such loops simply by giving the 'control variable' an initial, 'dummy' value that has no effect inside the loop. In your case, a value of zero for c is hardly likely to ever be an actual input value, so you can use that:

int c = 0; // Give c an initial (non)value.
do 
{
//  c = getchar(); // Remove this line, as it makes two 'reads' per loop!
    if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
        characters++;
    else if (c == ' ')
        words++;
    else if (c == '\n')
        newlines++;
} while ((c = getchar()) != EOF); // Note the added parentheses!!!

But note, all this really achieves is adding one extra run through the loop, with no purpose.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • Or you 'initialize' the `c` variable with a read: `int c = getchar();` ... but that adds an extra call to the code and, IMHO, makes it look dirtier. – Adrian Mole May 18 '21 at 23:10
1

Problem:

The thing you're attempting to do is completely antinatural and makes no sense. The situation you show should use a while loop and not just any loop.

Solution:

Technically you can still do this, but is very bad style:

    do
    {
        bool do_end = (c = getchar()) != EOF;
        if(do_end)
            break;
        if ((c >= 'a' && c <= 'z') || (c>= 'A' && c<= 'Z'))
            characters++;
        else if (c == ' ')
            words++;
        else if (c == '\n')
            newlines++;
    } while (true);

The while loop is much better in this case, so don't use do while unless you actually need.

Additionally, consider that this solves the problem you asked for, but your code itself will have problem counting the amount of words if there are adjacent spaces or if the string is empty.

Additional information:

  1. using namespace std; is considered a bad practice and here you don't even need it because you're not using anything in the std namespace.
  2. You are including <string> and <typeinfo> but not using any of the both.
Gary Strivin'
  • 908
  • 7
  • 20
  • Thank you, Gary. Just through the process of implementing a do-while loop, I can tell it is far more inefficient than a while loop. I'm new to C++ and trying to work through various flow controls and get a better understanding of how they work. I am now trying to solve the same problem using a switch function. Thanks for the input on "using namespace std;". The link you shared is helpful. – CPKEN May 19 '21 at 13:29