How to Convert an Instance of Std::String to Lower Case

How to convert an instance of std::string to lower case

Adapted from Not So Frequently Asked Questions:

#include <algorithm>
#include <cctype>
#include <string>

std::string data = "Abc";
std::transform(data.begin(), data.end(), data.begin(),
[](unsigned char c){ return std::tolower(c); });

You're really not going to get away without iterating through each character. There's no way to know whether the character is lowercase or uppercase otherwise.

If you really hate tolower(), here's a specialized ASCII-only alternative that I don't recommend you use:

char asciitolower(char in) {
if (in <= 'Z' && in >= 'A')
return in - ('Z' - 'z');
return in;
}

std::transform(data.begin(), data.end(), data.begin(), asciitolower);

Be aware that tolower() can only do a per-single-byte-character substitution, which is ill-fitting for many scripts, especially if using a multi-byte-encoding like UTF-8.

converting a string to lowercase/uppercase depending upon the count of upper/lower case characters in it

After your first loop, do this:

string result = (uc>lc) ? toupper(s) : tolower(s);

Can't convert string to lower case

Your use of tolower has undefined behavior! The cppreference page on std::tolower tells you exactly what to do to fix it:

Like all other functions from <cctype>, the behavior of std::tolower is undefined if the argument's value is neither representable as unsigned char nor equal to EOF. To use these functions safely with plain chars (or signed chars), the argument should first be converted to unsigned char:

char my_tolower(char ch) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}

Similarly, they should not be directly used with
standard algorithms when the iterator's value type is char or signed
char. Instead, convert the value to unsigned char first:

std::string str_tolower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(),
// static_cast<int(*)(int)>(std::tolower) // wrong
// [](int c){ return std::tolower(c); } // wrong
// [](char c){ return std::tolower(c); } // wrong
[](unsigned char c){ return std::tolower(c); } // correct
);
return s;
}

So your code should look like this:

transform(a.begin(), a.end(), a.begin(), [](unsigned char c) { return std::tolower(c); });

As for your warning, it's due to the warning level you have set for your compiler. You get around it you can explicitly cast the result:

transform(a.begin(), a.end(), a.begin(), [](unsigned char c) {
return static_cast<char>(std::tolower(c));
});

tolower function for C++ strings

If boost is an option:

#include <boost/algorithm/string.hpp>    

std::string str = "wHatEver";
boost::to_lower(str);

Otherwise, you may use std::transform:

std::string str = "wHatEver";
std::transform(str.begin(), str.end(), str.begin(), ::tolower);

You can also use another function if you have some custom locale-aware tolower.

Converting std::string to Lowercase Inside Function

You are passing the string by value, so the parameter lower in your function is actually a copy of the argument lower in main.

change your code to void low_string(string& lower) and it should work

Transforming a string to lowercase in a new string

std::transform doesn't know containers and will not insert new elements for you.
std::back_inserter provides an iterator that push_backs assigned values:

// DO NOT use this
std::transform(s1.begin(), s1.end(), std::back_inserter(s2), ::toupper);
// ^^^^^^^^^^^^^^^^^^^^^^

However, you should not use the global version of toupper since it's deprecated.
Also you have to cast the argument to unsigned char first, or undefined behavior is easily encountered.

transform does not seem to fit here as you would need a lambda; use

for (auto ch : s1)
s2 += std::toupper((unsigned char)ch);

Demo.

How do I loop through a string and convert to lowercase?

A few points:

  • tolower returns the lowercase character if it exists ('A' becomes
    'a', 'a' is unchanged, '9' is unchanged, etc.)
  • The line Text[i] = Text[i]; does not do anything, you want Text[i]
    = tolower(Text[i]);
  • There is no need to check if each character is lowercase, tolower
    will handle that for you

Simplified:

#include <iostream>

using namespace std;

int main() {
cout << "Enter Text: ";
string Text;
getline(cin, Text);
for (int i = 0; i < Text.length(); i++)
Text[i] = tolower(Text[i]);
cout << "Your text is: ";
cout << Text;
cout << "\n";
}

is it possible to make std::string always hold a lower-case string?

You can write your own char traits and pass it to std::basic_string as second template argument.

Here is a minimal example:

template<typename T>
struct lowercase_char_traits : std::char_traits<T>
{
static T* copy(T* dest, const T* src, std::size_t count )
{
for(size_t i = 0 ; i < count ; ++i)
dest[i] = std::tolower(src[i]);
return dest;
}
static void assign(T & out, T in)
{
out = std::tolower(in);
}
//implement other overload of assign yourself

//note that you may have to implement other functionality
//depending on your requirement
};

And then define a typedef as:

typedef std::basic_string<char, lowercase_char_traits<char>> lowercase;

And here is a test program:

int main() 
{
lowercase s1 = "Hello World";
std::cout << s1.c_str() << std::endl;

lowercase s2 = "HELLO WORLD";
std::cout << std::boolalpha << (s1 == s2) << std::endl;

lowercase s3 = "HELLO";
s3 += " WorL";
s3.append("D");
std::cout << std::boolalpha << (s1 == s3) << std::endl;

std::cout << s2.c_str() << std::endl;
std::cout << s3.c_str() << std::endl;
}

Output:

hello world
true
true
hello world
hello world

Cool, isn't it?

  • Online demo

Note that to have a fully-working lowercase string class, you may need to define other functionality of lowercase_char_traits also, depending on what behavior you want out of such class.

Have a look at the Herb Sutter brilliant article for details and explanation:

  • So you want a case-insensitive string class? Your mission, should you choose to accept it, is to write one.

Hope that helps.



Related Topics



Leave a reply



Submit