简体   繁体   中英

The Letter counter within my C++ code is not working properly

so my program runs like it is supposed to except for one thing, the letters counted by my function are not correct. For example, if you enter "Why hello there!" as the string, the number of letters displayed is three as opposed to 13. I am not sure what I am doing wrong, any help would be appreciated. Thanks!

Here is my code:

#include <iostream>
#include <cstring>
#include <iomanip>

using namespace std;

void Count_All(char*, int&, int&, double&, int&); // Function prototype.
double Calc_Average (char*, int, int, double); // Function prototype.

int main()
{
    const int size = 500;
    char userString[size];
    int Word = 0;
    int Pun = 0;
    int Letters = 0;
    double Total_Characters = 0;
    double Average = 0.0;

    cout << "Please enter a string of 500 or less characters: ";
    cin.getline(userString, size);

    int len = strlen(userString);
    char *Dyn_Array = new char[len+1];
    strcpy(Dyn_Array, userString);

    cout << "\n";

    Count_All (Dyn_Array, Letters, Word, Total_Characters, Pun);

    cout << "Number of letters in the string: " << Letters << "\n";
    cout << "\n";
    cout << "Number of words in the string: " << Word << "\n";
    cout << "\n";

    Average = Calc_Average (Dyn_Array, Word, Pun, Total_Characters);
    cout <<"Average number of letters per word: "<< fixed <<
    showpoint << setprecision(2) << Average << "\n" << endl;


    cin.ignore(1);
    delete [] Dyn_Array;
    return 0;
}

void Count_All (char*strptr, int &Letters, int &Word, double &Total_Characters, int &Pun) // Counts all characters and types.
{
    while (*strptr != '\0')
    {
        if ((isspace(*strptr)) || (ispunct(*strptr)))
        {
            while ((isspace(*strptr)) || (ispunct(*strptr)))
            {
                strptr++;
            }
        }

        for(int x = 0; x < strlen(*strptr); x++)
        {
            if(!isspace(strptr[x]) && !Ispunct(strptr[x]))
            {
                Letters++;
            }
        }

        //if (((*strptr >= 'a') && (*strptr <= 'z')) || ((*strptr >= 'A') && (*strptr <= 'Z')))
            //Letters++;

        if ((isalnum(*strptr)) || (ispunct(*strptr)))
        {
            Word++;
            while ((isalnum(*strptr))||(ispunct(*strptr)))
            {
                strptr++;
                Total_Characters++; // Counting the total printable characters (including digits and punctuation).

                if((ispunct(*strptr)))
                {
                    Pun++; // Counting punctuation.
                }

            }
        }
        strptr++;
    }
}

double Calc_Average(char*strptr, int Word, int Pun, double Total_Characters)  // Calculates the average number of characters per words.
{
    double Average = 0.0;
    Total_Characters = Total_Characters - Pun; // Subtracting punctuation from all of the characters in the string (not including spaces).
    Average = (Total_Characters / Word);
    return Average;
}
for(int x = 0; x < strlen(*strptr); x++)

Given that strptr is a char * , your compiler is most likely yelling at you, very loudly, on this line, because you're passing a char to strlen() instead of a char * . Loud screams from your compiler must not be ignored, even if the compiler still produces an executable.

Even if this is fixed as follows, this will still produce completely wrong results:

    for(int x = 0; x < strlen(strptr); x++)
    {
        if(!isspace(strptr[x]) && !Ispunct(strptr[x]))
        {
            Letters++;
        }
    }

This is a second inner loop. The outer loop iterates strptr over every character, executing a bunch of stuff, including this inner loop.

So, if the string in question is "hello":

  1. On the first iteration of the outer loop, strptr points at the ' h ', and this inner loop adds 5 to Letters .

  2. On the second iteration of the outer loop, strptr points at the ' e ', and this inner loop adds 4 to Letters .

  3. On the third iteration of the outer loop, strptr points at the first ' l ', and this inner loop adds 3 to Letters .

  4. On the fourth iteration of the outer loop, strptr points at the second ' l ', and this inner loop adds 2 to Letters .

  5. On the fifth iteration of the outer loop, strptr points at ' o ', and this inner loop adds 1 to Letters .

