-1

The challenge I've received is:

Exercise 2 – Strings

const int TOWNTOTAL = 6; string towns[TOWNTOTAL] = { "london", "glasGow", "HARWICH", "NoTtInGHaM","DERBy","BriSToL" };

Letters in a string are stored as ASCII numbers.
The letter ‘a’ is stored as the number 97. The letter ‘A’ is stored as the number 65.

Adding 32 will change an uppercase letter to lowercase.

Subtracting 32 will change a lowercase letter to uppercase.

Using the above strings, change the list so that all towns start with an uppercase letter but the rest of the letters are lowercase.

My code so far is

int main()
{
    const int TOWNTOTAL = 6;
    string towns[TOWNTOTAL] = { "london", "glasGow", "HARWICH", "NoTtInGHaM","DERBy","BriSToL" };


    for (int j = 0; j < towns[TOWNTOTAL].length(); j++) {
        towns[j][0] = int(towns[j][0] - 32);
        towns[j][0] = char(towns[j][0]);
        cout << towns[j] << " ";

        for (int i = 0; i < towns[j].length(); i++) {

        }
    }
}

So I've been able to loop through each word in the string of towns and output the first letter, I was even able to turn the letter 'g' into ASCII, minus 32 and convert it back to a char to gain 'G', but how am I now able to apply this to the rest of the words in the string?

