我想将std::string
转换为小写。我知道函数tolower()
,但是在过去我遇到过这个函数的问题,并且它很难理想,因为使用std::string
需要迭代每个字符。
有没有一种方法可以100%的时间运作?
改编自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); });
如果不迭代每个角色,你真的不会离开。否则无法知道字符是小写还是大写。
如果你真的讨厌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这样的多字节编码。
来自标准C ++ Localization库的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";
}
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);
有一种方法可以将大写字母转换为低级而不进行测试,而且非常简单。 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的方法相同。
如果你想要一些简单的东西,这是一个宏技术:
#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字符。
// 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;
}
有没有一种方法可以100%的时间运作?
没有
在选择小写方法之前,您需要先问几个问题。
一旦您获得了这些问题的答案,您就可以开始寻找适合您需求的解决方案。没有一种适合所有人适合所有人的尺寸!
由于没有一个答案提到即将推出的Ranges库,它可以在C ++ 20以后的标准库中找到,目前可以单独使用on 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>
和所需的Ranges标题。)
注意:使用unsigned char
作为lambda的参数受到cppreference的启发,其中指出:
与
<cctype>
的所有其他函数一样,如果参数的值既不能表示为std::tolower
也不等于unsigned char
,则EOF
的行为是不确定的。要使用普通char
s(或signed char
s)安全地使用这些函数,首先应将参数转换为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; }
在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 );
}
#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/中搜索'to_lower_case')
fplus::to_lower_case(std::string("ABC")) == std::string("abc");
Boost provides a string algorithm for this:
#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);
复制,因为不允许改善答案。谢谢你
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
说明:
for(auto& c : test)
是这种类型的range-based for loop
for (
range_declaration
:
range_expression
)
loop_statement
:
range_declaration
:auto& c
这里auto specifier用于自动类型扣除。因此,类型从变量初始值设定项中扣除。range_expression
:test
在这种情况下,范围是字符串test
的字符。字符串test
的字符在for循环中通过标识符c
作为参考。
C ++没有为字符串实现tolower或toupper方法,但它可用于char。可以轻松读取字符串的每个字符串,将其转换为必需的大小写并将其放回字符串中。不使用任何第三方库的示例代码:
#include<iostream>
int main(){
std::string str = std::string("How IS The Josh");
for(char &ch : str){
ch = std::tolower(ch);
}
std::cout<<str<<std::endl;
return 0;
}
对于字符串的基于字符的操作:For every character in string
我自己的模板函数,执行大小写。
#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);
}
这可能是另一个将大写转换为小写的简单版本,反之亦然。我使用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;
}
注意:如果有特殊字符,则需要使用条件检查进行处理。
我试过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;
}
//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
tl;博士
使用ICU library。
首先,您必须回答一个问题:std::string
的编码是什么?是ISO-8859-1吗?或者ISO-8859-8?或Windows代码页1252?无论你用什么来转换大写到小写都知道吗? (或者它是否因为0x7f
上的角色而失败?)
如果你使用std::string
作为容器使用UTF-8(8位编码中唯一合理的选择),你已经欺骗自己相信你仍然可以控制事物,因为你正在存储多字节字符序列。不知道多字节概念的容器。甚至像.substr()
这样简单的东西也是一个滴答作响的定时炸弹。 (因为拆分多字节序列将导致无效(子)字符串。)
一旦你尝试像std::toupper( 'ß' )
这样的东西,在任何编码中,你都会陷入困境。 (因为根本不可能使用标准库“正确”执行此操作,标准库只能提供一个结果字符,而不是此处所需的"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的包装。如果使用ICU支持编译Boost ...如果不是,则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年,德国正字法委员会裁定,“ẞ”U + 1E9E拉丁文大写字母SHARP S可以正式使用,作为传统“SS”转换旁边的选项,以避免歧义,例如:在护照中(名称大写)。我美丽的例子,由委员会决定过时了......
如果字符串包含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的回复的后续行动:如果您想将转换结果放在另一个字符串中,则需要在调用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;
}
另一种使用基于范围的循环与参考变量的方法
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
cout<<test<<endl;
据我所知,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所以我真的不需要在我的机器上测试它。
在不打扰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;
}