Program to change case of user input, not reading spaces/symbols/already cased words..

Pages: 12
Hello, I am making a menu program with 4 options. The user will enter a (string) sentence of any length, then make a selection 1 -4. Options 2 and 3 will change the users input all to UPPERCASE or all to LOWERCASE.

The problem I'm facing: "This IS a TEST !@#" this is my test input.
When converting "This IS a TEST 1@#" to UPPER, my output is: "HIS A"
When converting "This IS a TEST !@#" to LOWER, my output is: "t is test"

neither will read the char if it is already upper/lower, and also will not display the symbols. I will post my full code for better reading. Any help would be appreciated as I am stuck on this and cannot figure it out.

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  string usrinput;
    string upper;
    string lower;
    string str;
    int selection;
    int i;
    bool menu = true;

    
        
        cout << "=============================================================" << endl;
        cout << "Welcome to my program. Enter a sentence and make a selection: " << endl;
        cout << "Enter -999 to exit the program. " << endl;
        cout << "=============================================================" << endl;
        cout << "1. display middle character. " << endl; //if user selects 1, do "..."
        cout << "2. display sentence uppercase " << endl; //if user selects 2, do "..."             
        cout << "3. display sentence lowercase" << endl; //if user selects 3, do "..."
        cout << "4. display sentence backwards" << endl; //if user selects 4, do "..."               

        cout << "Enter a sentence: " << endl;        
        getline(cin, usrinput); //sentence input
        
        
        while (menu == true)
        {
        cout << "Make a selection: " << endl; // if selection is 1 - 4 || -999 (good input) anything <1 or >5 (bad input, loop until selection = 1 - 4
        cin >> selection;

        //Step 1. Input Validation
        while (selection != 1 && selection != 2 && selection != 3 && selection != 4 && selection != -999) //If the selection is not 1 - 4 || -999 loop until selection is valid. 
        {
            cout << "Invalid Entry. Please make another selection: " << endl; 
            cin >> selection;
        }

        if (selection == 1) // if the user enters 1: show middle character if there is one / let the user know there isn't one.
        {
            cout << "Not done yet" << endl;

        }

        else if (selection == 2) //if the user enters 2: display the entire sentence upper case
        {
            cout << "Upper Case:" << endl;
            cout << "===========" << endl;

            for (int i = 0; i < usrinput.length(); i++)
            {
                if (usrinput.at(i) >= 'a' && usrinput.at(i) <= 'z')
                {
                    upper += usrinput.at(i) - 32;
                    
                }
                else if (usrinput.at(i) == ' ')
                {
                    upper += ' ';
                    
                }
                 
            }
            cout << upper << endl;
        }

        else if (selection == 3) // if the user enters 3: display the entire sentence lower case
        {
            cout << "Lower Case: " << endl;
            cout << "===========" << endl;

            for (int i = 0; i < usrinput.length(); i++)
            {
                if (usrinput.at(i) >= 'A' && usrinput.at(i) <= 'Z')
                {
                    lower += usrinput.at(i) + 32;

                }
                else if (usrinput.at(i) == ' ')
                {
                    lower += ' ';

                }

            }
            cout << lower << endl;
        }

        else if (selection == 4) // if the user enters 4: display the sentence backwards
        {
            cout << "Backwards: " << endl;
            cout << "==========" << endl;

            for (int i = usrinput.length() - 1; i >= 0; i--)
            {
                cout << usrinput[i];

            }
            cout << endl;
        }   

        else if (selection == -999) //If the user enters -999 then quit the program. 
        {
            cout << "Thanks for using my program." << endl;
            cout << "Goodbye." << endl;
            menu = false;

        }
    }
}
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> // ::tolower
// http://www.cplusplus.com/reference/cctype/tolower/

#include <algorithm> // std::transform
// http://www.cplusplus.com/reference/algorithm/transform/

int main()
{
   // create a string
   std::string str { "This IS a TEST !@#" };

   // display it
   std::cout << str << '\n';

   // transform the string to all lower case
   std::transform(str.begin(), str.end(), str.begin(), ::tolower);

   // display the transformed string
   std::cout << str << '\n';
}

This IS a TEST !@#
this is a test !@#
mudd wrote:
I will post my full code for better reading.

Full code would have your included headers. Post a Short, Self Contained, Correct (Compilable), Example.

http://www.sscce.org/

You could use a loop to transform the string:
19
20
21
22
   for (unsigned itr { }; itr < str.size(); ++itr )
   {
      str[itr] = static_cast<char>(::tolower(str[itr]));
   }
@kbw thank you for the references.

@furry guy, Im not sure we are allowed to do it that way as we have never used "transform" or anything like that. The way he showed us looked just like the code I have, but I'm not sure how to just allow it to read whitespace and symbols, and ignore the case of the test string.

Also my headers:
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
Using the brute-force method for transforming a string to lower or upper case, transforming only the letters which need to be changed while ignoring all the other characters:
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
#include <iostream>
#include <string>

#include <cctype> // ::isupper // ::islower
// https://en.cppreference.com/w/cpp/string/byte/islower

int main()
{
   // create a string
   std::string str { "This IS a TEST !@#" };

   // display it
   std::cout << str << '\n';

   // transform the string to lower case
   for (unsigned i { }; i < str.size(); ++i)
   {
      // is the element an upper case letter?
      if (::isupper(str[i]))
      {
         str[i] += 32;
      }
   }

   // display the transformed string
   std::cout << str << '\n';

   // transform the string to upper case
   for (unsigned i { }; i < str.size(); ++i)
   {
      // is the element an lower case letter?
      if (::islower(str[i]))
      {
         str[i] -= 32;
      }
   }

   // display the transformed string
   std::cout << str << '\n';
}

This IS a TEST !@#
this is a test !@#
THIS IS A TEST !@#

Without needing <cctype>
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
#include <iostream>
#include <string>

int main()
{
   // create a string
   std::string str { "This IS a TEST !@#" };

   // display it
   std::cout << str << '\n';

   // transform the string to lower case
   for (unsigned i { }; i < str.size(); ++i)
   {
      // is the element an upper case letter?
      if (str[i] >= 'A' && str[i] <= 'Z')
      {
         str[i] += 32;
      }
   }

   // display the transformed string
   std::cout << str << '\n';

   // transform the string to upper case
   for (unsigned i { }; i < str.size(); ++i)
   {
      // is the element an lower case letter?
      if (str[i] >= 'a' && str[i] <= 'z')
      {
         str[i] -= 32;
      }
   }

   // display the transformed string
   std::cout << str << '\n';
}
Last edited on
@furry guy

You're a life saver. Thank you. I'm new to all these "i" function things. It's pretty tricky
I see you have a menu option to print out a string in reverse. There are a couple of ways to to do it using a for loop, from simple to more advanced:
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
#include <iostream>
#include <string>
#include <ranges>   // std::views::reverse

int main()
{
   // create a string
   std::string str { "This IS a TEST !@#" };

   // display it reversed, simple for loop
   for (unsigned i { str.size() }; i > 0; --i)
   {
      std::cout << str[i - 1];
   }
   std::cout << "\n\n";

   // display it reversed, using string iterators
   for (auto i { str.cend() }; i != str.cbegin(); --i)
   {
      std::cout << *i;
   }
   std::cout << "\n\n";

   // display it reversed, range-based for loop
   // C++20 feature for the reversal!
   // range-based for loops were added in C++11
   for (const auto& itr : str | std::views::reverse)
   {
      std::cout << itr;
   }
   std::cout << '\n';
}

If'n you don't understand loops 2 or 3 don't worry, hopefully you will be exposed to them in time.

One thing to note, your string transformation to either upper or lower case operates directly on the original string. If you need to retain the original copy it to a temp string and transform the copy.

Last edited on
@Furry Guy, option 4 "Backwards" i got it working, that was the first one I did. It works perfect.
1 is middle character but I haven't gotten to that yet because if the user can input a sentence of any length, I would have to figure out if it is odd or even I suppose, so I'm waiting to do that one.

2 and 3 have me stumped, but I think he used the "temp" string. Would you mind elaborating a bit more on how to use a temp string?
Transforming a temp string, leaving the original untouched:
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
#include <iostream>
#include <string>

int main()
{
   // create a string
   std::string str { "This IS a TEST !@#" };

   // display it
   std::cout << str << '\n';

   // create a temp string for the transformations
   std::string temp { str };

   // display the temp string
   std::cout << temp << "\n\n";

   // transform the temp string to lower case
   for (unsigned i { }; i < temp.size(); ++i)
   {
      // is the element an upper case letter?
      if (temp[i] >= 'A' && temp[i] <= 'Z')
      {
         temp[i] += 32;
      }
   }

   // display the strings
   std::cout << str << '\n' << temp << "\n\n";

   // restore the temp to the original
   temp = str;

   // transform the temp string to upper case
   for (unsigned i { }; i < temp.size(); ++i)
   {
      // is the element an lower case letter?
      if (temp[i] >= 'a' && temp[i] <= 'z')
      {
         temp[i] -= 32;
      }
   }

   // display the strings
   std::cout << str << '\n' << temp << '\n';
}

This IS a TEST !@#
This IS a TEST !@#

