【发布时间】:2010-10-18 14:59:08
【问题描述】:
如何将字符串转换为大写。我从谷歌搜索中找到的示例只需要处理字符。
【问题讨论】:
如何将字符串转换为大写。我从谷歌搜索中找到的示例只需要处理字符。
【问题讨论】:
#include <algorithm>
#include <string>
std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);
【讨论】:
toupper()可以实现为宏。这可能会导致问题。
[](auto c) { return std::toupper(c); }——也许值得更新答案?
#include <boost/algorithm/string.hpp>
#include <string>
std::string str = "Hello World";
boost::to_upper(str);
std::string newstr = boost::to_upper_copy<std::string>("Hello World");
【讨论】:
::toupper 很可能假定为 ASCII。
-O3 和 Boost 1.58 时表现非常糟糕(比在循环中调用 glibc 的 toupper 差 30 倍。)有一个没有得到的语言环境的 dynamic_cast从每个字符循环中提升出来。看我的回答。从好的方面来说,这可能是对 UTF-8 的正确认识,但减速并不是来自处理 UTF-8;它来自使用dynamic_cast 重新检查每个字符的语言环境。
使用 C++11 和 toupper() 的简短解决方案。
for (auto & c: str) c = toupper(c);
【讨论】:
c 不会是const char 类型(来自auto)?如果是这样,您不能将它(因为const 部分)分配给toupper(c) 返回的内容。
c 需要转换为 unsigned char 才能进行更正。
for (auto & c: str) c = (char)toupper(c);一起去了
这个问题可以用 SIMD 向量化 用于 ASCII 字符集。
在 Core2Duo (Merom) 上使用 x86-64 gcc 5.2 -O3 -march=native 进行初步测试。相同的 120 个字符的字符串(混合小写和非小写 ASCII),在循环中转换 40M 次(没有跨文件内联,因此编译器无法优化或将其中的任何一个提升出循环)。相同的源缓冲区和目标缓冲区,因此没有 malloc 开销或内存/缓存影响:L1 缓存中的数据一直很热,而且我们完全受 CPU 限制。
boost::to_upper_copy<char*, std::string>():198.0s。是的,Ubuntu 15.10 上的 Boost 1.58 真的这么慢。我在调试器中对 asm 进行了分析和单步执行,这真的,真的很糟糕:每个字符都会发生一个 locale 变量的 dynamic_cast !!! (dynamic_cast 需要多次调用 strcmp)。 LANG=C 和 LANG=en_CA.UTF-8 会发生这种情况。
我没有使用除 std::string 之外的 RangeT 进行测试。 Maybe the other form of to_upper_copy 优化得更好,但我认为它总是会为复制空间提供new/malloc 空间,因此更难测试。也许我所做的事情与正常的用例不同,也许通常停止的 g++ 可以将语言环境设置从每个字符循环中提升出来。我从std::string 读取并写入char dstbuf[4096] 的循环对于测试是有意义的。
循环调用 glibc toupper:6.67s(不过,不检查 int 结果中是否存在潜在的多字节 UTF-8。这对土耳其语很重要。)
cmov 更快,无论如何表在 L1 中都是热的。李>
另见this question about toupper() being slow on Windows when a locale is set。
我很震惊 Boost 比其他选项慢一个数量级。我仔细检查了我是否启用了-O3,甚至单步执行 asm 以查看它在做什么。与 clang++ 3.8 的速度几乎完全相同。它在每个字符循环内有巨大的开销。 perf record / report 结果(对于cycles perf 事件)是:
32.87% flipcase-clang- libstdc++.so.6.0.21 [.] _ZNK10__cxxabiv121__vmi_class_type_info12__do_dyncastElNS_17__class_type_info10__sub_kindEPKS1_PKvS4_S6_RNS1_16
21.90% flipcase-clang- libstdc++.so.6.0.21 [.] __dynamic_cast
16.06% flipcase-clang- libc-2.21.so [.] __GI___strcmp_ssse3
8.16% flipcase-clang- libstdc++.so.6.0.21 [.] _ZSt9use_facetISt5ctypeIcEERKT_RKSt6locale
7.84% flipcase-clang- flipcase-clang-boost [.] _Z16strtoupper_boostPcRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
2.20% flipcase-clang- libstdc++.so.6.0.21 [.] strcmp@plt
2.15% flipcase-clang- libstdc++.so.6.0.21 [.] __dynamic_cast@plt
2.14% flipcase-clang- libstdc++.so.6.0.21 [.] _ZNKSt6locale2id5_M_idEv
2.11% flipcase-clang- libstdc++.so.6.0.21 [.] _ZNKSt6locale2id5_M_idEv@plt
2.08% flipcase-clang- libstdc++.so.6.0.21 [.] _ZNKSt5ctypeIcE10do_toupperEc
2.03% flipcase-clang- flipcase-clang-boost [.] _ZSt9use_facetISt5ctypeIcEERKT_RKSt6locale@plt
0.08% ...
只有在循环之前知道迭代次数时,Gcc 和 clang 才会自动向量化循环。 (即像 strlen 的纯 C 实现这样的搜索循环不会自动矢量化。)
因此,对于小到足以放入缓存的字符串,我们从首先执行strlen 获得约128 个字符长的字符串的显着加速。对于显式长度的字符串(如 C++ std::string),这不是必需的。
// char, not int, is essential: otherwise gcc unpacks to vectors of int! Huge slowdown.
char ascii_toupper_char(char c) {
return ('a' <= c && c <= 'z') ? c^0x20 : c; // ^ autovectorizes to PXOR: runs on more ports than paddb
}
// gcc can only auto-vectorize loops when the number of iterations is known before the first iteration. strlen gives us that
size_t strtoupper_autovec(char *dst, const char *src) {
size_t len = strlen(src);
for (size_t i=0 ; i<len ; ++i) {
dst[i] = ascii_toupper_char(src[i]); // gcc does the vector range check with psubusb / pcmpeqb instead of pcmpgtb
}
return len;
}
任何体面的 libc 都会有一个高效的strlen,这比一次循环一个字节要快得多,因此单独的矢量化 strlen 和 toupper 循环更快。
基线:动态检查终止 0 的循环。
在 Core2 (Merom) 2.4GHz 上进行 40M 次迭代的时间。 gcc 5.2-O3 -march=native。 (Ubuntu 15.10)。 dst != src(所以我们复制了一份),但它们不重叠(也不在附近)。两者都对齐。
有些结果与 clang 有点不同。
调用函数的微基准循环位于单独的文件中。否则它内联并且strlen() 被提升出循环,并且它运行得更快,尤其是。对于 16 个字符字符串 (0.187s)。
这具有 gcc 可以为任何架构自动矢量化它的主要优点,但主要缺点是它对于通常常见的小字符串情况较慢。
因此有很大的加速,但编译器自动矢量化并不能生成出色的代码,尤其是。用于清理最后最多 15 个字符。
基于我的case-flip function,它反转了每个字母字符的大小写。它利用了“无符号比较技巧”,您可以在其中通过范围移位对单个无符号比较进行low < a && a <= high,这样任何小于low 的值都会包装成大于high 的值。 (如果 low 和 high 相距不太远,则此方法有效。)
SSE 只有一个有符号的 compare-greater,但我们仍然可以使用“unsigned 通过范围移动到有符号范围的底部来比较”技巧:减去'a'+128,因此字母字符的范围从-128到-128+25(-128+'z'-'a')
请注意,对于 8 位整数,加 128 和减 128 是一回事。进位无处可去,所以它只是异或(无进位加),翻转高位。
#include <immintrin.h>
__m128i upcase_si128(__m128i src) {
// The above 2 paragraphs were comments here
__m128i rangeshift = _mm_sub_epi8(src, _mm_set1_epi8('a'+128));
__m128i nomodify = _mm_cmpgt_epi8(rangeshift, _mm_set1_epi8(-128 + 25)); // 0:lower case -1:anything else (upper case or non-alphabetic). 25 = 'z' - 'a'
__m128i flip = _mm_andnot_si128(nomodify, _mm_set1_epi8(0x20)); // 0x20:lcase 0:non-lcase
// just mask the XOR-mask so elements are XORed with 0 instead of 0x20
return _mm_xor_si128(src, flip);
// it's easier to xor with 0x20 or 0 than to AND with ~0x20 or 0xFF
}
鉴于此函数适用于一个向量,我们可以在循环中调用它来处理整个字符串。由于我们已经针对 SSE2,我们可以同时进行向量化的字符串结尾检查。
我们还可以更好地“清理”在执行 16B 向量之后剩余的最后 15 个字节:大写是幂等的,因此重新处理一些输入字节就可以了。我们对源的最后 16B 进行非对齐加载,并将其存储到与循环中最后 16B 存储重叠的 dest 缓冲区中。
唯一不起作用的情况是整个字符串低于 16B:即使dst=src,非原子读取-修改-写入不与不触摸某些相同字节,并且可以破坏多线程代码。
为此,我们有一个标量循环,也可以让src 对齐。由于我们不知道终止 0 将在哪里,来自src 的未对齐负载可能会进入下一页并出现段错误。如果我们需要对齐的 16B 块中的任何字节,加载整个对齐的 16B 块总是安全的。
完整来源:in a github gist。
// FIXME: doesn't always copy the terminating 0.
// microbenchmarks are for this version of the code (with _mm_store in the loop, instead of storeu, for Merom).
size_t strtoupper_sse2(char *dst, const char *src_begin) {
const char *src = src_begin;
// scalar until the src pointer is aligned
while ( (0xf & (uintptr_t)src) && *src ) {
*(dst++) = ascii_toupper(*(src++));
}
if (!*src)
return src - src_begin;
// current position (p) is now 16B-aligned, and we're not at the end
int zero_positions;
do {
__m128i sv = _mm_load_si128( (const __m128i*)src );
// TODO: SSE4.2 PCMPISTRI or PCMPISTRM version to combine the lower-case and '\0' detection?
__m128i nullcheck = _mm_cmpeq_epi8(_mm_setzero_si128(), sv);
zero_positions = _mm_movemask_epi8(nullcheck);
// TODO: unroll so the null-byte check takes less overhead
if (zero_positions)
break;
__m128i upcased = upcase_si128(sv); // doing this before the loop break lets gcc realize that the constants are still in registers for the unaligned cleanup version. But it leads to more wasted insns in the early-out case
_mm_storeu_si128((__m128i*)dst, upcased);
//_mm_store_si128((__m128i*)dst, upcased); // for testing on CPUs where storeu is slow
src += 16;
dst += 16;
} while(1);
// handle the last few bytes. Options: scalar loop, masked store, or unaligned 16B.
// rewriting some bytes beyond the end of the string would be easy,
// but doing a non-atomic read-modify-write outside of the string is not safe.
// Upcasing is idempotent, so unaligned potentially-overlapping is a good option.
unsigned int cleanup_bytes = ffs(zero_positions) - 1; // excluding the trailing null
const char* last_byte = src + cleanup_bytes; // points at the terminating '\0'
// FIXME: copy the terminating 0 when we end at an aligned vector boundary
// optionally special-case cleanup_bytes == 15: final aligned vector can be used.
if (cleanup_bytes > 0) {
if (last_byte - src_begin >= 16) {
// if src==dest, this load overlaps with the last store: store-forwarding stall. Hopefully OOO execution hides it
__m128i sv = _mm_loadu_si128( (const __m128i*)(last_byte-15) ); // includes the \0
_mm_storeu_si128((__m128i*)(dst + cleanup_bytes - 15), upcase_si128(sv));
} else {
// whole string less than 16B
// if this is common, try 64b or even 32b cleanup with movq / movd and upcase_si128
#if 1
for (unsigned int i = 0 ; i <= cleanup_bytes ; ++i) {
dst[i] = ascii_toupper(src[i]);
}
#else
// gcc stupidly auto-vectorizes this, resulting in huge code bloat, but no measurable slowdown because it never runs
for (int i = cleanup_bytes - 1 ; i >= 0 ; --i) {
dst[i] = ascii_toupper(src[i]);
}
#endif
}
}
return last_byte - src_begin;
}
在 Core2 (Merom) 2.4GHz 上进行 40M 次迭代的时间。 gcc 5.2-O3 -march=native。 (Ubuntu 15.10)。 dst != src(所以我们复制了一份),但它们不重叠(也不在附近)。两者都对齐。
(实际上是在循环中与_mm_store 计时,而不是_mm_storeu,因为即使地址对齐,在 Merom 上 storeu 的速度也较慢。在 Nehalem 及更高版本上都很好。我也保留了代码原样现在,而不是修复在某些情况下复制终止 0 的失败,因为我不想重新计时。)
因此,对于长度超过 16B 的短字符串,这比自动矢量化要快得多。长度小于矢量宽度不存在问题。由于存储转发停顿,它们在就地操作时可能是一个问题。 (但请注意,处理我们自己的输出而不是原始输入仍然可以,因为 toupper 是幂等的)。
根据周围代码的需求和目标微架构,可以针对不同的用例进行大量调整。让编译器为清理部分发出漂亮的代码是很棘手的。使用 ffs(3)(在 x86 上编译为 bsf 或 tzcnt)似乎很好,但显然需要重新考虑这一点,因为我在写下大部分答案后发现了一个错误(参见 FIXME cmets)。
可以使用movq 或movd 加载/存储来获得更小字符串的向量加速。根据您的用例进行自定义。
我们可以检测我们的向量何时有任何设置了高位的字节,并在这种情况下回退到该向量的标量 utf-8 感知循环。 dst 点可以比src 指针前进不同的量,但是一旦我们回到对齐的src 指针,我们仍然只是对dst 进行未对齐的向量存储。
对于 UTF-8 文本,但主要由 UTF-8 的 ASCII 子集组成,这可能是件好事:在常见情况下具有高性能且在所有情况下行为正确。但是,当有很多非 ASCII 码时,它可能会比一直停留在标量 UTF-8 感知循环中更糟糕。
如果不利因素很大,以牺牲其他语言为代价提高英语并不是一个面向未来的决定。
在土耳其语言环境 (tr_TR) 中,toupper('i') 的正确结果是 'İ' (U0130),而不是 'I'(纯 ASCII)。有关 tolower() 在 Windows 上运行缓慢的问题,请参阅 Martin Bonner's comments。
我们还可以检查异常列表并回退到那里的标量,例如多字节 UTF8 输入字符。
有了这么多的复杂性,SSE4.2 PCMPISTRM 或其他东西可能可以一次性完成我们的大量检查。
【讨论】:
struct convert {
void operator()(char& c) { c = toupper((unsigned char)c); }
};
// ...
string uc_str;
for_each(uc_str.begin(), uc_str.end(), convert());
注意:顶级解决方案的几个问题:
21.5 空终止序列实用程序
这些头文件的内容应与标准 C 库头文件
、 、 、 和 [。 ..]
这意味着cctype 成员很可能是不适合在标准算法中直接使用的宏。
同一个例子的另一个问题是它没有强制转换参数或验证这是非负的;这对于签署了普通char 的系统尤其危险。 (原因是:如果这是作为宏实现的,它可能会使用查找表,并且您的参数索引到该表中。负索引将为您提供 UB。)
【讨论】:
string StringToUpper(string strToConvert)
{
for (std::string::iterator p = strToConvert.begin(); strToConvert.end() != p; ++p)
*p = toupper(*p);
return p;
}
或者,
string StringToUpper(string strToConvert)
{
std::transform(strToConvert.begin(), strToConvert.end(), strToConvert.begin(), ::toupper);
return strToConvert;
}
【讨论】:
** 做什么?
** 是在代码语法中尝试使用粗体时留下的拼写错误。
toupper 时,此代码会调用未定义的行为。
以下对我有用。
#include <algorithm>
void toUpperCase(std::string& str)
{
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}
int main()
{
std::string str = "hello";
toUpperCase(&str);
}
【讨论】:
toupper 时,此代码会调用未定义的行为。
字符串中有 ASCII 或国际字符吗?
如果是后一种情况,“大写”就没那么简单了,还要看使用的字母。有两院制和一院制字母表。只有两院字母的大小写字符不同。此外,还有复合字符,例如使用所谓的标题大小写的拉丁大写字母'DZ' (\u01F1 'DZ')。这意味着只有第一个字符 (D) 被更改。
我建议您查看ICU,以及简单和完整大小写映射之间的区别。这可能会有所帮助:
【讨论】:
如果您只使用 ASCII 字符则更快:
for(i=0;str[i]!=0;i++)
if(str[i]<='z' && str[i]>='a')
str[i]+='A'-'a';
请注意,此代码运行速度更快,但仅适用于 ASCII,不是“抽象”解决方案。
其他 UTF8 字母的扩展版本:
...
if(str[i]<='z' && str[i]>='a') //is latin
str[i]+='A'-'a';
else if(str[i]<='я' && str[i]>='а') //cyrillic
str[i]+='Я'-'я'
else if(str[i]<='ω' && str[i]>='α') //greek
str[i]+='Ω'-'ω'
//etc...
如果您需要完整的 UNICODE 解决方案或更传统和抽象的解决方案,请寻求其他答案并使用 C++ 字符串的方法。
【讨论】:
C++,但您在此处写了C 答案。 (我不是反对者之一。)
' 中包含的字符?
str[i]+='A'-'a' 而不是仅仅 32。这样的逻辑不仅适用于拉丁语
只要你对 ASCII-only 没问题,并且你可以提供一个指向 RW 内存的有效指针,C 中有一个简单且非常有效的单行:
void strtoupper(char* str)
{
while (*str) *(str++) = toupper((unsigned char)*str);
}
这对于您想要规范化为相同字符大小写的简单字符串(如 ASCII 标识符)特别有用。然后,您可以使用缓冲区构造 std:string 实例。
【讨论】:
使用 lambda。
std::string s("change my case");
std::locale locale;
auto to_upper = [&locale] (char ch) { return std::use_facet<std::ctype<char>>(locale).toupper(ch); };
std::transform(s.begin(), s.end(), s.begin(), to_upper);
【讨论】:
//works for ASCII -- no clear advantage over what is already posted...
std::string toupper(const std::string & s)
{
std::string ret(s.size(), char());
for(unsigned int i = 0; i < s.size(); ++i)
ret[i] = (s[i] <= 'z' && s[i] >= 'a') ? s[i]-('a'-'A') : s[i];
return ret;
}
【讨论】:
for (size_t i = 0 ...。也没有充分的理由让它如此难以阅读。这也首先复制字符串,然后循环遍历它。 @Luke 的答案在某些方面更好,除了不利用 'a' 字符常量。
#include <string>
#include <locale>
std::string str = "Hello World!";
auto & f = std::use_facet<std::ctype<char>>(std::locale());
f.toupper(str.data(), str.data() + str.size());
这将比使用全局 toupper 函数的所有答案表现更好,并且大概是 boost::to_upper 在下面所做的。
这是因为 ::toupper 必须查找语言环境 - 因为它可能已被不同的线程更改 - 对于每次调用,而这里只有对 locale() 的调用有这种惩罚。查找语言环境通常需要锁定。
这也适用于 C++98 后你替换自动,使用新的非常量 str.data(),并添加一个空格来打破模板关闭(“>>”到“>”)像这样:
std::use_facet<std::ctype<char> > & f =
std::use_facet<std::ctype<char> >(std::locale());
f.toupper(const_cast<char *>(str.data()), str.data() + str.size());
【讨论】:
typedef std::string::value_type char_t;
char_t up_char( char_t ch )
{
return std::use_facet< std::ctype< char_t > >( std::locale() ).toupper( ch );
}
std::string toupper( const std::string &src )
{
std::string result;
std::transform( src.begin(), src.end(), std::back_inserter( result ), up_char );
return result;
}
const std::string src = "test test TEST";
std::cout << toupper( src );
【讨论】:
reserve 和back_inserter(使字符串只复制一次)。 inline std::string to_lower(const std::string &s) { std::string result; result.reserve(s.size()); std::transform(s.begin(), s.end(), std::back_inserter( result ), static_cast<int(*)(int)>(std::tolower)); return result; }
std::locale() 而不是这个:std::locale(std::string(".") + std::to_string(GetConsoleCP()))。在我的情况下,在MSVC 2015 Update 3 下,可执行文件的大小增加了两倍。如果尝试仅使用std::locale() + facet,则在Release 中,可执行文件在+~60KB 上增加,在std::locale with string constructor 的情况下,在+~200KB 上增加。所以要小心。
@dirkgently 的answer 非常鼓舞人心,但我想强调的是,由于如下所示的关注,
与 中的所有其他函数一样,如果参数的值既不能表示为 unsigned char 也不等于 EOF,则 std::toupper 的行为是未定义的。为了安全地使用纯字符(或有符号字符)这些函数,应首先将参数转换为无符号字符
参考:std::toupper
由于标准没有指定普通char是有符号还是无符号[1],
std::toupper 的正确用法应该是:
#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
#include <string>
void ToUpper(std::string& input)
{
std::for_each(std::begin(input), std::end(input), [](char& c) {
c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
});
}
int main()
{
std::string s{ "Hello world!" };
std::cout << s << std::endl;
::ToUpper(s);
std::cout << s << std::endl;
return 0;
}
输出:
Hello world!
HELLO WORLD!
【讨论】:
std::string value;
for (std::string::iterator p = value.begin(); value.end() != p; ++p)
*p = toupper(*p);
【讨论】:
toupper 时,此代码会调用未定义的行为。
std::string str = "STriNg oF mIxID CasE lETteRS"
C++ 11
使用 for_each
std::for_each(str.begin(), str.end(), [](char & c){ c = ::toupper(c); });
使用变换
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
C++(仅限 Winodws)
_strupr_s(str, str.length());
C++(使用 Boost 库)
boost::to_upper_copy(str)
【讨论】:
尝试toupper() 函数 (#include <ctype.h>)。它接受字符作为参数,字符串由字符组成,因此您必须遍历每个单独的字符,这些字符放在一起构成字符串
【讨论】:
toupper 时,此建议会调用未定义的行为。您应该向unsigned char 提及必要的演员表。
这是最新的 C++11 代码
std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });
【讨论】:
toupper 时,此代码会调用未定义的行为。
使用 Boost.Text,它适用于 Unicode 文本
boost::text::text t = "Hello World";
boost::text::text uppered;
boost::text::to_title(t, std::inserter(uppered, uppered.end()));
std::string newstr = uppered.extract();
【讨论】:
如果你只想大写,试试这个功能。
#include <iostream>
using namespace std;
string upper(string text){
string upperCase;
for(int it : text){
if(it>96&&it<123){
upperCase += char(it-32);
}else{
upperCase += char(it);
}
}
return upperCase;
}
int main() {
string text = "^_abcdfghopqrvmwxyz{|}";
cout<<text<<"/";
text = upper(text);
cout<<text;
return 0;
}
Error: Range-based 'for' loops are not allowed in C++98 mode
【讨论】:
不确定是否有内置函数。试试这个:
包括 ctype.h 或 cctype 库以及 stdlib.h 作为预处理器指令的一部分。
string StringToUpper(string strToConvert)
{//change each element of the string to upper case
for(unsigned int i=0;i<strToConvert.length();i++)
{
strToConvert[i] = toupper(strToConvert[i]);
}
return strToConvert;//return the converted string
}
string StringToLower(string strToConvert)
{//change each element of the string to lower case
for(unsigned int i=0;i<strToConvert.length();i++)
{
strToConvert[i] = tolower(strToConvert[i]);
}
return strToConvert;//return the converted string
}
【讨论】:
toupper 时,此代码会调用未定义的行为。
我的解决方案(清除 alpha 的第 6 位):
#include <ctype.h>
inline void toupper(char* str)
{
while (str[i]) {
if (islower(str[i]))
str[i] &= ~32; // Clear bit 6 as it is what differs (32) between Upper and Lowercases
i++;
}
}
【讨论】:
toupper 时,此代码会调用未定义的行为。
基于Kyle_the_hacker's -----> answer 和我的临时演员。
在终端
列出所有语言环境locale -a
安装所有语言环境sudo apt-get install -y locales locales-all
编译main.cpp$ g++ main.cpp
运行编译好的程序$ ./a.out
结果
Zoë Saldaña played in La maldición del padre Cardona. ëèñ αω óóChloë
Zoë Saldaña played in La maldición del padre Cardona. ëèñ αω óóChloë
ZOË SALDAÑA PLAYED IN LA MALDICIÓN DEL PADRE CARDONA. ËÈÑ ΑΩ ÓÓCHLOË
ZOË SALDAÑA PLAYED IN LA MALDICIÓN DEL PADRE CARDONA. ËÈÑ ΑΩ ÓÓCHLOË
zoë saldaña played in la maldición del padre cardona. ëèñ αω óóchloë
zoë saldaña played in la maldición del padre cardona. ëèñ αω óóchloë
在 cmd 中运行 VCVARS 开发者工具"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
编译main.cpp> cl /EHa main.cpp /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /std:c++17 /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /MTd
Compilador de optimización de C/C++ de Microsoft (R) versión 19.27.29111 para x64
(C) Microsoft Corporation. Todos los derechos reservados.
main.cpp
Microsoft (R) Incremental Linker Version 14.27.29111.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
main.obj
kernel32.lib
user32.lib
gdi32.lib
winspool.lib
comdlg32.lib
advapi32.lib
shell32.lib
ole32.lib
oleaut32.lib
uuid.lib
odbc32.lib
odbccp32.lib
运行 main.exe>main.exe
结果
Zoë Saldaña played in La maldición del padre Cardona. ëèñ αω óóChloë
Zoë Saldaña played in La maldición del padre Cardona. ëèñ αω óóChloë
ZOË SALDAÑA PLAYED IN LA MALDICIÓN DEL PADRE CARDONA. ËÈÑ ΑΩ ÓÓCHLOË
ZOË SALDAÑA PLAYED IN LA MALDICIÓN DEL PADRE CARDONA. ËÈÑ ΑΩ ÓÓCHLOË
zoë saldaña played in la maldición del padre cardona. ëèñ αω óóchloë
zoë saldaña played in la maldición del padre cardona. ëèñ αω óóchloë
此代码仅在 Windows x64 和 Ubuntu Linux x64 上测试过。
/*
* Filename: c:\Users\x\Cpp\main.cpp
* Path: c:\Users\x\Cpp
* Filename: /home/x/Cpp/main.cpp
* Path: /home/x/Cpp
* Created Date: Saturday, October 17th 2020, 10:43:31 pm
* Author: Joma
*
* No Copyright 2020
*/
#include <iostream>
#include <set>
#include <string>
#include <locale>
// WINDOWS
#if (_WIN32)
#include <Windows.h>
#include <conio.h>
#define WINDOWS_PLATFORM 1
#define DLLCALL STDCALL
#define DLLIMPORT _declspec(dllimport)
#define DLLEXPORT _declspec(dllexport)
#define DLLPRIVATE
#define NOMINMAX
//EMSCRIPTEN
#elif defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#include <emscripten/bind.h>
#include <unistd.h>
#include <termios.h>
#define EMSCRIPTEN_PLATFORM 1
#define DLLCALL
#define DLLIMPORT
#define DLLEXPORT __attribute__((visibility("default")))
#define DLLPRIVATE __attribute__((visibility("hidden")))
// LINUX - Ubuntu, Fedora, , Centos, Debian, RedHat
#elif (__LINUX__ || __gnu_linux__ || __linux__ || __linux || linux)
#define LINUX_PLATFORM 1
#include <unistd.h>
#include <termios.h>
#define DLLCALL CDECL
#define DLLIMPORT
#define DLLEXPORT __attribute__((visibility("default")))
#define DLLPRIVATE __attribute__((visibility("hidden")))
#define CoTaskMemAlloc(p) malloc(p)
#define CoTaskMemFree(p) free(p)
//ANDROID
#elif (__ANDROID__ || ANDROID)
#define ANDROID_PLATFORM 1
#define DLLCALL
#define DLLIMPORT
#define DLLEXPORT __attribute__((visibility("default")))
#define DLLPRIVATE __attribute__((visibility("hidden")))
//MACOS
#elif defined(__APPLE__)
#include <unistd.h>
#include <termios.h>
#define DLLCALL
#define DLLIMPORT
#define DLLEXPORT __attribute__((visibility("default")))
#define DLLPRIVATE __attribute__((visibility("hidden")))
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
#define IOS_SIMULATOR_PLATFORM 1
#elif TARGET_OS_IPHONE
#define IOS_PLATFORM 1
#elif TARGET_OS_MAC
#define MACOS_PLATFORM 1
#else
#endif
#endif
typedef std::string String;
typedef std::wstring WString;
#define EMPTY_STRING u8""s
#define EMPTY_WSTRING L""s
using namespace std::literals::string_literals;
class Strings
{
public:
static String WideStringToString(const WString& wstr)
{
if (wstr.empty())
{
return String();
}
size_t pos;
size_t begin = 0;
String ret;
#if WINDOWS_PLATFORM
int size;
pos = wstr.find(static_cast<wchar_t>(0), begin);
while (pos != WString::npos && begin < wstr.length())
{
WString segment = WString(&wstr[begin], pos - begin);
size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &segment[0], segment.size(), NULL, 0, NULL, NULL);
String converted = String(size, 0);
WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &segment[0], segment.size(), &converted[0], converted.size(), NULL, NULL);
ret.append(converted);
ret.append({ 0 });
begin = pos + 1;
pos = wstr.find(static_cast<wchar_t>(0), begin);
}
if (begin <= wstr.length())
{
WString segment = WString(&wstr[begin], wstr.length() - begin);
size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &segment[0], segment.size(), NULL, 0, NULL, NULL);
String converted = String(size, 0);
WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &segment[0], segment.size(), &converted[0], converted.size(), NULL, NULL);
ret.append(converted);
}
#elif LINUX_PLATFORM || MACOS_PLATFORM || EMSCRIPTEN_PLATFORM
size_t size;
pos = wstr.find(static_cast<wchar_t>(0), begin);
while (pos != WString::npos && begin < wstr.length())
{
WString segment = WString(&wstr[begin], pos - begin);
size = wcstombs(nullptr, segment.c_str(), 0);
String converted = String(size, 0);
wcstombs(&converted[0], segment.c_str(), converted.size());
ret.append(converted);
ret.append({ 0 });
begin = pos + 1;
pos = wstr.find(static_cast<wchar_t>(0), begin);
}
if (begin <= wstr.length())
{
WString segment = WString(&wstr[begin], wstr.length() - begin);
size = wcstombs(nullptr, segment.c_str(), 0);
String converted = String(size, 0);
wcstombs(&converted[0], segment.c_str(), converted.size());
ret.append(converted);
}
#else
static_assert(false, "Unknown Platform");
#endif
return ret;
}
static WString StringToWideString(const String& str)
{
if (str.empty())
{
return WString();
}
size_t pos;
size_t begin = 0;
WString ret;
#ifdef WINDOWS_PLATFORM
int size = 0;
pos = str.find(static_cast<char>(0), begin);
while (pos != std::string::npos) {
std::string segment = std::string(&str[begin], pos - begin);
std::wstring converted = std::wstring(segment.size() + 1, 0);
size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, &segment[0], segment.size(), &converted[0], converted.length());
converted.resize(size);
ret.append(converted);
ret.append({ 0 });
begin = pos + 1;
pos = str.find(static_cast<char>(0), begin);
}
if (begin < str.length()) {
std::string segment = std::string(&str[begin], str.length() - begin);
std::wstring converted = std::wstring(segment.size() + 1, 0);
size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, segment.c_str(), segment.size(), &converted[0], converted.length());
converted.resize(size);
ret.append(converted);
}
#elif LINUX_PLATFORM || MACOS_PLATFORM || EMSCRIPTEN_PLATFORM
size_t size;
pos = str.find(static_cast<char>(0), begin);
while (pos != String::npos)
{
String segment = String(&str[begin], pos - begin);
WString converted = WString(segment.size(), 0);
size = mbstowcs(&converted[0], &segment[0], converted.size());
converted.resize(size);
ret.append(converted);
ret.append({ 0 });
begin = pos + 1;
pos = str.find(static_cast<char>(0), begin);
}
if (begin < str.length())
{
String segment = String(&str[begin], str.length() - begin);
WString converted = WString(segment.size(), 0);
size = mbstowcs(&converted[0], &segment[0], converted.size());
converted.resize(size);
ret.append(converted);
}
#else
static_assert(false, "Unknown Platform");
#endif
return ret;
}
static WString ToUpper(const WString& data)
{
WString result = data;
auto& f = std::use_facet<std::ctype<wchar_t>>(std::locale());
f.toupper(&result[0], &result[0] + result.size());
return result;
}
static String ToUpper(const String& data)
{
return WideStringToString(ToUpper(StringToWideString(data)));
}
static WString ToLower(const WString& data)
{
WString result = data;
auto& f = std::use_facet<std::ctype<wchar_t>>(std::locale());
f.tolower(&result[0], &result[0] + result.size());
return result;
}
static String ToLower(const String& data)
{
return WideStringToString(ToLower(StringToWideString(data)));
}
};
enum class ConsoleTextStyle
{
DEFAULT = 0,
BOLD = 1,
FAINT = 2,
ITALIC = 3,
UNDERLINE = 4,
SLOW_BLINK = 5,
RAPID_BLINK = 6,
REVERSE = 7,
};
enum class ConsoleForeground
{
DEFAULT = 39,
BLACK = 30,
DARK_RED = 31,
DARK_GREEN = 32,
DARK_YELLOW = 33,
DARK_BLUE = 34,
DARK_MAGENTA = 35,
DARK_CYAN = 36,
GRAY = 37,
DARK_GRAY = 90,
RED = 91,
GREEN = 92,
YELLOW = 93,
BLUE = 94,
MAGENTA = 95,
CYAN = 96,
WHITE = 97
};
enum class ConsoleBackground
{
DEFAULT = 49,
BLACK = 40,
DARK_RED = 41,
DARK_GREEN = 42,
DARK_YELLOW = 43,
DARK_BLUE = 44,
DARK_MAGENTA = 45,
DARK_CYAN = 46,
GRAY = 47,
DARK_GRAY = 100,
RED = 101,
GREEN = 102,
YELLOW = 103,
BLUE = 104,
MAGENTA = 105,
CYAN = 106,
WHITE = 107
};
class Console
{
private:
static void EnableVirtualTermimalProcessing()
{
#if defined WINDOWS_PLATFORM
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
if (!(dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
{
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode);
}
#endif
}
static void ResetTerminalFormat()
{
std::cout << u8"\033[0m";
}
static void SetVirtualTerminalFormat(ConsoleForeground foreground, ConsoleBackground background, std::set<ConsoleTextStyle> styles)
{
String format = u8"\033[";
format.append(std::to_string(static_cast<int>(foreground)));
format.append(u8";");
format.append(std::to_string(static_cast<int>(background)));
if (styles.size() > 0)
{
for (auto it = styles.begin(); it != styles.end(); ++it)
{
format.append(u8";");
format.append(std::to_string(static_cast<int>(*it)));
}
}
format.append(u8"m");
std::cout << format;
}
public:
static void Clear()
{
#ifdef WINDOWS_PLATFORM
std::system(u8"cls");
#elif LINUX_PLATFORM || defined MACOS_PLATFORM
std::system(u8"clear");
#elif EMSCRIPTEN_PLATFORM
emscripten::val::global()["console"].call<void>(u8"clear");
#else
static_assert(false, "Unknown Platform");
#endif
}
static void Write(const String& s, ConsoleForeground foreground = ConsoleForeground::DEFAULT, ConsoleBackground background = ConsoleBackground::DEFAULT, std::set<ConsoleTextStyle> styles = {})
{
#ifndef EMSCRIPTEN_PLATFORM
EnableVirtualTermimalProcessing();
SetVirtualTerminalFormat(foreground, background, styles);
#endif
String str = s;
#ifdef WINDOWS_PLATFORM
WString unicode = Strings::StringToWideString(str);
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), static_cast<DWORD>(unicode.length()), nullptr, nullptr);
#elif defined LINUX_PLATFORM || defined MACOS_PLATFORM || EMSCRIPTEN_PLATFORM
std::cout << str;
#else
static_assert(false, "Unknown Platform");
#endif
#ifndef EMSCRIPTEN_PLATFORM
ResetTerminalFormat();
#endif
}
static void WriteLine(const String& s, ConsoleForeground foreground = ConsoleForeground::DEFAULT, ConsoleBackground background = ConsoleBackground::DEFAULT, std::set<ConsoleTextStyle> styles = {})
{
Write(s, foreground, background, styles);
std::cout << std::endl;
}
static void Write(const WString& s, ConsoleForeground foreground = ConsoleForeground::DEFAULT, ConsoleBackground background = ConsoleBackground::DEFAULT, std::set<ConsoleTextStyle> styles = {})
{
#ifndef EMSCRIPTEN_PLATFORM
EnableVirtualTermimalProcessing();
SetVirtualTerminalFormat(foreground, background, styles);
#endif
WString str = s;
#ifdef WINDOWS_PLATFORM
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), str.c_str(), static_cast<DWORD>(str.length()), nullptr, nullptr);
#elif LINUX_PLATFORM || MACOS_PLATFORM || EMSCRIPTEN_PLATFORM
std::cout << Strings::WideStringToString(str);
#else
static_assert(false, "Unknown Platform");
#endif
#ifndef EMSCRIPTEN_PLATFORM
ResetTerminalFormat();
#endif
}
static void WriteLine(const WString& s, ConsoleForeground foreground = ConsoleForeground::DEFAULT, ConsoleBackground background = ConsoleBackground::DEFAULT, std::set<ConsoleTextStyle> styles = {})
{
Write(s, foreground, background, styles);
std::cout << std::endl;
}
static void WriteLine()
{
std::cout << std::endl;
}
static void Pause()
{
char c;
do
{
c = getchar();
std::cout << "Press Key " << std::endl;
} while (c != 64);
std::cout << "KeyPressed" << std::endl;
}
static int PauseAny(bool printWhenPressed = false, ConsoleForeground foreground = ConsoleForeground::DEFAULT, ConsoleBackground background = ConsoleBackground::DEFAULT, std::set<ConsoleTextStyle> styles = {})
{
int ch;
#ifdef WINDOWS_PLATFORM
ch = _getch();
#elif LINUX_PLATFORM || MACOS_PLATFORM || EMSCRIPTEN_PLATFORM
struct termios oldt, newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
#else
static_assert(false, "Unknown Platform");
#endif
if (printWhenPressed)
{
Console::Write(String(1, ch), foreground, background, styles);
}
return ch;
}
};
int main()
{
std::locale::global(std::locale(u8"en_US.UTF-8"));
String dataStr = u8"Zoë Saldaña played in La maldición del padre Cardona. ëèñ αω óóChloë";
WString dataWStr = L"Zoë Saldaña played in La maldición del padre Cardona. ëèñ αω óóChloë";
std::string locale = u8"";
//std::string locale = u8"de_DE.UTF-8";
//std::string locale = u8"en_US.UTF-8";
Console::WriteLine(dataStr);
Console::WriteLine(dataWStr);
dataStr = Strings::ToUpper(dataStr);
dataWStr = Strings::ToUpper(dataWStr);
Console::WriteLine(dataStr);
Console::WriteLine(dataWStr);
dataStr = Strings::ToLower(dataStr);
dataWStr = Strings::ToLower(dataWStr);
Console::WriteLine(dataStr);
Console::WriteLine(dataWStr);
Console::WriteLine(u8"Press any key to exit"s, ConsoleForeground::DARK_GRAY);
Console::PauseAny();
return 0;
}
【讨论】:
//Since I work on a MAC, and Windows methods mentioned do not work for me, I //just built this quick method.
string str;
str = "This String Will Print Out in all CAPS";
int len = str.size();
char b;
for (int i = 0; i < len; i++){
b = str[i];
b = toupper(b);
// b = to lower(b); //alternately
str[i] = b;
}
cout<<str;
【讨论】:
此页面上的所有这些解决方案都比他们需要的更难。
这样做
RegName = "SomE StRing That you wAnt ConvErTed";
NameLength = RegName.Size();
for (int forLoop = 0; forLoop < NameLength; ++forLoop)
{
RegName[forLoop] = tolower(RegName[forLoop]);
}
RegName 是您的string。
获取您的字符串大小不要使用string.size() 作为您的实际测试人员,非常混乱和
可能会导致问题。
然后。最基本的for 循环。
记住字符串大小也会返回分隔符,所以在循环测试中使用
输出将是: 一些你想要转换的字符串
【讨论】:
tolower 循环,而且大多数都使用标准的循环变量名称,例如i,而不是奇怪的forLoop。
不使用任何库:
std::string YourClass::Uppercase(const std::string & Text)
{
std::string UppperCaseString;
UppperCaseString.reserve(Text.size());
for (std::string::const_iterator it=Text.begin(); it<Text.end(); ++it)
{
UppperCaseString.push_back(((0x60 < *it) && (*it < 0x7B)) ? (*it - static_cast<char>(0x20)) : *it);
}
return UppperCaseString;
}
【讨论】:
如果您只关心 8 位字符(除了 Milan Babuškov 之外的所有其他答案也都假设),您可以通过在编译时使用元编程生成查找表来获得最快的速度。在 ideone.com 上,它的运行速度比库函数快 7 倍,比手写版本快 3 倍 (http://ideone.com/sb1Rup)。它也可以通过特征进行定制,不会减慢速度。
template<int ...Is>
struct IntVector{
using Type = IntVector<Is...>;
};
template<typename T_Vector, int I_New>
struct PushFront;
template<int ...Is, int I_New>
struct PushFront<IntVector<Is...>,I_New> : IntVector<I_New,Is...>{};
template<int I_Size, typename T_Vector = IntVector<>>
struct Iota : Iota< I_Size-1, typename PushFront<T_Vector,I_Size-1>::Type> {};
template<typename T_Vector>
struct Iota<0,T_Vector> : T_Vector{};
template<char C_In>
struct ToUpperTraits {
enum { value = (C_In >= 'a' && C_In <='z') ? C_In - ('a'-'A'):C_In };
};
template<typename T>
struct TableToUpper;
template<int ...Is>
struct TableToUpper<IntVector<Is...>>{
static char at(const char in){
static const char table[] = {ToUpperTraits<Is>::value...};
return table[in];
}
};
int tableToUpper(const char c){
using Table = TableToUpper<typename Iota<256>::Type>;
return Table::at(c);
}
用例:
std::transform(in.begin(),in.end(),out.begin(),tableToUpper);
对于它的工作原理的深入(多页)描述允许我无耻地插入我的博客:http://metaporky.blogspot.de/2014/07/part-4-generating-look-up-tables-at.html
【讨论】:
template<size_t size>
char* toupper(char (&dst)[size], const char* src) {
// generate mapping table once
static char maptable[256];
static bool mapped;
if (!mapped) {
for (char c = 0; c < 256; c++) {
if (c >= 'a' && c <= 'z')
maptable[c] = c & 0xdf;
else
maptable[c] = c;
}
mapped = true;
}
// use mapping table to quickly transform text
for (int i = 0; *src && i < size; i++) {
dst[i] = maptable[*(src++)];
}
return dst;
}
【讨论】:
这个c++函数总是返回大写字符串...
#include <locale>
#include <string>
using namespace std;
string toUpper (string str){
locale loc;
string n;
for (string::size_type i=0; i<str.length(); ++i)
n += toupper(str[i], loc);
return n;
}
【讨论】: