我想将std::string
转换为小写。我知道函数tolower()
,但是在过去我遇到过这个函数的问题,因为使用std::string
需要迭代每个字符,所以它几乎不理想。
有没有一种方法可以在100%的时间内起作用?
答案 0 :(得分:828)
来自this:
#include <algorithm>
#include <string>
std::string data = "Abc";
std::transform(data.begin(), data.end(), data.begin(), ::tolower);
你真的不会逃避迭代每个角色。否则无法知道该字符是小写还是大写。
如果你真的讨厌tolower()
,这是一个我不建议您使用的非便携式替代方案:
char easytolower(char in) {
if(in <= 'Z' && in >= 'A')
return in - ('Z' - 'z');
return in;
}
std::transform(data.begin(), data.end(), data.begin(), easytolower);
请注意::tolower()
只能执行每单字节字符替换,这对于许多脚本来说都是不合适的,特别是如果使用像UTF-8这样的多字节编码。
答案 1 :(得分:296)
有一个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);
答案 2 :(得分:202)
<强> TL;博士强>
使用ICU library。
首先,您必须回答一个问题:std::string
的编码是什么?是ISO-8859-1吗?或者ISO-8859-8?或Windows代码页1252? 你用来转换大写到小写的内容是否知道?(或者对0x7f
以上的字符惨遭失败?)
如果你使用std::string
作为容器使用UTF-8(8位编码中唯一合理的选择),那么你已经在欺骗自己相信你仍然掌控着事物,因为你是将多字节字符序列存储在不知道多字节概念的容器中。即使像.substr()
这样简单的东西也是一个滴答作响的定时炸弹。 (因为拆分多字节序列将导致无效(子)字符串。)
只要您尝试使用std::toupper( 'ß' )
,任何编码,就会遇到麻烦。 (因为它根本无法做到这一点&#34;正确&#34;使用标准库,它只能提供一个结果字符,而不是此处所需的"SS"
。)[1]另一个例子是std::tolower( 'I' )
,它应该产生不同的结果,具体取决于语言环境。在德国,'i'
是正确的;在土耳其,'ı'
(LATIN SMALL LETTER DOTLESS I)是预期的结果(在UTF-8编码中也是一个以上的字节)。
然后有一点是标准库取决于运行软件的机器上支持的哪些语言环境...如果它不是,你会怎么做?
那么真正寻找的是一个能够正确处理所有这些的字符串类,而不是 std::string
强>
(C ++ 11注意:std::u16string
和std::u32string
更好,但仍然不完美。)
虽然Boost 看起来很好,但API明智,Boost.Locale基本上是ICU的包装器。 如果 Boost 已编译且ICU支持...如果不是,则Boost.Locale仅限于为标准库编译的语言环境支持。
相信我,得到 Boost与ICU一起编译有时候真的很痛苦。 (Windows没有预编译的二进制文件,因此您必须将它们与您的应用程序一起提供,并且 打开一整套新的蠕虫......)
所以我个人建议直接从马的口中获得完整的Unicode支持并直接使用ICU库:
#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>
#include <iostream>
int main()
{
char const * someString = "Eidenges\xe4\xdf";
icu::UnicodeString someUString( someString, "ISO-8859-1" );
// Setting the locale explicitly here for completeness.
// Usually you would use the user-specified system locale.
std::cout << someUString.toLower( "de_DE" ) << "\n";
std::cout << someUString.toUpper( "de_DE" ) << "\n";
return 0;
}
编译(在此示例中使用G ++):
g++ -Wall example.cpp -licuuc -licuio
这给出了:
eidengesäß
EIDENGESÄSS
[1] 2017年,德国正字法委员会裁定&#34;ẞ&#34; U + 1E9E LATIN CAPITAL LETTER SHARP S可以正式使用,作为传统&#34; SS&#34;转换以避免歧义,例如在护照中(名称大写)。我的漂亮的例子,由委员会的决定淘汰了......
答案 3 :(得分:29)
如果字符串包含ASCII范围之外的UTF-8字符,则boost :: algorithm :: to_lower将不会转换这些字符。当涉及UTF-8时,最好使用boost :: locale :: to_lower。见http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html
答案 4 :(得分:25)
使用基于范围的C ++循环for 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);
}
答案 5 :(得分:14)
这是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 space
destinationString.resize(sourceString.size());
// Convert the source string to lower case
// storing the result in destination string
std::transform(sourceString.begin(),
sourceString.end(),
destinationString.begin(),
::tolower);
// Output the result of the conversion
std::cout << sourceString
<< " -> "
<< destinationString
<< std::endl;
}
答案 6 :(得分:8)
使用基于范围的for循环与参考变量
的另一种方法string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
cout<<test<<endl;
答案 7 :(得分:7)
据我所知,Boost库的性能非常糟糕。我已经测试了他们的unordered_map到STL,平均慢了3倍(最好的情况2,最差的是10次)。此算法看起来也太低了。
差异是如此之大,以至于我确信你需要做的任何事情tolower
使其等于提升“满足你的需求”方式更快而不是提升。
我已经在Amazon EC2上完成了这些测试,因此在测试过程中性能会有所不同,但您仍然可以理解。
./test
Elapsed time: 12365milliseconds
Elapsed time: 1640milliseconds
./test
Elapsed time: 26978milliseconds
Elapsed time: 1646milliseconds
./test
Elapsed time: 6957milliseconds
Elapsed time: 1634milliseconds
./test
Elapsed time: 23177milliseconds
Elapsed time: 2421milliseconds
./test
Elapsed time: 17342milliseconds
Elapsed time: 14132milliseconds
./test
Elapsed time: 7355milliseconds
Elapsed time: 1645milliseconds
-O2
就是这样:
./test
Elapsed time: 3769milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3815milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3643milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 22018milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 3845milliseconds
Elapsed time: 569milliseconds
来源:
string str;
bench.start();
for(long long i=0;i<1000000;i++)
{
str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
boost::algorithm::to_lower(str);
}
bench.end();
bench.start();
for(long long i=0;i<1000000;i++)
{
str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
for(unsigned short loop=0;loop < str.size();loop++)
{
str[loop]=tolower(str[loop]);
}
}
bench.end();
我想我应该在专用机器上进行测试但是我将使用这个EC2所以我真的不需要在我的机器上进行测试。
答案 8 :(得分:5)
std::ctype::tolower()
将正确地为您执行此操作。以下是从tolower reference page
#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";
}
答案 9 :(得分:5)
在不打扰std命名空间的情况下将字符串转换为loweercase的最简单方法如下
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;
}
答案 10 :(得分:3)
Boost的另一种选择是POCO(pocoproject.org)。
POCO提供两种变体:
以下演示了两个版本:
#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);
答案 11 :(得分:2)
有一种方法可以将大写字母转换为较低的而不进行测试,并且非常简单。 isupper()函数/宏使用clocale.h应该处理与你的位置有关的问题,但如果没有,你可以随时调整UtoL []到你心脏的内容。
鉴于C的字符实际上只是8位整数(暂时忽略宽字符集),您可以创建一个包含另一组字符的256字节数组,并在转换函数中使用字符串中的字符作为下载到转换数组中。
不是使用1对1映射,而是为大写数组成员提供小写字符的BYTE int值。您可能会在此处找到islower() and isupper()。
代码看起来像这样......
#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 buffer
while (*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;
}
此方法同时允许您重新映射您想要更改的任何其他字符。
当在现代处理器上运行时,这种方法具有一个巨大的优势,不需要进行分支预测,因为如果包含分支的测试则没有。这为其他循环保存了CPU的分支预测逻辑,并且倾向于防止流水线停顿。
有些人可能认为这种方法与用于将EBCDIC转换为ASCII的方法相同。
答案 12 :(得分:2)
如果你想要一些简单的东西,这是一种宏观技术:
#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对this answer的评论仍然是一个重要的考虑因素,但是,如果您正在处理的不仅仅是ASCII字符。
答案 13 :(得分:2)
尝试此功能:)
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;
}
答案 14 :(得分:1)
我写了这个简单的辅助函数:
#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.
答案 15 :(得分:1)
// 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;
}
答案 16 :(得分:1)
由于所有答案都没有提到即将到来的Ranges库,该库自C ++ 20起在标准库中可用,并且目前分别以range-v3
的形式on GitHub可用,因此我想添加一种方法使用它来执行此转换。
要就地修改字符串:
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>
和必需的Ranges标头。)
注意:cppreference激发了unsigned char
作为lambda参数的使用,它指出:
与
<cctype>
中的所有其他函数一样,如果参数的值既不能表示为std::tolower
也不能等于unsigned char
,则EOF
的行为是不确定的。要在纯char
(或signed char
)中安全地使用这些函数,应首先将参数转换为unsigned char
:char my_tolower(char ch) { return static_cast<char>(std::tolower(static_cast<unsigned char>(ch))); }
类似地,当迭代器的值类型为
char
或signed 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; }
答案 17 :(得分:1)
有没有一种替代方法可以100%地起作用?
否
在选择一种下套管方法之前,您需要问自己几个问题。
一旦您对这些问题有答案,就可以开始寻找适合您需求的解决方案。没有一种尺寸适合所有地方的所有人!
答案 18 :(得分:0)
复制,因为不允许改进答案。谢谢SO
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
<强>解释强>
for(auto& c : test)
是for (
range_declaration
:
{{1}的range-based for loop }} range_expression
)
:
loop_statement
:range_declaration
这里auto specifier用于自动类型推导。因此,类型会从变量初始值设定项中扣除。
auto& c
:range_expression
在这种情况下,范围是字符串test
的字符。
字符串test
的字符在for循环中通过标识符test
作为参考提供。
答案 19 :(得分:0)
为 ASCII 字符串 to_lower 添加一些可选库,这两个库都是生产级别的,并进行了微优化,预计会比此处现有的答案更快(TODO:添加基准测试结果)。
Facebook 的 Folly:
void toLowerAscii(char* str, size_t length)
Google 的 Abseil:
void AsciiStrToLower(std::string* s);
答案 20 :(得分:0)
我自己的模板函数执行大写/小写。
#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 std::move(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 std::move(s2);
}
答案 21 :(得分:0)
C ++没有为字符串实现tolower或toupper方法,但是可用于char。可以轻松读取字符串的每个字符,将其转换为所需的大小写,然后将其放回字符串中。 不使用任何第三方库的示例代码:
margin: 0 auto
对于基于字符的字符串操作:For every character in string
答案 22 :(得分:0)
#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;
}
答案 23 :(得分:0)
在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 copies
char * 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 );
}
答案 24 :(得分:-1)
这可能是另一个将大写转换为小写的简单版本,反之亦然。我使用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 convert
char upperA = 'A';
char lowerA = 'a';
cout << (int)upperA << endl; // ASCII value of 'A' -> 65
cout << (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;
}
注意:如果有特殊字符,则需要使用条件检查来处理。
答案 25 :(得分:-8)
我尝试了std :: transform,我得到的是令人讨厌的stl criptic编译错误,只有200年前的德鲁伊才能理解(不能转换为flibidi flabidi flu)
这很好用,可以轻松调整
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;
}
答案 26 :(得分:-12)
//You can really just write one on the fly whenever you need one.
#include <string>
void _lower_case(std::string& s){
for(unsigned short l = s.size();l;s[--l]|=(1<<5));
}
//Here is an example.
//http://ideone.com/mw2eDK