909

我想将 a 转换std::string为小写。我知道这个功能tolower()。然而,在过去我遇到过这个函数的问题,无论如何它都不是理想的,因为将它与 astd::string一起使用需要遍历每个字符。

有没有 100% 有效的替代方案?

4

27 回答 27

1036

改编自不太常见的问题

#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 这样的多字节编码时。

于 2008-11-24T11:59:33.050 回答
363

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);
于 2008-11-24T11:57:21.557 回答
296

tl;博士

使用ICU 库如果您不这样做,您的转换例程将在您可能甚至不知道存在的情况下静默中断。


首先你必须回答一个问题:你的编码std::string什么?是 ISO-8859-1 吗?或者也许是 ISO-8859-8?还是 Windows 代码页 1252?您用于将大写转换为小写的任何东西都知道吗?(或者它是否会因为角色过度而惨遭失败0x7f?)

如果您使用 UTF-8(8 位编码中唯一明智的选择std::string作为容器,那么如果您认为自己仍然可以控制事物,那么您已经在欺骗自己了。您将多字节字符序列存储在不了解多字节概念的容器中,并且您可以对其执行的大多数操作也不是!即使是简单的事情.substr()也可能导致无效(子)字符串,因为您在多字节序列的中间进行拆分。

一旦你尝试类似std::toupper( 'ß' ), 或任何std::tolower( 'Σ' )编码的东西,你就有麻烦了。因为 1),该标准一次只对一个字符起作用,所以它根本不可能变成正确的。并且 2),该标准一次只对一个字符进行操作,因此它无法确定是在一个单词的中间(哪里是正确的),还是在结尾()。另一个例子是,它应该根据语言环境产生不同的结果——几乎在你期望的任何地方,但在土耳其(拉丁小写字母 DOTLESS I)是正确的答案(同样,在 UTF-8 中超过一个字节编码)。ßSSΣσςstd::tolower( 'I' )iı

因此,任何一次在一个字符上工作的大小写转换,或者更糟糕的是,一次一个字节,都被设计破坏了。这包括目前std::存在的所有变体。

然后有一点,标准库能力取决于运行您的软件的机器上支持哪些语言环境......如果您的目标语言环境不受支持,您会怎么做在您客户的机器上?

所以你真正要找的是一个能够正确处理所有这些的字符串类,不是任何std::basic_string<>变体

(C++11 注意:std::u16string并且更好std::u32string,但仍然不完美。C++20 带来了,但所有这些都是指定编码。在许多其他方面,他们仍然对 Unicode 机制一无所知,如规范化、排序规则、.. .)std::u8string

虽然 Boost看起来不错,但在 API 方面,Boost.Locale 基本上是ICU的包装器。如果Boost 是在 ICU 支持下编译的……如果不是,Boost.Locale 仅限于为标准库编译的语言环境支持。

相信我,Boost 与 ICU 一起编译有时会很痛苦。(没有包含 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>基于 - 的解决方案可以为您提供。

于 2014-06-05T15:06:39.723 回答
38

使用 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);
}
于 2013-10-09T08:00:34.853 回答
33

如果字符串包含 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

于 2012-10-10T07:24:12.917 回答
28

另一种使用基于范围的for循环和参考变量的方法

string test = "Hello World";
for(auto& c : test)
{
   c = tolower(c);
}

cout<<test<<endl;
于 2017-01-10T19:53:45.383 回答
22

这是对 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;
}
于 2013-03-28T06:25:54.357 回答
9

将字符串转换为小写而不用担心 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;
}
于 2015-06-12T06:50:29.950 回答
5

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";
}
于 2016-01-29T02:25:50.813 回答
4

Boost 的替代方案是 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);
于 2013-09-18T20:20:37.243 回答
4

我自己的执行大写/小写的模板函数。

#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 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 s2;
}
于 2019-05-18T14:40:40.290 回答
3

有一种方法可以在不进行 if 测试的情况下将大写转换为小写,而且非常简单。isupper() 函数/宏对 clocale.h 的使用应该可以解决与您的位置相关的问题,但如果没有,您可以随时调整 UtoL[] 以适应您的需要。

鉴于 C 的字符实际上只是 8 位整数(暂时忽略宽字符集),您可以创建一个 256 字节数组来保存一组替代字符,并在转换函数中使用字符串中的字符作为下标到转换数组。

但是,不是 1 对 1 映射,而是为大写数组成员提供小写字符的 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;
}

同时,这种方法将允许您重新映射您希望更改的任何其他字符。

这种方法在现代处理器上运行时有一个巨大的优势,不需要进行分支预测,因为没有包含分支的 if 测试。这为其他循环节省了 CPU 的分支预测逻辑,并倾向于防止流水线停顿。

有些人可能会认为这种方法与用于将 EBCDIC 转换为 ASCII 的方法相同。

于 2014-01-08T17:48:19.410 回答
3