This code ends up counting 5+4+3+2+1, or 15 letters in the string "hello". Obviously not the correct result.

This inner loop is completely unneeded. Get rid of it.

You should also find a very useful tool on your computer, called a "debugger". Using this tool, you would've been able to step through the execution of your code, one line at a time, examine all the variables, and determine this problem all by yourself. Learning how to use a debugger is a required skill for every C++ developer.

EDIT: the letter counting should probably be incorporated at your word detector, since it advances strptr as well. Not only does it mess up the separate letter counting logic, it is also broken on its own merits, because it could advance strptr to the terminating \\0 character, and the final strptr++ will advance it once more, resulting in undefined behavior, and a likely crash. There are too many problems with the overall logic. It should be rewritten from scratch.

Efficiency is great. Doing everything in one pass can be awesome, but it is rarely elegant and is often a real [expletive deleted] to debug. As a result there is a recommendation in software engineering that you do one thing and do it well.

Count_All counts letters, words, and punctuation all at once and gets it wrong several different ways. It is hard to figure out which bit of functionality is broken how because bugs tend to feed off and hide each other.

On the other hand, with three functions, Count_Punctuation ; Count_Letters and Count_Words , each does exactly one thing. Each can be written separately, tested separately, and debugged separately. Write one function. Prove that it works. Move onto writing and proving the next function. Rinse, repeat.

What results is not as fast as a highly optimized, all-in-one solution, but you can code and debug it a lot faster. This is very important when learning. It means you can spend more time doing the learning.

For example:

int Count_Punctuation(char * strptr)
{
    int punc = 0;
    while (*strptr != '\0')
    {
        if (ispunct(*strptr))
        {
            punc ++;
        }
        strptr++;
    }
    return punc;
}

One loop through each character. If the character is punctuation, increment the counter. when out of characters return the counter.

Count_Letter is nearly identical, but counting letters. Count_Word is a bit trickier.

As Sam said, you need to be the pointer into the strlen function. Then you need to move your letter counter outside of your while loop and you should be okay. Something like this:

for(int x = 0; x < strlen(strptr); x++)
{
   if(!isspace(strptr[x]) && !Ispunct(strptr[x]))
   {
   Letters++;
   }
}

while (*strptr != '\0')
{
    if ((isspace(*strptr)) || (ispunct(*strptr)))
    {
        while ((isspace(*strptr)) || (ispunct(*strptr)))
        {
            strptr++;
        }
    }

    if ((isalnum(*strptr)) || (ispunct(*strptr)))
    {
        Word++;
        while ((isalnum(*strptr))||(ispunct(*strptr)))
        {
            strptr++;
            Total_Characters++; // Counting the total printable characters (including digits and punctuation).

            if((ispunct(*strptr)))
            {
                Pun++; // Counting punctuation.
            }

        }
    }
    strptr++;
}

I updated to fix the problem on comments, is better now as far as I tested...

#include <iostream>
#include <cstring>
#include <iomanip>

using namespace std;

void Count_All(char*, int&, int&, double&, int&); // Function prototype.
double Calc_Average (char*, int, int, double); // Function prototype.

int main()
{
    const int size = 500;
    char userString[size];
    int Word = 0;
    int Pun = 0;
    int Letters = 0;
    double Total_Characters = 0;
    double Average = 0.0;

    cout << "Please enter a string of 500 or less characters: ";
    cin.getline(userString, size);

    int len = strlen(userString);
    char *Dyn_Array = new char[len+1];
    strcpy(Dyn_Array, userString);

    cout << "\n";

    Count_All (Dyn_Array, Letters, Word, Total_Characters, Pun);

    cout << "Number of letters in the string: " << Letters << "\n";
    cout << "\n";
    cout << "Number of words in the string: " << Word << "\n";
    cout << "\n";

    Average = Calc_Average (Dyn_Array, Word, Pun, Total_Characters);
    cout <<"Average number of letters per word: "<< fixed <<
    showpoint << setprecision(2) << Average << "\n" << endl;


    cin.ignore(1);
    delete [] Dyn_Array;
    return 0;
}

