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

时间:2008-11-24 11:50:00

标签: c++ string c++-standard-library tolower

我想将std::string转换为小写。我知道函数tolower(),但是在过去我遇到过这个函数的问题,因为使用std::string需要迭代每个字符,所以它几乎不理想。

有没有一种方法可以在100%的时间内起作用?

27 个答案:

答案 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::u16stringstd::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)

来自标准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";
}

答案 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提供两种变体:

  1. 第一个变体制作副本而不改变原始字符串。
  2. 第二个变体将原始字符串更改为原位 “就地”版本的名称中始终包含“InPlace”。
  3. 以下演示了两个版本:

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

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的方法相同。

答案 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;
}

有关详细信息:http://www.cplusplus.com/reference/locale/tolower/

答案 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)));
}
     

类似地,当迭代器的值类型为charsigned 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%地起作用?

在选择一种下套管方法之前,您需要问自己几个问题。

  1. 字符串如何编码?纯ASCII码? UTF-8?某种形式的扩展ASCII传统编码?
  2. 无论如何,小写是什么意思?案例映射规则因语言而异!您是否需要本地化到用户语言环境的内容?您是否希望在您的软件所运行的所有系统上都能表现出一致的性能?您是否只想小写ASCII字符并通过其他所有字符?
  3. 可以使用哪些库?

一旦您对这些问题有答案,就可以开始寻找适合您需求的解决方案。没有一种尺寸适合所有地方的所有人!

答案 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 )

  1. loop_statement range_declaration
    这里auto specifier用于自动类型推导。因此,类型会从变量初始值设定项中扣除。

  2. auto& c range_expression
    在这种情况下,范围是字符串test的字符。

  3. 字符串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