如何将字符串转换为大写。我从谷歌上找到的例子只涉及字符。


当前回答

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

其他回答

这个问题可以用ASCII字符集的SIMD向量化。


加速比较:

在Core2Duo (Merom)上使用x86- 64gcc 5.2 -O3 -march=native进行初步测试。相同的120个字符的字符串(混合小写和非小写ASCII),在循环中转换40M次(没有跨文件内联,因此编译器不能优化或从循环中提升任何字符)。相同的源和dest缓冲区,因此没有malloc开销或内存/缓存影响:数据在L1缓存中始终是热的,我们完全受cpu限制。

boost::to_upper_copy<char*, std::string>(): 198.0s. Yes, Boost 1.58 on Ubuntu 15.10 is really this slow. I profiled and single-stepped the asm in a debugger, and it's really, really bad: there's a dynamic_cast of a locale variable happening per character!!! (dynamic_cast takes multiple calls to strcmp). This happens with LANG=C and with LANG=en_CA.UTF-8. I didn't test using a RangeT other than std::string. Maybe the other form of to_upper_copy optimizes better, but I think it will always new/malloc space for the copy, so it's harder to test. Maybe something I did differs from a normal use-case, and maybe normally stopped g++ can hoist the locale setup stuff out of the per-character loop. My loop reading from a std::string and writing to a char dstbuf[4096] makes sense for testing. loop calling glibc toupper: 6.67s (not checking the int result for potential multi-byte UTF-8, though. This matters for Turkish.) ASCII-only loop: 8.79s (my baseline version for the results below.) Apparently a table-lookup is faster than a cmov, with the table hot in L1 anyway. ASCII-only auto-vectorized: 2.51s. (120 chars is half way between worst case and best case, see below) ASCII-only manually vectorized: 1.35s

另请参阅这个关于设置区域设置时toupper()在Windows上运行缓慢的问题。


Boost比其他选项慢了一个数量级,这让我感到震惊。我反复检查我是否启用了-O3,甚至单步运行asm,看看它在做什么。它几乎与clang++ 3.8的速度完全相同。它在每个字符循环中有巨大的开销。性能记录/报告结果(对于周期性能事件)为:

  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实现不会自动向量化。)

因此,对于足够小以适合缓存的字符串,对于长度为128个字符的字符串,首先执行strlen可以获得显著的加速。对于显式长度的字符串(如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的循环。

4000万次迭代,Core2 (Merom) 2.4GHz。gcc 5.2 -O3 -march=native。(Ubuntu 15.10)。DST != SRC(因此我们创建了一个副本),但它们不重叠(也不在附近)。两者都是对齐的。

15个字符字符串:基线:1.08秒。autovec: 1.34秒 16字符字符串:基线:1.16秒。autovec: 1.52秒 127字符字符串:基线:8.91秒。Autovec: 2.98s //非矢量清理有15个字符要处理 128字符字符串:基线:9秒。autovec: 2.06秒 129字符字符串:基线:9.04秒。Autovec: 2.07s //非向量清理有1个字符要处理

一些结果与clang略有不同。

调用该函数的微基准测试循环在一个单独的文件中。否则,它会内联并将strlen()提升到循环之外,运行速度会大大加快,特别是对于16字符字符串(0.187s)。

这样做的主要优点是gcc可以对任何体系结构自动向量化,但主要缺点是对于小字符串的常见情况来说速度较慢。


因此,可以大大提高速度,但编译器自动向量化并不能生成很好的代码,特别是对于最后到15个字符的清理。

使用SSE intrinsic手工向量化:

基于我的大小写翻转函数它颠倒了每个字母的大小写。它利用了“无符号比较技巧”,您可以通过范围移位对单个无符号比较进行low < a && a <= high,因此任何小于low的值都换行为大于high的值。(如果低点和高点之间的距离不是太远,这个方法就有效。)

SSE只有一个带符号的比较数,但我们仍然可以使用“unsigned” 通过范围移动到符号范围底部的技巧:减去'a'+128,因此字母字符的范围从-128到-128+25 (-128+'z'-'a')

注意,加128和减128对于8位整数是一样的。进位无处可去,所以它只是xor(无进位加法),翻转高位。

#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时,非原子的read-modify-write与根本不触及某些字节是不一样的,并且会破坏多线程代码。

我们有一个标量循环,也可以让src对齐。由于我们不知道终止0将在哪里,来自src的未对齐的加载可能会交叉到下一页和段错误。如果我们需要一个对齐的16B块中的任何字节,加载整个对齐的16B块总是安全的。

完整的来源:在github的要旨。

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

4000万次迭代,Core2 (Merom) 2.4GHz。gcc 5.2 -O3 -march=native。(Ubuntu 15.10)。DST != SRC(因此我们创建了一个副本),但它们不重叠(也不在附近)。两者都是对齐的。

15个字符字符串:基线:1.08秒。autovec: 1.34 s。手动:1.29秒 16字符字符串:基线:1.16秒。autovec: 1.52 s。手动:0.335秒 31个字符字符串:手动:0.479s 127字符字符串:基线:8.91秒。autovec: 2.98 s。手动:0.925秒 128字符字符串:基线:9秒。autovec: 2.06 s。手动:0.931秒 129字符字符串:基线:9.04秒。autovec: 2.07 s。手动:1.02秒

(实际上是在循环中使用_mm_store进行计时,而不是_mm_storeu,因为即使在地址对齐时,storeu在Merom上也较慢。尼哈勒姆和以后都没问题。我现在也保持代码原样,而不是修复在某些情况下复制终止0的失败,因为我不想重新计时。)

对于比16B长的短字符串,这比自动向量化要快得多。长度小于一个向量的宽度不会出现问题。他们可能是一个问题,当操作在原地,因为一个商店转发摊位。(但请注意,处理我们自己的输出,而不是原始输入,仍然是可以的,因为toupper是幂等的)。

根据周围代码的需求和目标微架构,在不同的用例中有很大的调优范围。让编译器为清理部分发出良好的代码是很棘手的。使用ffs(3)(在x86上编译为bsf或tzcnt)似乎很好,但显然这一点需要重新考虑,因为我在写完大部分答案后注意到一个错误(参见FIXME评论)。

对于更小的字符串,可以使用movq或movd加载/存储来获得矢量加速。根据需要定制您的用例。


utf - 8:

我们可以检测我们的向量是否有任何具有高位设置的字节,在这种情况下,对于该向量退回到一个支持utf-8的标量循环。dst点可以比src指针前进不同的量,但一旦我们回到一个对齐的src指针,我们仍然只是将未对齐的向量存储到dst。

对于UTF-8,但主要由UTF-8的ASCII子集组成的文本,这可能很好:在常见情况下高性能,在所有情况下都有正确的行为。但是,当有大量非ascii时,可能会比一直停留在标量UTF-8感知循环中更糟糕。

以牺牲其他语言为代价提高英语的速度并不是一个经得起未来考验的决定,如果负面影响很大的话。


Locale-aware:

在土耳其语言环境(tr_TR)中,toupper('i')的正确结果是'İ' (U0130),而不是'i'(纯ASCII)。参见Martin Bonner关于在Windows上降低速度的问题的评论。

我们还可以检查异常列表和回退到那里的标量,比如多字节UTF8输入字符。

有了这么多的复杂性,SSE4.2 PCMPISTRM或其他东西可能能够一次性完成我们的许多检查。

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

尝试toupper()函数(#include <ctype.h>)。它接受字符作为参数,字符串是由字符组成的,所以你必须遍历每个单独的字符,当它们放在一起组成字符串时

#include <algorithm>
#include <string>

std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);

不使用任何库:

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