void Count_All (char*strptr, int &Letters, int &Word, double &Total_Characters, int &Pun) // Counts all characters and types.
{
    // sorry this was a test: strptr[strlen(strptr)+1]='\0';
    while (strptr[0] != '\0')
    {
        while (isspace(strptr[0]))
        {
            strptr++;
        }

        if(!isspace(strptr[0]) && !ispunct(strptr[0]))
        {
            cout << strptr[0] << " ";
            Letters++;
            Total_Characters++; // Counting the total printable characters (including digits and punctuation).
            strptr++;
            if(strptr[0] == '\0' || isspace(strptr[0]) || ispunct(strptr[0]))
            {
                Word++;
            }
        }

        if ((isalnum(strptr[0])) || (ispunct(strptr[0])))
        {
            if((ispunct(strptr[0])))
            {
                Pun++; // Counting punctuation.
                Total_Characters++; // Counting the total printable characters (including digits and punctuation).
                strptr++;
            }
        }
    }
}

double Calc_Average(char*strptr, int Word, int Pun, double Total_Characters)  // Calculates the average number of characters per words.
{
    double Average = 0.0;
    Total_Characters = Total_Characters - Pun; // Subtracting punctuation from all of the characters in the string (not including spaces).
    Average = (Total_Characters / Word);
    return Average;
}

Loop through the string character-by-character once, using a finite-state machine to keep track of whether we are in a word or not and to count the number of words.

#include <iostream>
#include <cstring>
#include <iomanip>

#include <string>

using namespace std;

const int STATE_WITHOUT = 1;
const int STATE_IN_WORD = 2;

class TestInput {
    public:
        string m_s_input;
        int m_i_expected_words;
        int m_i_expected_punctuation_marks;
        int m_i_expected_letters;
        int m_i_expected_total_printable_characters;

    TestInput(
        string s_input,
        int i_expected_words,
        int i_expected_punctuation_marks,
        int i_expected_letters,
        int i_expected_total_printable_characters
    ):  m_s_input(s_input),
        m_i_expected_words(i_expected_words),
        m_i_expected_punctuation_marks(i_expected_punctuation_marks),
        m_i_expected_letters(i_expected_letters),
        m_i_expected_total_printable_characters(i_expected_total_printable_characters)
    {}
}; /* class TestInput */

// Counts all characters and types. 
void Count_All (const string& str, int &Letters, int &Words, int &Total_Printable_Characters, int &Punctuation_Marks) {

    // Clear all these "out params" so the caller can call this
    // method multiple times without having to theirself
    // clear them...
    Letters = 0;
    Words = 0;
    Total_Printable_Characters = 0;
    Punctuation_Marks = 0;

    int i_state = STATE_WITHOUT;

    char c = '\0';

    for( size_t i = 0; i < str.length(); i++ ){

        c = str[i];

        if( isalpha(c) )
        {
            Letters++;
        }

        if( isalnum(c) || ispunct(c) )
        {
            Total_Printable_Characters++; // Counting the total printable characters (including digits and punctuation).
        }

        if( ispunct(c) ) 
        {
            Punctuation_Marks++;
        }

        /* Use finite-state machine to count words... */
        switch( i_state ){
            case STATE_WITHOUT:
                if( ispunct(c) || isalnum(c) )
                {
                    i_state = STATE_IN_WORD;
                    Words++;
                }
                break;

            case STATE_IN_WORD:
                if( isspace(c) )
                {
                    i_state = STATE_WITHOUT;
                }
                break;
        }/* switch( i_state ) */

    }/* for( size_t i = 0; i < str.length(); i++ ) */

}/* Count_All() */

// Calculates the average number of characters per words.
double Calc_Average(int Word, int Pun, int Total_Characters){
    double Average = 0.0;

    Total_Characters = Total_Characters - Pun; // Subtracting punctuation from all of the characters in the string (not including spaces).

    if( Word == 0 ){
        // Avoid divide by zero error...
        return 0.0;
    }

    Average = ((double)Total_Characters / (double)Word);

    return Average;
}/* Calc_Average() */

