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

Kon*_*rad 736 c++ string c++-standard-library tolower

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

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

Ste*_*Mai 867

这个:

#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); });
Run Code Online (Sandbox Code Playgroud)

你真的不会逃避迭代每个角色.否则无法知道字符是小写还是大写.

如果你真的讨厌tolower(),这是一个我不建议您使用的非便携式替代方案:

char asciitolower(char in) {
    if (in <= 'Z' && in >= 'A')
        return in - ('Z' - 'z');
    return in;
}

std::transform(data.begin(), data.end(), data.begin(), asciitolower);
Run Code Online (Sandbox Code Playgroud)

请注意,tolower()只能执行每单字节字符替换,这对于许多脚本来说都是不合适的,特别是如果使用像UTF-8这样的多字节编码.

  • 每当你假设角色是ASCII时,上帝杀死了一只小猫.:( (241认同)
  • -1这个`:: tolower`的使用可能会崩溃,它是非ASCII输入的UB. (37认同)
  • (可能是旧的,所讨论的算法变化不大)@Stefan Mai:在调用STL算法时会出现什么样的"全部开销"?这些函数相当精简(即简单的循环)并且经常内联,因为在同一个编译单元中很少有多个调用同一个函数和相同的模板参数. (24认同)
  • 你的第一个例子可能有*未定义的行为*(将`char`传递给`:: tolower(int)`.)你需要确保不传递负值. (13认同)
  • 在tolower之前需要::来表明它位于最外层的命名空间中.如果你在另一个命名空间中使用这个代码,可能会有一个不同的(可能是不相关的)tolower定义,最终在不使用::的情况下优先选择. (7认同)
  • 这太棒了,我总是想知道最好的方法是什么.我不知道使用std :: transform.:) (5认同)
  • @eq公平点,我的基准测试在使用`-O3'进行编译时同意你的意见(尽管STL实际上优于手动调优的代码,所以我想知道编译器是否在耍一些技巧).调试STL代码仍然是一个熊;). (3认同)
  • @MichalW如果你只有字母,这种方法很有效,但并非总是如此.如果你在那个领域,你可以通过在长片上使用位掩码做得更好 - 一次取8个字符;) (3认同)
  • 虽然这应该是在理智的世界中这样做的规范方式,但它有太多的问题无法推荐它.首先,来自ctype.h的tolower不适用于unicode.其次,许多其他std库头文件中包含的locale.h定义了一个冲突的tolower,这会引起麻烦,请参阅http://stackoverflow.com/q/5539249/339595.最好使用std :: locale或boost :: locale :: to_lower作为其他答案建议. (3认同)
  • @MichalW嘿,你能解释一下你在那里写的吗?另外,为什么我们在`:: tolower`中使用`::`? (3认同)
  • ```std :: transform(data.begin(),data.end(),data.begin(),easytolower);```很危险.由于````std :: tolower```的行为是未定义的,如果输入不能表示为```unsigned char```并且不等于```EOF``` (3认同)
  • 这种非便携式解决方案可能更快.你可以避免这样分支:inChar | = 0x20.我认为这是将ascii转换为低位的最快方法.如果你想将低位转换为高位则:inChar&= ~0x20. (2认同)
  • ::如果您是国际/使用宽字符,请使用 (2认同)
  • @StefanMai嗨.为什么在"tolower"之前需要"::"?我不明白. (2认同)
  • @TypicalHog:因为不能保证'A'到'Z'是连续范围(EBCDIC);但更重要的是,因为*以外的*字母(''Ü'`,''á'`,...)。非常遗憾的是,作者更倾向于使用非便携式解决方案获得更多的赞誉,而不是正确指出自己的缺点... (2认同)
  • 我不明白为什么这里的 tolower 被包裹在 lambda 中,而不是仅仅将它传递给它自己进行转换。 (2认同)
  • @JPhi1618 1)确保字符首先转换为“unsigned char”(参见上面Deduplicator的评论);2) 启用重载解析以选择 `&lt;cctype&gt;` 中定义的 [`int tolower( int ch );`](https://en.cppreference.com/w/cpp/string/byte/tolower) 重载在 `&lt;clocale&gt;` 中定义的 [`template&lt; class charT &gt; charT tolower( charT ch, const locale&amp; loc );`](https://en.cppreference.com/w/cpp/locale/tolower) 重载。 (2认同)

Rob*_*Rob 304

有一个Boost字符串算法:

#include <boost/algorithm/string.hpp>

std::string str = "HELLO, WORLD!";
boost::algorithm::to_lower(str); // modifies str
Run Code Online (Sandbox Code Playgroud)

或者,对于非就地:

#include <boost/algorithm/string.hpp>

const std::string str = "HELLO, WORLD!";
const std::string lower_str = boost::algorithm::to_lower_copy(str);
Run Code Online (Sandbox Code Playgroud)

  • 非ASCII-7失败. (16认同)
  • @Ray,是的,`to_lower_copy` (5认同)
  • 不,不是。这是您在本网站上的每个 C++ 问题上看到的极其不幸的答案之一......因为添加整个库只是为了做一些如此简单的事情显然是最流行的路线! (4认同)
  • 这非常慢,请参阅此基准测试:godbolt.org/z/neM5jsva1 (3认同)
  • @史前企鹅慢吗?嗯,慢是调试代码,因为你自己的实现有一个错误,因为它比仅仅调用 boost 库更复杂;)如果代码很关键,比如调用很多并且提供了瓶颈,那么,好吧,它可以是值得思考缓慢 (3认同)
  • 我认为这与使用ASCII输入的tolower没有相同的问题? (2认同)

Dev*_*lar 221

TL;博士

使用ICU库.


首先,您必须回答一个问题:您的编码std::string什么?是ISO-8859-1吗?或者ISO-8859-8?或Windows代码页1252?无论你用什么来转换大写到小写都知道吗?(或者对于角色来说,它是否会失败0x7f?)

如果您使用的是UTF-8(8位编码中唯一理智的选择)与std::string作为容器,你已经在自欺欺人,以为你还在控制的事情,因为你是在一个容器中存储的多字节字符序列那不了解多字节概念.甚至像.substr()滴答作响的定时炸弹一样简单.(因为拆分多字节序列将导致无效(子)字符串.)

只要你尝试类似的东西std::toupper( 'ß' ),在任何编码中,你就会陷入困境.(因为根本不可能使用标准库"正确"执行此操作,标准库只能提供一个结果字符,而不是"SS"此处所需的.)[1]另一个例子是std::tolower( 'I' ),根据语言环境应该产生不同的结果.在德国,'i'这是正确的; 在土耳其,'?'(LATIN SMALL LETTER DOTLESS I)是预期的结果.

然后有一点是标准库取决于运行软件的机器上支持的语言环境......如果不是,你会怎么做?

所以你真正想要的是一个能够正确处理所有这些的字符串类,但事实并非如此 std::basic_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;
}
Run Code Online (Sandbox Code Playgroud)

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

g++ -Wall example.cpp -licuuc -licuio
Run Code Online (Sandbox Code Playgroud)

这给出了:

eidengesäß
EIDENGESÄSS
Run Code Online (Sandbox Code Playgroud)

[1]在2017年,理事会对德国正字裁定"ẞ" U + 1E9E拉丁大写字母清音S可以正式使用,作为传统的"SS"转换旁边的一个选项,以避免歧义,例如在护照(其中名称是大写).我美丽的例子,由委员会决定过时了......

  • 这是一般情况下的正确答案.除了谎言和欺骗之外,该标准没有为除"ASCII"之外的任何东西提供任何处理.它让你*想*你可以处理UTF-16,但你不能.正如这个答案所说,如果不进行自己的unicode处理,就无法获得UTF-16字符串的正确字符长度(不是字节长度).如果您必须处理真实文本,请使用ICU.谢谢,@ DevSolar (17认同)
  • @DevSolar同意!长度的概念在文本上相当无意义(我们可以将连字添加到违法者列表中).也就是说,由于人们习惯于选项卡和控制字符占用一个长度单位,因此代码点将是更直观的衡量标准.哦,谢谢你给出了正确答案,很遗憾看到它如此遥远:-( (2认同)
  • @LF稍微好一点。但是很多事情仍然没有涉及:`toupper`和`tolower`仍然可以在单个字符上使用。字符串类仍然没有归一化的概念(例如,“ü”被编码为“具有脱音符的u”还是“ u +组合脱音符”),或者字符串可能会分离也可能不会分离。清单继续。u8string(与其他标准字符串类一样)适合“通过”。但是,如果您要*处理* Unicode,则需要** ICU。 (2认同)

inc*_*ses 31

使用基于范围的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);
}
Run Code Online (Sandbox Code Playgroud)

  • 但是,在法语机器上,此程序不会转换法语中允许的非ASCII字符.例如一个字符串'Test String123.ÉÏ\n'将转换为:'test string123.ÉÏ\n'虽然字符ÉÏ和他们的小写couterparts'é'和'ï',允许用法语.似乎这个线程的其他消息没有提供解决方案. (9认同)
  • 我认为您需要为此设置适当的语言环境。 (2认同)

Pat*_*hly 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


小智 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这并没有为我调整 ä 的大小 (2认同)

Gil*_* PJ 11

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

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

cout<<test<<endl;
Run Code Online (Sandbox Code Playgroud)


Tar*_*aro 7

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

#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(),
        [](const T v){ return static_cast<T>(std::tolower(v)); });
    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(),
        [](const T v){ return static_cast<T>(std::toupper(v)); });
    return s2;
}
Run Code Online (Sandbox Code Playgroud)

  • 这就是我所需要的。我只是使用“towlower”来表示支持 UTF-16 的宽字符。 (2认同)

A-S*_*ani 7

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

#include <locale> // tolower

string to_lower(string s) {        
    for(char &c : s)
        c = tolower(c);
    return s;
}
Run Code Online (Sandbox Code Playgroud)

用法:

string s = "TEST";
cout << to_lower("HELLO WORLD"); // output: "hello word"
cout << to_lower(s); // won't change the original variable.
Run Code Online (Sandbox Code Playgroud)


Eth*_*one 6

据我所知,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
Run Code Online (Sandbox Code Playgroud)

-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
Run Code Online (Sandbox Code Playgroud)

资源:

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();
Run Code Online (Sandbox Code Playgroud)

我想我应该在专用机器上进行测试但是我将使用这个EC2所以我真的不需要在我的机器上测试它.

  • unordered_map的性能取决于散列算法与您正在使用的数据的组合.没有魔术哈希算法适用于所有和任何数据,以尽可能快地使unordered_map.基准和尝试不同的东西.你性能越来越差的原因是因为你使用的哈希会导致大量的冲突,这基本上会导致列表中的查找.有关更多信息,请访问此站点:http://fgda.pl/post/7/gcc-hash-map-vs-unordered-map就我的目的而言,链接提供的功能减少了冲突,因此非常快. (2认同)

小智 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;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)


Sam*_*eer 5

std::ctype::tolower()从标准C++本地化库将正确地为您执行此操作.以下是从tolower参考页面中提取的示例

#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";
}
Run Code Online (Sandbox Code Playgroud)