由于没有一个答案提到即将推出的 Ranges 库,该库自 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启发,其中指出:

与 中的所有其他函数一样,如果参数的值既不能表示为也不等于<cctype>,则 的行为是未定义的。要在普通s(或s)中安全地使用这些函数,首先应将参数转换为:std::tolowerunsigned charEOFcharsigned charunsigned char

char my_tolower(char ch)
{
    return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}

char同样,当迭代器的值类型为or时,它们不应直接与标准算法一起使用signed char。相反,将值转换为unsigned charfirst:

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;
}
于 2019-04-15T09:36:59.740 回答
3

我写了这个简单的辅助函数:

#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.
于 2020-09-29T22:52:22.127 回答
2

在 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 );
}
于 2014-08-29T17:18:02.363 回答
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 对此答案的评论仍然是一个重要的考虑因素,但是,如果您正在处理的不仅仅是 ASCII 字符。

于 2016-01-30T21:02:24.137 回答
2
// 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/

于 2017-03-20T05:20:04.203 回答
2

有没有 100% 有效的替代方案?

在选择小写方法之前,您需要问自己几个问题。

  1. 字符串是如何编码的?纯ASCII?UTF-8?某种形式的扩展 ASCII 传统编码?
  2. 无论如何,小写是什么意思?大小写映射规则因语言而异!你想要一些本地化到用户区域的东西吗?你想要在你的软件运行的所有系统上表现一致的东西吗?您是否只想将 ASCII 字符小写并通过其他所有字符?
  3. 有哪些库可用?

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

于 2019-01-28T21:31:21.767 回答
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;

}
于 2020-03-19T01:12:35.497 回答
1

fplus::to_lower_case()fplus库中使用。

to_lower_casefplus API中搜索

例子:

fplus::to_lower_case(std::string("ABC")) == std::string("abc");
于 2017-05-08T07:21:04.207 回答
1

复制,因为它不允许改进答案。谢谢所以


string test = "Hello World";
for(auto& c : test)
{
   c = tolower(c);
}

解释:

for(auto& c : test)是一种基于范围的 for 循环
for (range_declaration:range_expression)loop_statement

  1. range_declaration:auto& c
    这里的auto 说明符用于自动类型推导。所以类型会从变量初始化器中扣除。

  2. range_expression:test
    本例中的范围是字符串的字符test

字符串的字符test可用作 for 循环内的引用 identifier c

于 2018-04-17T12:20:30.167 回答
1

C++ 没有为 实现tolowertoupper实现方法std::string,但它可用于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;
}

对于基于字符的字符串操作:对于字符串中的每个字符

于 2019-03-17T14:35:38.750 回答
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;
}
于 2017-04-10T19:11:06.857 回答
0

为 ASCII 字符串 to_lower 添加一些可选库,这两个库都是生产级别的,并且具有微优化,预计会比此处现有的答案更快(TODO:添加基准结果)。

Facebook的愚蠢

void toLowerAscii(char* str, size_t length)

谷歌的Abseil

void AsciiStrToLower(std::string* s);
于 2021-06-22T09:49:44.463 回答
0

我写了一个适用于任何字符串的模板版本:

#include <type_traits> // std::decay
#include <ctype.h>    // std::toupper & std::tolower


template <class T = void> struct farg_t { using type = T; };
template <template<typename ...> class T1, 
class T2> struct farg_t <T1<T2>> { using type = T2*; };
//---------------

template<class T, class T2 = 
typename std::decay< typename farg_t<T>::type >::type>
void ToUpper(T& str) { T2 t = &str[0]; 
for (; *t; ++t) *t = std::toupper(*t); }


template<class T, class T2 = typename std::decay< typename 
farg_t<T>::type >::type>
void Tolower(T& str) { T2 t = &str[0]; 
for (; *t; ++t) *t = std::tolower(*t); }

用 gcc 编译器测试:

#include <iostream>
#include "upove_code.h"

int main()
{

    std::string str1 = "hEllo ";
    char str2 [] = "wOrld";

    ToUpper(str1);
    ToUpper(str2);
    std::cout << str1 << str2 << '\n'; 
    Tolower(str1);
    Tolower(str2);
    std::cout << str1 << str2 << '\n'; 
    return 0;
}

输出:

>HELLO WORLD
>
>hello world
于 2022-02-03T10:11:16.957 回答
-2

这可能是将大写转换为小写的另一个简单版本,反之亦然。我用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;
}

注意:如果有特殊字符则需要使用条件检查来处理。

于 2018-06-04T02:47:46.877 回答
-11

我尝试了 std::transform,我得到的只是可恶的 stl criptic 编译错误,只有 200 年前的德鲁伊才能理解(无法转换为 flibidi flabidi 流感)

这工作正常,可以很容易地调整

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;
}
于 2014-07-10T14:20:34.190 回答