如何将std::字符串的实例转换为小写

我想将std::string转换为小写。我知道函数tolower()。然而,在过去,我遇到了这个函数的问题,无论如何它都不理想,因为将它与std::string一起使用需要迭代每个字符。

有没有一种方法可以100%的工作?

1220674 次浏览

Boost为此提供了一个字符串算法

#include <boost/algorithm/string.hpp>
std::string str = "HELLO, WORLD!";boost::algorithm::to_lower(str); // modifies str

或者,对于不到位

#include <boost/algorithm/string.hpp>
const std::string str = "HELLO, WORLD!";const std::string lower_str = boost::algorithm::to_lower_copy(str);

改编自不常问的问题

#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); });

不遍历每个字符,你真的无法逃脱。否则无法知道字符是小写还是大写。

如果你真的讨厌tolower(),这里有一个专门的ASCII替代方案,我不建议你使用:

char asciitolower(char in) {if (in <= 'Z' && in >= 'A')return in - ('Z' - 'z');return in;}
std::transform(data.begin(), data.end(), data.begin(), asciitolower);

请注意,tolower()只能执行每个单字节字符的替换,这不适合许多脚本,特别是如果使用像UTF-8这样的多字节编码。

如果字符串包含ASCII范围之外的UTF-8字符,那么::a算法::to_lower将不会转换这些字符。当涉及UTF-8时,最好使用提升::本地e::to_lower。请参阅http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html

这是Stefan Mai响应的后续:如果您想将转换的结果放置在另一个字符串中,您需要在调用std::transform之前预先分配其存储空间。由于STL将转换后的字符存储在目标迭代器中(在循环的每次迭代中递增),目标字符串不会自动调整大小,并且您有内存踩踏的风险。

#include <string>#include <algorithm>#include <iostream>
int main (int argc, char* argv[]){std::string sourceString = "Abc";std::string destinationString;
// Allocate the destination spacedestinationString.resize(sourceString.size());
// Convert the source string to lower case// storing the result in destination stringstd::transform(sourceString.begin(),sourceString.end(),destinationString.begin(),::tolower);
// Output the result of the conversionstd::cout << sourceString<< " -> "<< destinationString<< std::endl;}

Boost的替代方案是POCO(pocoproject.org)。

POCO提供了两种变体:

  1. 第一个变体在不改变原始字符串的情况下复制。
  2. 第二个变体改变了原来的字符串。
    “就地”版本的名称中总是有“InPlace”。

两个版本如下所示:

#include "Poco/String.h"using namespace Poco;
std::string hello("Stack Overflow!");
// Copies "STACK OVERFLOW!" into 'newString' without altering 'hello.'std::string newString(toUpper(hello));
// Changes newString in-place to read "stack overflow!"toLowerInPlace(newString);

使用基于范围的for循环C++11一个更简单的代码是:

#include <iostream>       // std::cout#include <string>         // std::string#include <locale>         // std::locale, std::tolower
int main (){std::locale loc;std::string str="Test String.\n";
for(auto elem : str)std::cout << std::tolower(elem,loc);}

有一种方法可以将大写转换为小写不做if测试,而且非常简单。isup()函数/宏使用clocale. h应该可以解决与您的位置相关的问题,但如果没有,您可以随时调整UtoL[]以满足您的需求。

鉴于C的字符实际上只是8位整数(暂时忽略宽字符集),您可以创建一个包含替代字符集的256字节数组,并在转换函数中使用字符串中的字符作为转换数组的下标。

不过,不要使用1 for 1映射,而是为大写数组成员提供小写字符的BYTE int值。您可能会发现islower()和isuper()在这里很有用。

输入图片描述

代码看起来像这样…

#include <clocale>static char UtoL[256];// ----------------------------------------------------------------------------void InitUtoLMap()  {for (int i = 0; i < sizeof(UtoL); i++)  {if (isupper(i)) {UtoL[i] = (char)(i + 32);}   else    {UtoL[i] = i;}}}// ----------------------------------------------------------------------------char *LowerStr(char *szMyStr) {char *p = szMyStr;// do conversion in-place so as not to require a destination bufferwhile (*p) {        // szMyStr must be null-terminated*p = UtoL[*p];p++;}return szMyStr;}// ----------------------------------------------------------------------------int main() {time_t start;char *Lowered, Upper[128];InitUtoLMap();strcpy(Upper, "Every GOOD boy does FINE!");
Lowered = LowerStr(Upper);return 0;}

