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_back
s 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 wantText[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
How to Determine Opencv Version
How to Have All the Inputs on the Same Line C++
Count How Many Times Elements in an Array Are Repeated
Fastest Way to Check If a File Exists Using Standard C++/C++11,14,17/C
How to Align Text to the Right Using Cout
Checking the Neighbour Values of Arrays
How to Read in User Entered Comma Separated Integers
Balanced Array Index While Summing an Array from Left and Right
How to Cast Const Uint8_T* to Char*
How to Determine the Boost Version on a System
How to Properly Clean Up Elements from Vectors of Object Pointers
Automatically Add All Files in a Folder to a Target Using Cmake
How to Check Whether a String Contains Every Letter in the Alphabet in C++
Is It Okay to Inherit Implementation from Stl Containers, Rather Than Delegate
Why Do I Get the Same Sequence For Every Run With Std::Random_Device With Mingw Gcc4.8.1