int main()
{
    int Words = 0;
    int Punctuation_Marks = 0;
    int Letters = 0;
    int Total_Printable_Characters = 0;

    double Average = 0.0;

    TestInput test_inputs[] = {
        // s_input, i_expected_words, i_expected_punctuation_marks, i_expected_letters, i_expected_total_printable_characters 
        TestInput("", 0, 0, 0, 0 ) 
        ,TestInput(" ", 0, 0, 0, 0 ) 
        ,TestInput("Why, hello there!", 3, 2, 13, 15 ) 
        ,TestInput("I am sam.", 3, 1, 6, 7 ) 
        ,TestInput("I'll take 1 bagel.", 4, 2, 12, 15 ) // Counting both contraction "I'll" and numerical "1" as one word each...as "wc" utility seems to do...
        ,TestInput("I'll be back, Bennett!", 4, 3, 16, 19) 
        ,TestInput("Let off some steam, Bennett!", 5, 2, 22, 24) 
        ,TestInput("Supercalifragilisticexpyalidocious", 1, 0, 34, 34 )
        ,TestInput("'ere, what'cha doin' 'ere, guv'nor?", 5, 8, 23, 31 )
        ,TestInput(" 'ere, what'cha doin' 'ere, guv'nor?", 5, 8, 23, 31 )
        ,TestInput("That's one small step for a man, one giant leap for mankind.", 12, 3, 46, 49 )
    };

    for( size_t i = 0; i < sizeof(test_inputs)/sizeof(TestInput); i++ ){
        cout << "i = " << i << ": Running Count_All( \"" << test_inputs[i].m_s_input << "\" )\n" 
               << "\t" << "(length of input = " << test_inputs[i].m_s_input.length() << ")..." << endl;

        Count_All( test_inputs[i].m_s_input, Letters, Words, Total_Printable_Characters, Punctuation_Marks );

        cout << "i = " << i << ": Letters = " << Letters << " (Expected " << test_inputs[i].m_i_expected_letters << ")..."
            << (( Letters == test_inputs[i].m_i_expected_letters ) ? "PASSED" : "FAILED" ) << "..." << endl;

        cout << "i = " << i << ": Words = " << Words << " (Expected " << test_inputs[i].m_i_expected_words << ")..."
            << (( Words == test_inputs[i].m_i_expected_words ) ? "PASSED" : "FAILED" ) << "..." << endl;

        cout << "i = " << i << ": Total_Printable_Characters = " << Total_Printable_Characters << " (Expected " << test_inputs[i].m_i_expected_total_printable_characters << ")..."
            << (( Total_Printable_Characters == test_inputs[i].m_i_expected_total_printable_characters) ? "PASSED" : "FAILED" ) << "..." << endl;

        cout << "i = " << i << ": Punctuation_Marks = " << Punctuation_Marks << " (Expected " << test_inputs[i].m_i_expected_punctuation_marks << ")..."
            << (( Punctuation_Marks == test_inputs[i].m_i_expected_punctuation_marks ) ? "PASSED" : "FAILED" ) << "..." << endl;

        Average = Calc_Average ( Words, Punctuation_Marks, Total_Printable_Characters);

        cout << "i = " << i << ": Average number of letters per word: " << fixed 
            << showpoint << setprecision(2) << Average << "\n" << endl;
    }

    return 0;

}/* main() */

OUTPUT:

i = 0: Running Count_All( "" )
    (length of input = 0)...
i = 0: Letters = 0 (Expected 0)...PASSED...
i = 0: Words = 0 (Expected 0)...PASSED...
i = 0: Total_Printable_Characters = 0 (Expected 0)...PASSED...
i = 0: Punctuation_Marks = 0 (Expected 0)...PASSED...
i = 0: Average number of letters per word: 0.00

i = 1: Running Count_All( " " )
    (length of input = 1)...
i = 1: Letters = 0 (Expected 0)...PASSED...
i = 1: Words = 0 (Expected 0)...PASSED...
i = 1: Total_Printable_Characters = 0 (Expected 0)...PASSED...
i = 1: Punctuation_Marks = 0 (Expected 0)...PASSED...
i = 1: Average number of letters per word: 0.00