Henry
  • 95
  • 2
  • 12
  • 2
    Do you have to manually add and subtract values? `std::toupper` and `std::tolower` already handle corner cases like upper-to-upper conversion (at which your code fails) – Yksisarvinen Feb 04 '20 at 12:23
  • 2
    Does this answer your question? [Convert a String In C++ To Upper Case](https://stackoverflow.com/questions/735204/convert-a-string-in-c-to-upper-case) – ExaltedBagel Feb 04 '20 at 12:23
  • Start with [character classification](https://en.cppreference.com/w/cpp/string/byte#Character_classification), continue with [character manipulation](https://en.cppreference.com/w/cpp/string/byte#Character_manipulation). – Some programmer dude Feb 04 '20 at 12:24
  • @ExaltedBagel Not really. You cannot use any algorithm which works over whole string here, so boost answer (accepted), range-based loop and others cannot be used. `std::transform` would be rather annoying to use here too. – Yksisarvinen Feb 04 '20 at 12:27
  • @ExaltedBagel this question you've linked doesn't really help as I've been asked to complete this task a specific way, as mentioned in the question – Henry Feb 04 '20 at 12:30
  • 2
    @idclev 463035818 This is not really a duplicate question as the way I have to approach it is completely different. – Henry Feb 04 '20 at 12:32
  • "as I've been asked to complete this task a specific way, as mentioned in the question" i read it again and I didnt find the "specific" requirements. Are you not allowed to use `std::toupper` / `std::tolower` ? If that is the case you should mention it in the question – 463035818_is_not_an_ai Feb 04 '20 at 12:34
  • Hint: you need to use `&`, `~` and `|`; not `+` and `-` – Caleth Feb 04 '20 at 15:26
  • @idclev463035818 i haven't used c++ before so forgive my ignorance but wont `std::toupper` / `std::tolower` change the whole string and not just the wanted characters? – WhatsThePoint Feb 04 '20 at 17:20
  • Why not perform two operations? One being to convert the whole string to lowercase then convert the first character of the string to uppercase – WhatsThePoint Feb 04 '20 at 17:21
  • No, I don't think I'm allowed to use that, a friend used if statements to check whether the ASCII value was greater than 97 then converted it to lower/upper case based on that value and also checked if it was in the first position of the word. – Henry Feb 04 '20 at 17:56
  • @WhatsThePoint, No, it won't. `toupper()` and `tolower()` only operate on a single character. They also avoid the problem in OP's code of attempting to convert a character that shouldn't be. – sweenish Feb 04 '20 at 22:42
  • @Henry Your friend's code sounds like it would break if someone tried to feed it numbers or non-letter ASCII characters like `'?'`. – sweenish Feb 04 '20 at 22:49
  • @sweenish ah ok, only languages i've ever used `toupper`/`tolower` are performed on a string and change the whole string – WhatsThePoint Feb 05 '20 at 10:04

2 Answers2

0
#include <array>
#include <iostream>
#include <string>

// Because it appears you're allergic to toupper() and tolower(),
// Here are two functions that do what they do
// NOTE: uppercase() will only act on a lowercase letter, and
//       lowercase() will only act on an uppercase letter
char uppercase(char letter)
{
    return letter >= 'a' && letter <= 'z' ? letter - 32 : letter;
}

char lowercase(char letter)
{
    return letter >= 'A' && letter <= 'Z' ? letter + 32 : letter;
}

int main()
{
    const int TOWNTOTAL = 6;
    std::array<std::string, TOWNTOTAL> towns { "london",
        "glasGow",
        "HARWICH",
        "NoTtInGHaM",
        "DERBy",
        "BriSToL" };

    for (auto& j : towns) {
        j[0] = uppercase(j[0]);
        for (int i = 1; i < j.length(); ++i) {
            j[i] = lowercase(j[i]);
        }
    }

    for (const auto& i : towns) {
        std::cout << i << '\n';
    }
}

While you never say that toupper() and tolower() are forbidden, you certainly imply it. So, I recreated them.

If toupper() and tolower() are acceptable, I feel better using std::for_each() to simplify the program some more.

#include <algorithm>
#include <array>
#include <cctype>
#include <iostream>
#include <string>

int main()
{
    const int TOWNTOTAL = 6;
    std::array<std::string, TOWNTOTAL> towns { "london",
        "glasGow",
        "HARWICH",
        "NoTtInGHaM",
        "DERBy",
        "BriSToL" };

    // Writing the lambda here so that the for_each call
    // reads better
    auto make_title_case = [](std::string& str) {
        str[0] = toupper(str[0]);
        for (int i = 1; i < str.length(); ++i) {
            str[i] = tolower(str[i]);
        }
    };

    std::for_each(towns.begin(), towns.end(), make_title_case);

    for (const auto& i : towns) {
        std::cout << i << '\n';
    }
}
sweenish
  • 4,793
  • 3
  • 12
  • 23
0

There are a number of ways you can handle both the data and the conversions. One of the easiest ways to store your collection of strings that lends itself to easy manipulation is std::vector which then allows you to create a std::vector<std::string> (a vector of strings). A simple range-based for loop provides all you need to iterate over each string.

To convert the characters in each string to upper/lower case std::transform provides an easy way to convert all or some of the characters in each string (or apply any other operation to each element in the container) Your conversions to lowercase and to uppercase, then reduce to:

void stolower (std::string& s)
{
    std::transform (s.begin(), s.end(), s.begin(),
                    [](unsigned char c) { return std::tolower(c); });
}

and

void stoupper (std::string& s)
{
    std::transform (s.begin(), s.end(), s.begin(),
                    [](unsigned char c) { return std::toupper(c); });
}

(note: the use of std::transform above omits the trailing return type. Adding -> unsigned char between (unsigned char c) { return std::toupper(c); }); would provide the full form)

In each case above the transformation is applied to the range of characters from s.begin(), s.end(), outputting the transformation beginning at s.begin(), where the transformation itself is provided by the lambda function [](unsigned char c) { return std::tolower(c); } or the toupper(c) counterpart.

To implement your vector of strings and then perform the conversion to lower and then to upper, you could do:

int main (void) {

    std::vector<std::string> towns = { "london", "glasGow", "HARWICH",
                                       "NoTtInGHaM","DERBy","BriSToL" };

    for (auto& s : towns) {
        stolower(s);
        std::cout << std::left << std::setw(12) << s << "  (";
        stoupper(s);
        std::cout << s << ")\n";
    }
}

(note: the << std::left << std::setw(12) provided by the <iomanip> header simply allow the output to be in tidy tabular form)

Putting it altogether and adding the needed headers, you could do:

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <cctype>
#include <algorithm>

void stolower (std::string& s)
{
    std::transform (s.begin(), s.end(), s.begin(),
                    [](unsigned char c) { return std::tolower(c); });
}

void stoupper (std::string& s)
{
    std::transform (s.begin(), s.end(), s.begin(),
                    [](unsigned char c) { return std::toupper(c); });
}

int main (void) {

    std::vector<std::string> towns = { "london", "glasGow", "HARWICH",
                                       "NoTtInGHaM","DERBy","BriSToL" };

    for (auto& s : towns) {
        stolower(s);
        std::cout << std::left << std::setw(12) << s << "  (";
        stoupper(s);
        std::cout << s << ")\n";
    }
}

Example Use/Output

$ ./bin/strcasetransform
london        (LONDON)
glasgow       (GLASGOW)
harwich       (HARWICH)
nottingham    (NOTTINGHAM)
derby         (DERBY)
bristol       (BRISTOL)

Where each string in your vector is first converted to lowercase, and then to uppercase and output as shown above. There are many, many ways you can put the pieces together. Getting familiar with the different tools available to apply changes to each member (element) in a container using the niceties provided by the Standard Template Library, can make your work shorter and generally more robust. Let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85