How to count uppercase and lowercase strings

I managed to finish this homework problem, the only issue is that it did not count instances of a substring in a string properly to include uppercase T's
PROMPT FOR QUESTION:
Write a program that counts all of the occurrences of a given substring within a larger string. You should declare string variables like string sentence = ""; and string subst = "";
Your program should input the sentence and the substring and then print out the number of times the substring appears.
Test your program with the following two examples:
sentence - "The address is 1212 Walnut Street and the time is 12:12 on July 12 2012."
subst = "12"
(Answer = 6)
sentence - "The area of the rectangle is less than the area of the triangle."
subst = "the" (NOTE: You should also count the beginning word The with an uppercase T.)
(Answer = 4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>

using namespace std;
int main()
{
    string sentence = "";
    string subst = "";
    cout << "Enter sentence: \n";
    getline(cin, sentence);
    cout << "Enter Substring: \n";
    getline(cin, subst);
    int count = 0;
    int nPos = sentence.find(subst, 0);
    while (nPos != string::npos)
    {
        count++;
        nPos = sentence.find(subst, nPos + subst.size());
    }
    cout << count << endl;
    return 0;
}

I know I have includes I am not supposed to have but I just copy a template of includes because I'm lazy.
> (NOTE: You should also count the beginning word The with an uppercase T.)
You could convert both strings to lowercase before counting substrings.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <string>
#include <cctype>

void tolower(std::string& str)
{
	for (auto& ch : str)
		ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}

int main()
{
	std::string sentence;
	std::string subst;

	std::cout << "Enter sentence: \n";
	std::getline(std::cin, sentence);
	tolower(sentence);

	std::cout << "Enter Substring: \n";
	std::getline(std::cin, subst);
	tolower(subst);

	unsigned count {};

	for (size_t nPos {}; (nPos = sentence.find(subst, nPos)) != std::string::npos; ++count, ++nPos);
	std::cout << count << '\n';
}

Or using std::transform:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <string>
#include <cctype>
#include <algorithm>

int main()
{
	std::string sentence;
	std::string subst;

	std::cout << "Enter sentence: \n";
	std::getline(std::cin, sentence);
	std::transform(sentence.begin(), sentence.end(), sentence.begin(), tolower);

	std::cout << "Enter Substring: \n";
	std::getline(std::cin, subst);
	std::transform(subst.begin(), subst.end(), subst.begin(), tolower);

	unsigned count {};

	for (size_t nPos {}; (nPos = sentence.find(subst, nPos)) != std::string::npos; ++count, ++nPos);
	std::cout << count << '\n';
}
Hello gigacapybara,

In case you have not learned what has been suggested so far this might work for you.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//#define NOMINMAX  // <--- Only needed if you should include "Window.h". Does not define the min and max macros from "Windows.h".

#include <iostream>
//#include <iomanip>  //  setw(), fixed, setprecision, std::quoted().
//#include <limits>
#include <string>
//#include <vector>
#include <cctype>  // <--- For "std::tolower() and std::toupper()" + others.

//#include <algorithm>
//#include <fstream>

// <--- This section would be any header  files in double quotes.

using namespace std;  // <--- Best not to use.

int main()
{
    string sentence = "";  // <--- Strings are empty when defined and do not need initialized.
    string subst = "";     // <--- The empty ("") do nothing. Its still empty with no size.

    cout << "Enter sentence: -\n--> ";  // <--- Changed.
    getline(cin, sentence);

    for (size_t idx = 0; idx < sentence.size(); idx++)
    {
        sentence[idx] = static_cast<char>(std::tolower(static_cast<unsigned char>(sentence[idx])));
    }

    cout << "Enter Substring: -\n--> ";  // <--- Changed.
    getline(cin, subst);

    for (size_t idx = 0; idx < subst.size(); idx++)
    {
        subst[idx] = static_cast<char>(std::tolower(static_cast<unsigned char>(subst[idx])));
    }

    int count = 0;
    int nPos = sentence.find(subst, 0);

    while (nPos != string::npos)
    {
        count++;
        nPos = sentence.find(subst, nPos + subst.size());
    }

    cout << count << '\n';

    return 0;  // <--- Not required, but makes a good break point for testing.
}

Now there is nothing wrong with being lazy and just copying header files, but there is no point in including header files that you do not need or use. It just adds extra code to your program. This way as you grow you can uncomment header files that you do need.

The 2 for loops will change the inputted strings to all lower case letters, so that the ".find" will find all the correct matches.

When you use the ".find" it will compare 1 character at a time, so the letter "T" which has an ASCII value of 116 decimal while "t" which has an ASCII value of 148 decimal. These 2 values do not match. and that is why "The" is not considered a match to "the".

If you had mentioned your operating system and IDE I may have some suggestions for you.

Andy
For some versions of some compilers (gcc?? - VS was OK), you had to wrap tolower into a lambda to compile as an argument to std::transform(). That's why I started to use a range-for function.

Do all current compilers now allow this form for std::transform() ?
Topic archived. No new replies allowed.