i = 2: Running Count_All( "Why, hello there!" )
    (length of input = 17)...
i = 2: Letters = 13 (Expected 13)...PASSED...
i = 2: Words = 3 (Expected 3)...PASSED...
i = 2: Total_Printable_Characters = 15 (Expected 15)...PASSED...
i = 2: Punctuation_Marks = 2 (Expected 2)...PASSED...
i = 2: Average number of letters per word: 4.33

i = 3: Running Count_All( "I am sam." )
    (length of input = 9)...
i = 3: Letters = 6 (Expected 6)...PASSED...
i = 3: Words = 3 (Expected 3)...PASSED...
i = 3: Total_Printable_Characters = 7 (Expected 7)...PASSED...
i = 3: Punctuation_Marks = 1 (Expected 1)...PASSED...
i = 3: Average number of letters per word: 2.00

i = 4: Running Count_All( "I'll take 1 bagel." )
    (length of input = 18)...
i = 4: Letters = 12 (Expected 12)...PASSED...
i = 4: Words = 4 (Expected 4)...PASSED...
i = 4: Total_Printable_Characters = 15 (Expected 15)...PASSED...
i = 4: Punctuation_Marks = 2 (Expected 2)...PASSED...
i = 4: Average number of letters per word: 3.25

i = 5: Running Count_All( "I'll be back, Bennett!" )
    (length of input = 22)...
i = 5: Letters = 16 (Expected 16)...PASSED...
i = 5: Words = 4 (Expected 4)...PASSED...
i = 5: Total_Printable_Characters = 19 (Expected 19)...PASSED...
i = 5: Punctuation_Marks = 3 (Expected 3)...PASSED...
i = 5: Average number of letters per word: 4.00

i = 6: Running Count_All( "Let off some steam, Bennett!" )
    (length of input = 28)...
i = 6: Letters = 22 (Expected 22)...PASSED...
i = 6: Words = 5 (Expected 5)...PASSED...
i = 6: Total_Printable_Characters = 24 (Expected 24)...PASSED...
i = 6: Punctuation_Marks = 2 (Expected 2)...PASSED...
i = 6: Average number of letters per word: 4.40

i = 7: Running Count_All( "Supercalifragilisticexpyalidocious" )
    (length of input = 34)...
i = 7: Letters = 34 (Expected 34)...PASSED...
i = 7: Words = 1 (Expected 1)...PASSED...
i = 7: Total_Printable_Characters = 34 (Expected 34)...PASSED...
i = 7: Punctuation_Marks = 0 (Expected 0)...PASSED...
i = 7: Average number of letters per word: 34.00

i = 8: Running Count_All( "'ere, what'cha doin' 'ere, guv'nor?" )
    (length of input = 35)...
i = 8: Letters = 23 (Expected 23)...PASSED...
i = 8: Words = 5 (Expected 5)...PASSED...
i = 8: Total_Printable_Characters = 31 (Expected 31)...PASSED...
i = 8: Punctuation_Marks = 8 (Expected 8)...PASSED...
i = 8: Average number of letters per word: 4.60

i = 9: Running Count_All( " 'ere, what'cha doin' 'ere, guv'nor?" )
    (length of input = 36)...
i = 9: Letters = 23 (Expected 23)...PASSED...
i = 9: Words = 5 (Expected 5)...PASSED...
i = 9: Total_Printable_Characters = 31 (Expected 31)...PASSED...
i = 9: Punctuation_Marks = 8 (Expected 8)...PASSED...
i = 9: Average number of letters per word: 4.60

i = 10: Running Count_All( "That's one small step for a man, one giant leap for mankind." )
    (length of input = 60)...
i = 10: Letters = 46 (Expected 46)...PASSED...
i = 10: Words = 12 (Expected 12)...PASSED...
i = 10: Total_Printable_Characters = 49 (Expected 49)...PASSED...
i = 10: Punctuation_Marks = 3 (Expected 3)...PASSED...
i = 10: Average number of letters per word: 3.83

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM