我想将A转换std::string
为小写。我知道该函数tolower()
,但是在过去,我对该函数存在问题,无论如何它都不是理想的选择,因为与a std::string
一起使用需要对每个字符进行迭代。
有没有一种替代方法可以100%地起作用?
我想将A转换std::string
为小写。我知道该函数tolower()
,但是在过去,我对该函数存在问题,无论如何它都不是理想的选择,因为与a std::string
一起使用需要对每个字符进行迭代。
有没有一种替代方法可以100%地起作用?
Answers:
改编自不太常见的问题:
#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这样的多字节编码的情况下。
char
给::tolower(int)
。)您需要确保您不传递负值。
::tolower
可能会崩溃,它是用于非ASCII输入的UB。
#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);
to_lower_copy
tl; dr
使用ICU库。如果您不这样做,则在您可能甚至不知道存在的情况下,转换例程将默默中断。
首先,您必须回答一个问题:您的编码是std::string
什么?是ISO-8859-1吗?还是ISO-8859-8?还是Windows Codepage 1252?您用来转换成小写字母的任何内容都知道吗?(或者,字符超过会失败0x7f
吗?)
如果您将UTF-8(8位编码中唯一的明智选择)与std::string
用作容器,则您已经在欺骗自己,认为自己仍然可以控制一切,因为您正在容器中存储多字节字符序列那不知道多字节的概念。即使是像.substr()
滴答作响的定时炸弹一样简单的事情。(因为拆分多字节序列将导致无效(子)字符串。)
而且,只要您尝试std::toupper( 'ß' )
使用任何编码进行类似的操作,都将遇到很大的麻烦。(因为用标准库根本不可能做到“正确”,因为标准库只能传递一个结果字符,在"SS"
这里不是必需的。)[1]另一个例子是std::tolower( 'I' )
,根据地区的不同,它会产生不同的结果。在德国,'i'
这是正确的;在土耳其,'ı'
(拉丁文小写字母I)是预期的结果(同样,以UTF-8编码超过一个字节)。另一个示例是希腊文Sigma,大写'∑'
,小写'σ'
...除了单词结尾处是'ς'
。
因此,任何一次在一个字符上或更糟的是一次在一个字节上工作的大小写转换都被设计破坏了。
再有就是点的标准库,它是能够做,取决于其语言环境的支持你的软件上运行的计算机上...和你做什么,如果它是不是?
因此,您真正要寻找的是一个能够正确处理所有这些问题的字符串类,并且不是任何std::basic_string<>
变体。
(C ++ 11注:std::u16string
和std::u32string
是更好的,但还不够完善C ++ 20带来的std::u8string
,但这一切都为指定编码在其他许多方面,他们仍然一无所知的Unicode力学,像正常化,整理,...。 )
虽然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()
{
/* "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>
基于解决方案的解决方案。
[1]在2017年,德国拼字法理事会裁定可以正式使用“ẞ” U + 1E9E拉丁大写字母SHARP S,作为传统“ SS”转换旁边的一种选择,以避免歧义,例如在护照中(姓名大写) )。我美丽的例子,由于委员会的决定而过时了。
toupper
并且tolower
仍然可以在单个字符上工作。字符串类仍然没有归一化的概念(例如,“ü”被编码为“带有音符的u”还是“ u +组合音符”),或者字符串可以分隔或不分隔。清单继续。u8string(与其他标准字符串类一样)适合“通过”。但是,如果要处理 Unicode,则需要 ICU。
使用基于范围的C ++ 11的for循环,一个简单的代码将是:
#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);
}
如果字符串包含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
这是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;
}
据我所知,Boost库在性能方面确实很差。我已经测试了他们对STL的unordered_map,它平均慢了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,因此我真的不需要在我的计算机上对其进行测试。
将字符串转换为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;
}
std::ctype::tolower()
标准C ++本地化库中的代码将为您正确执行此操作。这是从下面的参考页中摘录的示例
#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";
}
const
什么?这似乎使它更加混乱(例如,看起来您不能使用f.tolower()
),因为您需要将字符放入新的字符串中。您会为运营商使用transform()
诸如此类的东西std::bind1st( std::mem_fun() )
吗?
tolower
with locale
参数中,对的隐式调用use_facet
似乎是性能瓶颈。我的一位同事通过boost::iequals
用一个use_facet
仅在循环外被调用一次的版本替换(存在此问题),就已经实现了100%的速度提高。
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);
有一种方法可以将大写转换为小写而不用执行if tests,这很简单。isupper()函数/宏对clocale.h的使用应解决与您的位置有关的问题,但是如果没有,您可以随时调整UtoL []以适应您的内心需求。
假设C的字符实际上只是8位整数(暂时忽略宽字符集),则可以创建一个256字节的数组,其中包含另一组字符,然后在转换函数中将字符串中的字符用作下标到转换数组。
但是,可以使用大写数组成员BYTE int值代替小写字符,而不是1对1映射。您可能会在这里发现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;
}
同时,这种方法将允许您重新映射要更改的任何其他字符。
这种方法在现代处理器上运行时具有一个巨大的优势,因为没有测试包含分支,因此无需进行分支预测。这样可以将CPU的分支预测逻辑保存为其他循环,并可以防止流水线停顿。
这里的一些人可能会认识到这种方法与用于将EBCDIC转换为ASCII的方法相同。
由于没有一个答案中提到的即将到来的范围库,这是因为在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>
和必需的Ranges标头。)
注意:使用unsigned char
lambda作为参数是受cppreference启发的,它指出:
与from中的所有其他函数一样
<cctype>
,std::tolower
如果参数的值既不能表示unsigned char
也不等于,则的行为是不确定的EOF
。为了安全地将这些函数与Plainchar
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
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; }
我自己的模板函数执行大写/小写。
#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);
}
towlower
支持UTF-16的宽字符。
如果您想简单一些,可以使用以下宏技术:
#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字符。
void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
x
可能是一个有效的表达式,恰好可以正确编译,但是由于宏的原因,将给出完全虚假的结果。
// 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;
}
在Microsoft平台上,您可以使用以下strlwr
功能族:http : //msdn.microsoft.com/zh-cn/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 );
}
使用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");
复制是因为不允许改善答案。非常感谢
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
说明:
for(auto& c : test)
是基于范围的for循环类型:
for (
range_declaration
:
range_expression
)
loop_statement
range_declaration
:auto& c
这里的自动说明符用于自动类型推导。因此,从变量初始值设定项中推导出类型。
range_expression
:test
在这种情况下,范围是string的字符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;
}
对于基于字符的字符串操作:对于字符串中的每个字符
这可能是将大写转换为小写,反之亦然的另一个简单版本。我使用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抄写错误,只有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;
}