同时,这种方法将允许您重新映射您希望更改的任何其他字符。

这种方法在现代处理器上运行时有一个巨大的优势,不需要进行分支预测,因为没有包含分支的if测试。这节省了CPU对其他循环的分支预测逻辑,并倾向于防止流水线停滞。

这里的一些人可能会认为这种方法与将EBCDIC转换为ASCII的方法相同。

tl; dr

使用ICU库。如果不这样做,您的转换例程将在您可能甚至不知道存在的情况下静默中断。


首先,你必须回答一个问题:你的std::string编码是什么?是ISO-8859-1吗?或者是ISO-8859-8?或者是Windows代码页1252?你用来转换大写字母到小写字母的东西知道吗?(或者它对0x7f以上的字符失败得很惨?)

如果您使用UTF-8(8位编码中唯一明智的选择)和std::string作为容器,如果您认为自己仍然可以控制事物,您已经在欺骗自己。您将多字节字符序列存储在一个不知道多字节概念的容器中,您可以对其执行的大多数操作也不知道!即使像.substr()这样简单的事情也可能导致无效(子)字符串,因为您在多字节序列的中间拆分。

一旦你尝试std::toupper( 'ß' )std::tolower( 'Σ' )0编码中的std::tolower( 'Σ' ),你就会遇到麻烦。因为1),标准一次只对一个字符进行操作,所以它根本无法正确地将ß变成SS。并且2),标准一次只对一个字符进行操作,所以它无法决定Σ是在单词的中间(σ是正确的),还是在结尾(ς)。另一个例子是std::tolower( 'I' ),它应该产生不同的结果std::tolower( 'Σ' )1——几乎在你期望i的任何地方,但在土耳其ı(拉丁小写圆点I)是正确的答案(同样,在UTF-8编码中不止一个字节)。

所以,任何一次对一个字符起作用的大小写转换,或者更糟糕的是,一次对一个字节起作用,都是被设计破坏的。这包括此时存在的所有std::变体。

然后有一点,标准库,对于它能够做的事情,取决于您的软件运行的机器上哪些语言环境是支持……如果您的目标语言环境是客户端机器上不支持的,您该怎么办?

所以你要找的是一个能够正确处理所有这些的字符串类,这是不是任何#0变体

(C++11注:std::u16stringstd::u32string更好,但仍不完美。C++20带来了std::u8string,但所有这些都指定了编码。在许多其他方面,他们仍然不了解Unicode机制,如归一化,排序规则,…)

虽然Boost看起来很好,API明智,Boost. Locale基本上是围绕ICU的包装器。如果 Boost是编译,具有ICU支持……如果不是,Boost. Locale仅限于为标准库编译的语言环境支持。

相信我,得到 Boost编译与ICU有时可能是一个真正的痛苦。(没有包含ICU的Windows预编译二进制文件,所以你必须将它们与你的应用程序一起提供,打开了一个全新的蠕虫罐…)

所以我个人建议直接从马嘴里获得完整的Unicode支持,并直接使用ICU库:

