我想将 a 转换std::string
为小写。我知道这个功能tolower()
。然而,在过去我遇到过这个函数的问题,无论如何它都不是理想的,因为将它与 astd::string
一起使用需要遍历每个字符。
有没有 100% 有效的替代方案?
我想将 a 转换std::string
为小写。我知道这个功能tolower()
。然而,在过去我遇到过这个函数的问题,无论如何它都不是理想的,因为将它与 astd::string
一起使用需要遍历每个字符。
有没有 100% 有效的替代方案?
改编自不太常见的问题:
#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 这样的多字节编码时。
#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);
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>
基于 - 的解决方案可以为您提供。
使用 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);
}
如果字符串包含 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
另一种使用基于范围的for循环和参考变量的方法
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
cout<<test<<endl;
这是对 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;
}
将字符串转换为小写而不用担心 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;
}
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";
}
Boost 的替代方案是 POCO (pocoproject.org)。
POCO 提供了两种变体:
两个版本如下所示:
#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);
我自己的执行大写/小写的模板函数。
#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;
}
有一种方法可以在不进行 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 的方法相同。
由于没有一个答案提到即将推出的 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::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))); }
char
同样,当迭代器的值类型为or时,它们不应直接与标准算法一起使用signed char
。相反,将值转换为unsigned char
first: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; }
我写了这个简单的辅助函数:
#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.
在 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 );
}
如果你想要一些简单的东西,这里有一个宏技术:
#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 字符。
// 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;
}
有没有 100% 有效的替代方案?
不
在选择小写方法之前,您需要问自己几个问题。
一旦您对这些问题有了答案,您就可以开始寻找适合您需求的解决方案。没有一种尺寸适合所有地方的所有人!
试试这个功能:)
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;
}
复制,因为它不允许改进答案。谢谢所以
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
解释:
for(auto& c : test)
是一种基于范围的 for 循环:
for (
range_declaration
:
range_expression
)
loop_statement
range_declaration
:auto& c
这里的auto 说明符用于自动类型推导。所以类型会从变量初始化器中扣除。
range_expression
:test
本例中的范围是字符串的字符test
。
字符串的字符test
可用作 for 循环内的引用 identifier c
。
C++ 没有为 实现tolower
或toupper
实现方法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;
}
对于基于字符的字符串操作:对于字符串中的每个字符
#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;
}
我写了一个适用于任何字符串的模板版本:
#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
这可能是将大写转换为小写的另一个简单版本,反之亦然。我用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;
}
注意:如果有特殊字符则需要使用条件检查来处理。
我尝试了 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;
}