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

问题描述 投票:681回答:25

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

有没有一种方法可以100%的时间运作?

c++ string c++-standard-library tolower
25个回答
840
投票

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


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";
}

3
投票

Boost的另一种选择是POCO(pocoproject.org)。

POCO提供两种变体:

  1. 第一个变体制作副本而不更改原始字符串。
  2. 第二个变体将原始字符串更改为适当位置。 “就地”版本的名称中始终包含“InPlace”。

两个版本如下所示:

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

3
投票

有一种方法可以将大写字母转换为低级而不进行测试,而且非常简单。 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的方法相同。


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字符。


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/


1
投票

有没有一种方法可以100%的时间运作?

没有

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

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

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


1
投票

由于没有一个答案提到即将推出的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的行为是不确定的。要使用普通chars(或signed chars)安全地使用这些函数,首先应将参数转换为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;
}

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

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

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

297
投票

Boost provides a string algorithm for this

#include <boost/algorithm/string.hpp>

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

Or, for non-in-place

#include <boost/algorithm/string.hpp>

const std::string str = "HELLO, WORLD!";
const std::string lower_str = boost::algorithm::to_lower_copy(str);

0
投票

复制,因为不允许改善答案。谢谢你


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

  1. range_declarationauto& c 这里auto specifier用于自动类型扣除。因此,类型从变量初始值设定项中扣除。
  2. range_expressiontest 在这种情况下,范围是字符串test的字符。

字符串test的字符在for循环中通过标识符c作为参考。


0
投票

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


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

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

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


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

-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

205
投票

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::u16stringstd::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”转换旁边的选项,以避免歧义,例如:在护照中(名称大写)。我美丽的例子,由委员会决定过时了......


30
投票

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


26
投票

使用基于范围的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);
}

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

8
投票

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

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

cout<<test<<endl;

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所以我真的不需要在我的机器上测试它。


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;
}
© www.soinside.com 2019 - 2024. All rights reserved.