#include <unicode/unistr.h>#include <unicode/ustream.h>#include <unicode/locid.h>
#include <iostream>
int main(){/*                          "Odysseus" */char const * someString = u8"ΟΔΥΣΣΕΥΣ";icu::UnicodeString someUString( someString, "UTF-8" );// Setting the locale explicitly here for completeness.// Usually you would use the user-specified system locale,// which *does* make a difference (see ı vs. i above).std::cout << someUString.toLower( "el_GR" ) << "\n";std::cout << someUString.toUpper( "el_GR" ) << "\n";return 0;}

编译(在本例中使用G++):

g++ -Wall example.cpp -licuuc -licuio

这提供了:

ὀδυσσεύς

请注意,在单词的中间进行了σ<->σ转换,在单词的末尾进行了σ<->π转换。没有基于<algorithm>的解决方案可以为您提供这一点。

我尝试了std::转换,我得到的只是可恶的stl脚本编译错误,只有200年前的德鲁伊才能理解(无法从flabidi flabidi流感转换)

这工作正常,可以很容易地调整

string LowerCase(string s){int dif='a'-'A';for(int i=0;i<s.length();i++){if((s[i]>='A')&&(s[i]<='Z'))s[i]+=dif;}return s;}
string UpperCase(string s){int dif='a'-'A';for(int i=0;i<s.length();i++){if((s[i]>='a')&&(s[i]<='z'))s[i]-=dif;}return s;}

在Microsoft平台上,您可以使用strlwr系列函数:http://msdn.microsoft.com/en-us/library/hkxwh33z.aspx

// crt_strlwr.c// compile with: /W3// This program uses _strlwr and _strupr to create// uppercase and lowercase copies of a mixed-case string.#include <string.h>#include <stdio.h>
int main( void ){char string[100] = "The String to End All Strings!";char * copy1 = _strdup( string ); // make two copieschar * copy2 = _strdup( string );
_strlwr( copy1 ); // C4996_strupr( copy2 ); // C4996
printf( "Mixed: %s\n", string );printf( "Lower: %s\n", copy1 );printf( "Upper: %s\n", copy2 );
free( copy1 );free( copy2 );}

将string转换为loweercase而无需担心std命名空间的最简单方法如下

1:带/不带空格的字符串

#include <algorithm>#include <iostream>#include <string>using namespace std;int main(){string str;getline(cin,str);//------------function to convert string into lowercase---------------transform(str.begin(), str.end(), str.begin(), ::tolower);//--------------------------------------------------------------------cout<<str;return 0;}

2:没有空格的字符串

#include <algorithm>#include <iostream>#include <string>using namespace std;int main(){string str;cin>>str;//------------function to convert string into lowercase---------------transform(str.begin(), str.end(), str.begin(), ::tolower);//--------------------------------------------------------------------cout<<str;return 0;}

标准C++本地化库中的std::ctype::tolower()将正确地为您执行此操作。这是从托尔威尔参考页中提取的示例

#include <locale>#include <iostream>
int main () {std::locale::global(std::locale("en_US.utf8"));std::wcout.imbue(std::locale());std::wcout << "In US English UTF-8 locale:\n";auto& f = std::use_facet<std::ctype<wchar_t>>(std::locale());std::wstring str = L"HELLo, wORLD!";std::wcout << "Lowercase form of the string '" << str << "' is ";f.tolower(&str[0], &str[0] + str.size());std::wcout << "'" << str << "'\n";}

如果你想要一些简单的东西,这里有一个宏技术:

#define STRTOLOWER(x) std::transform (x.begin(), x.end(), x.begin(), ::tolower)#define STRTOUPPER(x) std::transform (x.begin(), x.end(), x.begin(), ::toupper)#define STRTOUCFIRST(x) std::transform (x.begin(), x.begin()+1, x.begin(),  ::toupper); std::transform (x.begin()+1, x.end(),   x.begin()+1,::tolower)

但是,请注意,@AndreasSpindler对这个答案的评论仍然是一个重要的考虑因素,但是,如果您正在处理不仅仅是ASCII字符的内容。

另一种使用基于范围的for循环和引用变量的方法

string test = "Hello World";for(auto& c : test){c = tolower(c);}
cout<<test<<endl;
// tolower example (C++)#include <iostream>       // std::cout#include <string>         // std::string#include <locale>         // std::locale, std::tolower
int main (){std::locale loc;std::string str="Test String.\n";for (std::string::size_type i=0; i<str.length(); ++i)std::cout << std::tolower(str[i],loc);return 0;}

更多信息:http://www.cplusplus.com/reference/locale/tolower/

代码段

#include<bits/stdc++.h>using namespace std;

int main (){ios::sync_with_stdio(false);
string str="String Convert\n";
for(int i=0; i<str.size(); i++){str[i] = tolower(str[i]);}cout<<str<<endl;
return 0;}

使用fplus库中的fplus::to_lower_case()

fplus API搜索中搜索to_lower_case

示例:

fplus::to_lower_case(std::string("ABC")) == std::string("abc");

复制,因为它是不允许提高答案。谢谢


string test = "Hello World";for(auto& c : test){c = tolower(c);}

说明:

for(auto& c : test)基于范围的for循环的类型
for (range_declaration:range_expression)for (0:

  1. range_declarationauto& c
    这里自动说明符用于自动类型推导。因此类型从变量初始化器中扣除。

  2. range_expressiontest
    在这种情况下,范围是字符串test的字符。

字符串test的字符可作为通过标识符c的for循环内的引用。

这可能是另一个将大写转换为小写,反之亦然的简单版本。我使用VS2017社区版本编译此源代码。

#include <iostream>#include <string>using namespace std;
int main(){std::string _input = "lowercasetouppercase";#if 0// My idea is to use the ascii value to convertchar upperA = 'A';char lowerA = 'a';
cout << (int)upperA << endl; // ASCII value of 'A' -> 65cout << (int)lowerA << endl; // ASCII value of 'a' -> 97// 97-65 = 32; // Difference of ASCII value of upper and lower a#endif // 0
cout << "Input String = " << _input.c_str() << endl;for (int i = 0; i < _input.length(); ++i){_input[i] -= 32; // To convert lower to upper#if 0_input[i] += 32; // To convert upper to lower#endif // 0}cout << "Output String = " << _input.c_str() << endl;
return 0;}

注意:如果有特殊字符,则需要使用条件检查来处理。

有没有一种方法可以100%的工作?

有几个问题,你需要问自己之前,选择一个低卡方法。

  1. 字符串是如何编码的?纯ASCII?UTF-8?某种形式的扩展ASCII遗留编码?
  2. 小写是什么意思?大小写映射规则因语言而异!您想要本地化为用户语言环境的东西吗?您想要在您的软件运行的所有系统上表现一致的东西吗?您只想小写ASCII字符并传递其他一切吗?
  3. 有哪些图书馆可用?

一旦你对这些问题有了答案,你就可以开始寻找适合你需求的解决方案了。没有适合所有人的一刀切!

C++没有为std::string实现tolowertoupper方法,但它可用于char。可以轻松读取字符串的每个字符,将其转换为所需的大小写并将其放回字符串。不使用任何第三方库的示例代码:

#include<iostream>    
int main(){std::string str = std::string("How ARe You");for(char &ch : str){ch = std::tolower(ch);}std::cout<<str<<std::endl;return 0;}

对于基于字符的字符串操作:对于字符串中的每个字符

由于没有一个答案提到即将到来的Ranges库,该库自C++20起在标准库中可用,目前单独可用在github作为range-v3,我想添加一种使用它执行此转换的方法。

要就地修改字符串:

str |= action::transform([](unsigned char c){ return std::tolower(c); });

生成一个新的字符串:

auto new_string = original_string| view::transform([](unsigned char c){ return std::tolower(c); });

(不要忘记#include <cctype>和所需的范围标头。

注意:使用unsigned char作为lambda的参数是受用户偏好的启发,它指出:

<cctype>中的所有其他函数一样,如果参数的值既不能表示为unsigned char也不等于EOF,则std::tolower的行为是未定义的。要安全地使用chars(或signed chars)的这些函数,参数应首先转换为unsigned char

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

类似地,当迭代器的值类型为charsigned char时,它们不应该直接与标准算法一起使用。相反,首先将值转换为unsigned char

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;}

我自己的模板功能,执行大写/小写。

#include <string>#include <algorithm>
////  Lowercases string//template <typename T>std::basic_string<T> lowercase(const std::basic_string<T>& s){std::basic_string<T> s2 = s;std::transform(s2.begin(), s2.end(), s2.begin(), tolower);return s2;}
//// Uppercases string//template <typename T>std::basic_string<T> uppercase(const std::basic_string<T>& s){std::basic_string<T> s2 = s;std::transform(s2.begin(), s2.end(), s2.begin(), toupper);return s2;}

试试这个功能:)

string toLowerCase(string str) {
int str_len = str.length();
string final_str = "";
for(int i=0; i<str_len; i++) {
char character = str[i];
if(character>=65 && character<=92) {
final_str += (character+32);
} else {
final_str += character;
}
}
return final_str;
}

我写了这个简单的辅助函数:

#include <locale> // tolower
string to_lower(string s) {for(char &c : s)c = tolower(c);return s;}

用法:

string s = "TEST";cout << to_lower("HELLO WORLD"); // output: "hello word"cout << to_lower(s); // won't change the original variable.

为ASCII字符串to_lower添加一些可选库,这两个库都是生产级别的,并且具有微优化,预计会比这里现有的答案更快(待办事项:添加基准测试结果)。

Facebook的愚蠢

void toLowerAscii(char* str, size_t length)

Google的Abseil

void AsciiStrToLower(std::string* s);

我写了一个适用于任何字符串的模板版本:

#include <type_traits> // std::decay#include <ctype.h>    // std::toupper & std::tolower

template <class T = void> struct farg_t { using type = T; };template <template<typename ...> class T1,class T2> struct farg_t <T1<T2>> { using type = T2*; };//---------------
template<class T, class T2 =typename std::decay< typename farg_t<T>::type >::type>void ToUpper(T& str) { T2 t = &str[0];for (; *t; ++t) *t = std::toupper(*t); }

template<class T, class T2 = typename std::decay< typenamefarg_t<T>::type >::type>void Tolower(T& str) { T2 t = &str[0];for (; *t; ++t) *t = std::tolower(*t); }

使用gcc编译器测试:

#include <iostream>#include "upove_code.h"
int main(){
std::string str1 = "hEllo ";char str2 [] = "wOrld";
ToUpper(str1);ToUpper(str2);std::cout << str1 << str2 << '\n';Tolower(str1);Tolower(str2);std::cout << str1 << str2 << '\n';return 0;}

输出:

>HELLO WORLD>>hello world

看看优秀的c++17cpp-Unicodelbgithub)。它是单文件和只有标头的。

#include <exception>#include <iostream>#include <codecvt>
// cpp-unicodelib, downloaded from GitHub#include "unicodelib.h"#include "unicodelib_encodings.h"
using namespace std;using namespace unicode;
// converter that allows displaying a Unicode32 stringwstring_convert<codecvt_utf8<char32_t>, char32_t> converter;
std::u32string  in = U"Je suis là!";cout << converter.to_bytes(in) << endl;
std::u32string  lc = to_lowercase(in);cout << converter.to_bytes(lc) << endl;

产出

Je suis là!je suis là!

使用此代码更改c++中字符串的大小写。

#include<bits/stdc++.h>
using namespace std;
int main(){string a = "sssAAAAAAaaaaDas";transform(a.begin(),a.end(),a.begin(),::tolower);cout<<a;}

Google的absl库有absl::AsciiStrToLower/absl::AsciiStrToUpper