c - 如何将std::string转换为小写?

  显示原文与译文双语对照的内容

我想将 std::string 转换为小写。 我知道是什么函数 tolower(),但是在过去我已经出现了问题,且这里函数,它并不是什么理想如一起使用一个字符串还是继续将需要循环访问每个字符。

是否还有一个可以供选择的时间 100%?

时间:

http://notfaq.wordpress.com/1 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);

你真的不会去遍历每个字符。 无法知道字符是小写还是大写。

如果你真的讨厌 tolower(),,我建议你不要使用non-portable选项:


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() 只能进行per-single-byte-character替换,这对于许多脚本来说是ill-fitting的,尤其是当使用multi-byte-encoding像UTF-8的时候。

这里有一个Boost字符串算法:


#include <boost/algorithm/string.hpp> 

std::string str ="HELLO, WORLD!";
boost::algorithm::to_lower(str);

首先你必须回答一个问题: 你的字符串的编码编码是什么? 是 ISO-8859-1或者是 ISO-8859-8? 或者 Windows 代码页 1252?

如果是 UTF-8,你已经在欺骗自己,相信你仍然在控制事情,因为你在一个容器中存储了多字节的概念。 甚至像 .length() 这样简单的东西对你撒谎,.substr() 是一个滴答的timebomb 。 ( 因为拆分多字节序列将导致无效的( 子程序) 字符串。)

一旦你尝试了像 std::toupper('ß') 这样的东西,在的编码中,你就陷入了深深的麻烦。 ( 因为是用标准库,它决不可能为此"右边"只能揭晓一个字符,而不是所需的"SS" 结果这里) 。

然后,标准库取决于你的软件正在运行的计算机上的支持的区域设置。 如果不是,你会做什么?

那又怎样你是真的要找寻的是一个字符串类,它有能力处理这一切正确,这就是不 std::string

而升压看起来不错,API明智,围绕 Boost.Locale 基本上是一个包装器急救 。 在ria客户端支持状态., 如果升压是编译. 如果不是,Boost.Locale 仅限于为标准库编译的语言环境支持。

开颅是个大麻烦sometimes,和相信我,获得 升压到 compile. ( 没有为 Windows pre-compiled二进制文件,所以必须再必须与应用程序一起提供它们,然后 ,打开一个全新的是蠕虫的盒子。。)

因此,我建议你直接从马口中获得完整的Unicode支持,并直接使用 ICU 库:


#include <unicode/unistr.h>
#include <unicode/ustream.h>

#include <iostream>

int main()
{
 char const * someString ="Eidengesxe4xdf";
 icu::UnicodeString someUString( someString,"ISO-8859-1" );
 std::cout <<someUString.toLower() <<"n";
 std::cout <<someUString.toUpper() <<"n";
 return 0;
}

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


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

这给出了:


eidengesäß
EIDENGESÄSS

你在那里- )?

注意:tolower() 不在 100%的时间内工作。

小写/大写操作只适用于字符,而 std::string 实际上是字节数组,而不是字符。 普通的tolower 对于ASCII字符串很好,但它不会对latin-1或者utf-8字符串进行正确的处理。 你必须知道字符串的编码,并可能对它的进行解码,然后才能对它的字符进行小写编码。

如果字符串包含ASCII范围之外的UTF-8字符,则 boost:: to_lower:: 将不会转换这些字符。 更好的使用提升:: 区域设置:: to_lower UTF-8时介入。 参见 http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html web

这是对mai的Stefan响应的跟踪: 如果你希望放置的结果转换在另一个字符串中,你需要将它的存储空间方法之前,pre-allocate 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;
}

使用range-based循环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);
}

就我看来Boost库是非常糟糕的performance-wise 。 我已经测试了他们的unordered_map到 STL,它的平均速度是 3倍。 这里算法看起来太低。

不同的是大,我相信不论添加操作都需要做的,以 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,所以我不需要在我的机器上测试它。

有一个方法,把大写来降低 ,而不用执行如果,并证明它是非常 straight-forward 。 isupper() clocale.h 应该负责处理问题的函数/宏的使用关于你的位置,但如果不是,你可以随时拧动 UtoL [] 到你的心的内容。

考虑到字符的c 实际上也可以算作 8 -bit int ( 忽略当前的宽字符集) 你可以创建一个 256字节数组举行一个替代的字符集,然后在转换函数转换你在字符串中使用char作为下标到数组中。

而不是使用 1 -for-1映射,为upper-case数组成员提供lower-case字符的字节int值。 你可以在这里找到 islower() 和 isupper()

enter image description here

代码看起来像这样。。


#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 。

的另一种选择升压是 POCO (pocoproject.org).

POCO提供了两种变体:

  1. 第一个变量做一个复制而不改变原始字符串。
  2. 第二个变量将原始字符串替换到位。
    "将"版本总是有的"位置置于名称中。

下面演示了两个版本:


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

...