This IS a TEST !@#
this is a test !@#

This IS a TEST !@#
THIS IS A TEST !@#
that makes so much sense. Thank you for all of your help, truly i appreciate it. I need to study a bit more, but I'm not entirely sure where i can find info on all of these different functions. But again, thank you, you're very thorough with your explanations.
I'm not entirely sure where i can find info on all of these different functions.

http://www.cplusplus.com/reference/cctype/

Another potential issue that can cause problems, mixing std::cin and std::getline.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <string>

int main()
{
   std::string name { };
   int age { };

   std::cout << "Enter your age: ";
   std::cin >> age;

   std::cout << "Enter your name: ";
   std::getline(std::cin, name);

   std::cout << "\nHello, " << name << ", you are " << age << " years old.\n";
}

Enter your age: 20
Enter your name:
Hello, , you are 20 years old.

Ooops!

std::cin leaves the carriage return ('\n') in the input stream, std::getline sees the carriage return and acts as if the user entered something. The string gets no data!

You need to IGNORE any superfluous data in the input stream, including the carriage return.

One method is using std::ignore.
https://stackoverflow.com/questions/21567291/why-does-stdgetline-skip-input-after-a-formatted-extraction

That is the most common method.

Another way is to use std::ws to remove the not-needed data:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <string>

int main()
{
   std::string name { };
   int age { };

   std::cout << "Enter your age: ";
   std::cin >> age;

   std::cout << "Enter your name: ";
   std::getline(std::cin >> std::ws, name);

   std::cout << "\nHello, " << name << ", you are " << age << " years old.\n";
}

Enter your age: 20
Enter your name: John Doe

Hello, John Doe, you are 20 years old.

https://en.cppreference.com/w/cpp/io/manip/ws
Last edited on
Furry Guy wrote:
 
for (unsigned i { }; i < temp.size(); ++i)

Argh! That’s an abomination of modern C++ syntax weirdness.

 
for (unsigned i = 0;  i < temp.size();  ++i)

The compiler will generate identical code, just as efficient, and C++ won’t reveal itself as an alien language as readily.
Argh! That’s an abomination of modern C++ syntax weirdness.

C++ Core Guidelines says one should "Prefer the {}-initializer syntax":
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es23-prefer-the--initializer-syntax

Personally I haven't been able to evaluate whether list-initialization prevents more runtime errors than it causes.
My intuition is that it might come out ahead, but not by much, because I believe it's easier to avoid runtime errors involving {} than with =.

We'd need data to call it, and even then the answer could vary depending on the code you write.
Last edited on
C++ Core Guidelines says one should ...

Yeah, more arbitrary rules to be parachuted in, irrespective of context, like use auto. That link shows int y = x; as a compliant example.

Isn't C++ thinking man's programming language?
Last edited on
This is exactly the problem with C++ — users of the language spend as much time programming the language as they do programming other stuff.

And now we have this really stupid syntax to “fix” problems by offloading them onto the programmer.

int y = x is considered compliant because it does not invoke any narrowing conversions (both x and y are ints).

The (narrowing) conversion issue is one caused by allowing operator overloading. There are multiple ways to automagically transform one type to another type — not necessarily obvious ways — during assignment.

Rather than fix the problem some reasonable way (IMNSHO) the committee chose to introduce a new syntax which is non-obvious and just looks wrong. Like, how the quiznak are you supposed to get “assignment/initialization” out of int x {12};?

You can’t. It’s stupid, and requires you to learn more and think harder to use.
1
2
3
4
int main()
{
   auto x{nullptr};
}


And the type of x is ...



Edit: Tried
1
2
3
4
5
6
7
8
9
#include <iostream>
#include <typeinfo>
using namespace std;

int main()
{
   auto x{nullptr};
   cout << typeid( x ).name() << '\n';
}

and, on my PC it produced
Dn

Does that stand for "don't (k)now"?
Last edited on
1
2
3
4
for (auto i { str.cend() }; i != str.cbegin(); --i)
   {
      std::cout << *i;
   }


You can't dereference an .end() iterator - as it points 1 past the end.

Perhaps using a reverse iterator:

1
2
3
4
for (auto i { str.crbegin() }; i != str.crend(); ++i)
   {
      std::cout << *i;
   }


PS. For those wondering, 32 is the answer to 'a' - 'A' !
Last edited on
For VS2022:

 
auto x{nullptr};


displays:


std::nullptr_t


See https://en.cppreference.com/w/cpp/types/nullptr_t
Last edited on
If temp is a suitable type with a begin and end, I would rather do a range based for loop.

Among other things it avoids problems with the return type of size(). std::size_t works but why bother?
Pages: 12