如何将std :: string转换为小写?
我想将一个std::string
转换为小写。 我知道函数tolower()
,但是在过去,我已经有了这个函数的问题,它是不是很理想,因为使用string将需要迭代每个字符。
有100%的时间有效的替代方法吗?
从http://notfaq.wordpress.com/2007/08/04/cc-convert-string-to-upperlower-case/ :
#include <algorithm> #include <string> std::string data = "Abc"; std::transform(data.begin(), data.end(), data.begin(), ::tolower);
你真的不想逃避每个angular色的迭代。 没有办法知道字符是小写字母还是大写字母。
如果你真的讨厌tolower(),这里是一个非可移植的替代品,我不build议你使用:
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()
只能执行单字节字符replace,这对许多脚本来说是不合适的,特别是在使用像UTF-8这样的多字节编码时。
这里有一个Booststringalgorithm:
#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);
TL;博士
使用ICU库 。
首先你必须回答一个问题:什么是你的std::string
的编码 ? 是ISO-8859-1吗? 或者也许ISO-8859-8? 或Windows代码页1252? 不pipe你使用什么来转换大小写知道吗? (或者对于超过0x7f
字符是否失败?)
如果你用std::string
作为容器使用UTF-8(8位编码中唯一的理智select),你已经在欺骗自己,相信你仍然在控制着事物,因为你正在存储一个多字节字符序列在不知道多字节概念的容器中。 即使像.substr()
这样简单的东西也是一个滴答作响的时间炸弹。 (因为拆分多字节序列将导致无效的(子)string。)
只要你尝试了像std::toupper( 'ß' )
这样的编码,你就会陷入困境。 (因为使用标准库只能传递一个结果字符,而不是在这里需要的"SS"
所以这是不可能的。)[1]另一个例子是std::tolower( 'I' )
, 根据语言环境 ,这应该会产生不同的结果。 在德国, 'i'
是对的。 在土耳其, 'ı'
(拉丁小写字母D)是预期的结果。
那么标准库就是取决于你的软件运行在哪台机器上支持哪种语言环境……如果不是,你会怎么做?
所以你真正想要的是一个能够正确处理所有这些的string类, 而不是 std::string
。
(C ++ 11注意: std::u16string
和std::u32string
比较好 ,但还不完美。)
虽然Boost 看起来不错,API明智,Boost.Locale基本上是围绕ICU的包装。 如果 Boost与ICU支持编译 …如果不是,Boost.Locale仅限于为标准库编译的区域支持。
相信我,和ICU一起进行Boost编译可能是一个真正的痛苦。 (Windows没有预编译的二进制文件,所以你不得不将它们与你的应用程序一起提供,这就打开了一整套新的蠕虫…)
所以我个人build议直接从马的口中得到完整的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年,德国正字法委员会裁定:“除了传统的”SS“转换之外,还可以正式使用这个选项,以避免模糊,例如在护照中(名称为大写)。 我的美丽的例子,由委员会的决定过时了
如果string包含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
使用基于范围的循环的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); }
这是Stefan Mai的回应:如果您想将转换结果放在另一个string中,则需要在调用std::transform
之前预先分配其存储空间。 由于STL在目标迭代器中存储转换后的字符(在循环的每次迭代中递增),目标string将不会自动resize,并且可能会导致内存跺脚。
#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; }
据我看到Boost库是非常糟糕的性能明智的。 我testing了他们的无序地图STL,平均慢了3倍(最好的情况2,最差的10倍)。 而且这个algorithm看起来太低。
差异是如此之大,我相信无论你需要做些什么来增加“满足你的需求”,都会比提升更快 。
我已经在亚马逊EC2上做了这些testing,因此在testing过程中性能各不相同,但是您仍然可以看到这个想法。
./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();
我想我应该在专用机器上testing,但是我将使用这个EC2,所以我不需要在我的机器上testing它。
标准C ++本地化库中的std::ctype::tolower()
将正确地为您执行此操作。 这里是一个从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"; }
有一种方法可以将大写字母转换为低字节,而不需要做testing ,而且非常简单。 isupper()函数/macros对clocale.h的使用应该处理与你的位置有关的问题,但是如果不是,你可以随时调整UtoL []到你的内容。
考虑到C的字符实际上只是8位整数(现在忽略宽字符集),您可以创build一个256字节的数组来保存另一组字符,并且在转换函数中使用string中的字符作为下标进入转换数组。
尽pipe不是一对一的映射,但给大写数组成员的小写字符的BYTE int值。 你可能会发现islower()和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; }
这种方法将同时允许您重新映射您想要更改的其他angular色。
这种方法在现代处理器上运行时有一个巨大的优势,不需要进行分支预测,因为如果包含分支的testing没有。 这为其他循环保存了CPU的分支预测逻辑,并且趋向于防止stream水线延迟。
这里的一些人可能认识到这种方法与用于将EBCDIC转换为ASCII的方法相同。
Boost的替代scheme是POCO(pocoproject.org)。
POCO提供了两种变体:
- 第一个变体在不改变原始string的情况下进行复制。
- 第二个变体改变了原来的string。
“In Place”版本的名称中总是有“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);
将string转换为loweercase而不打扰std命名空间的最简单方法如下
1:带/不带空格的string
#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:没有空格的string
#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; }
另一种方法是使用基于范围的参考variables循环
string test = "Hello World"; for(auto& c : test) { c = tolower(c); } cout<<test<<endl;
在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 ); }
如果你想要一些简单的东西,这是一个macros观技术:
#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字符的东西。
// 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 :: to_lower_case()。
(fplus: https : //github.com/Dobiasd/FunctionalPlus 。
在http://www.editgym.com/fplus-api-search/search“to_lower_case”;
fplus::to_lower_case(std::string("ABC")) == std::string("abc");
我做这样的事情…
void toLower(string &str) { for(int i=0;i<strlen(str.c_str());i++) { str[i]= tolower(str[i]); } }
我试过std :: transform,所有我得到的是可恶的stl拙劣的编译错误,只有从200年前的德鲁伊可以理解(不能转换为flibidi flabidistream感)
这工作正常,可以很容易地调整
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; }
//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