将C ++中的字符串转换为大写


268

如何将字符串转换为大写。我从谷歌搜索中找到的示例仅涉及字符。

Answers:


205

Boost字符串算法:

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

5
这也具有i18n的优势,其中::toupper最有可能采用ASCII。
Ben Straub

4
您的最后一行没有编译-您必须更改为以下内容:std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
maxschlepzig 2014年

58
这不是可接受的答案,因为它需要增强,否则应更改标题。
安德里亚(Andrea)

44
是的,我将只为to_upper安装boost。</ sarcasm> :)
thang

12
作为“我如何在C ++中执行x的答案”的答案,我本人不愿意使用boost 。因为boost根本不是轻量级的解决方案。看来,您要么购买了boost作为框架(或ACE或Qt或Recusion ToolKit ++或...),要么却没有。我希望看到语言解决方案。
jwm

486
#include <algorithm>
#include <string>

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

8
实际上,toupper()可以实现为宏。这可能会引起问题。
2009年

3
我认为,带有boost.lambda的bind(:: toupper,construct <unsigned char>(_ 1))会很好地工作。
Johannes Schaub-litb

11
这种方法对于ASCII可以很好地工作,但是对于多字节字符编码或特殊的大小写规则(例如德语“ß”)则无效。
dan04'8

9
我使用boost库将接受的答案更改为一个答案,因为它更快(在我的非正式测试中),易于使用且没有与该解决方案相关的问题。对于无法使用升压的情况,这仍然是一个很好的解决方案。
OrangeAlmondSoap 2011年

2
我不明白为什么在没有::限定符之前编译器会拒绝此代码toupper。有任何想法吗?
sasha.sochka 2013年

89

使用C ++ 11和toupper()的简短解决方案。

for (auto & c: str) c = toupper(c);

不会cconst char类型(来自auto)吗?如果是这样,则不能(由于const部分原因)将其分配给所返回的值toupper(c)
PolGraphic

5
@PolGraphic:范围-基于用于使用容器的begin()/ end()方法迭代其内容。std :: basic_string同时具有const和可变的迭代器(分别由cbegin()和begin()返回,请参见std :: basic_string :: begin),因此for(:)使用一个合适的(cbegin()如果str为使用auto =:= const char声明的const,否则使用auto =:= char的begin())。
Thanasis Papoutsidakis

5
请参见下面的dirkgently的分析工具,c需要unsigned char对此进行校正。
克里斯·伦戈

boost的to_upper()与c ++ STL函数似乎比toupper更加一致。
tartaruga_casco_mole

29
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库标头<ctype.h>,<wctype.h>,<string.h>,<wchar.h>和<stdlib.h>相同。[...]

  • 这意味着cctype成员很可能是不适合在标准算法中直接使用的宏。

  • 同一示例的另一个问题是,它不会强制转换参数或验证其是否为非负数。这对于使用普通char签名的系统尤其危险。(原因是:如果将其实现为宏,则可能会使用查找表并将您的参数索引到该表中。负索引将给您UB。)


普通cctype成员是宏。我记得读过它们也必须是函数,尽管我没有C90标准的副本,也不知道是否明确声明了它。
David Thornley,2009年

1
它们必须是C ++中的函数-即使C允许它们成为宏。我同意您关于选角的第二点。顶级解决方案可能会传递负值,并导致UB。这就是我没有投票的原因(但我也没有投反对票):)
Johannes Schaub-litb

1
不能缺少标准报价:7.4.2.2/1(可怜的家伙,仅引用C99 TC2草案),以及荣耀c ++ 98标准中的C ++ 17.4.1.2/6。
Johannes Schaub-litb

1
(请注意它的脚注:“这不允许提供屏蔽宏的常规做法。。。。。。。。。。。。。。。。):)
Johannes Schaub -litb


27

对于ASCII字符集,SIMD可以解决此问题


加速比较:

-O3 -march=native在Core2Duo(Merom)上使用x86-64 gcc 5.2 进行的初步测试。相同的120个字符的字符串(混合的小写字母和非小写ASCII)在循环中转换了40M次(没有跨文件内联,因此编译器无法优化或提升其中的任何一个)。源缓冲区和目标缓冲区相同,因此没有malloc开销或内存/缓存影响:数据一直在L1缓存中处于高温状态,而我们纯粹是受CPU限制的。

  • boost::to_upper_copy<char*, std::string>()198.0秒。是的,Ubuntu 15.10上的Boost 1.58确实很慢。我在调试器中对asm进行了概要分析并单步执行,这确实非常糟糕:每个字符都有一个dynamic_cast的语言环境变量!!!(dynamic_cast需要多次调用strcmp)。LANG=C和一起发生这种情况LANG=en_CA.UTF-8

    我没有使用除std :: string之外的RangeT进行测试。 也许的其他形式to_upper_copy提供更优更好,但我认为它会永远new/ malloc在复制空间,所以它很难测试。也许我所做的事情与正常的用例有所不同,也许正常停止的g ++可以将语言环境设置的内容从每个字符循环中提升出来。我的循环从a读取std::string并写入到a char dstbuf[4096]对于测试很有意义。

  • 循环调用glibc toupper6.67秒 (不过,不检查int潜在的多字节UTF-8 的结果。这对于土耳其语很重要。)

  • 仅ASCII的循环:8.79秒 (我的基线版本用于下面的结果。)显然,表查找比a快cmov,无论如何该表在L1中都很热。
  • 仅ASCII自动矢量化:2.51s。(120个字符介于最坏情况和最好情况之间,请参阅下文)
  • 仅ASCII手动矢量化:1.35s

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


我感到震惊的是,Boost比其他选项慢一个数量级。我仔细检查了我是否-O3启用了该功能,甚至单步检查了组件的运行状况。与clang ++ 3.8几乎完全相同的速度。每个字符循环内部都有巨大的开销。的perf record/ report结果(对于cyclesPERF事件)为:

  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才会自动对循环进行矢量化。(即,像普通C实现那样的搜索循环strlen不会自动矢量化。)

因此,对于足够小的字符串以适合高速缓存的字符串,从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(因此我们制作了副本),但它们没有重叠(并且不在附近)。两者都对齐。

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

对于clang,某些结果有些不同。

调用该函数的微基准循环位于一个单独的文件中。否则,它会内联并strlen()从循环中吊起,并且运行速度会大大加快,尤其是。为16个字符字符串(0.187s)。

gcc的主要优点是可以对任何体系结构将其自动矢量化,但是主要缺点是,在通常的小字符串情况下,它要慢一些。


因此,可以大大提高速度,但是编译器自动向量化并不能产生出色的代码,尤其是。用于清理最后15个字符。

使用SSE内部函数进行手动向量化:

基于我的case-flip函数,该函数可以反转每个字母字符的大小写。它利用了“无符号比较技巧”,在这里您可以low < a && a <= high通过范围移动来进行单个无符号比较,这样,小于的任何值都会low换成大于的值high。(如果相隔不远lowhigh则此方法有效。)

SSE只有一个带符号的比较器更大,但是我们仍然可以通过将范围移动到带符号的范围的底部来使用“无符号比较”技巧:减去“ 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块始终是安全的。

全文:在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(因此我们制作了副本),但它们没有重叠(并且不在附近)。两者都对齐。

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

(实际上_mm_store是在循环中定时,不是_mm_storeu,因为即使地址对齐,在Merom上storeu也会变慢。在Nehalem和更高版本上都很好。我现在还是按原样保留代码,而不是解决无法复制的问题在某些情况下以0结尾,因为我不想重新计时所有时间。)

因此,对于长度超过16B的短字符串,这比自动矢量化要快得多。长度小于向量宽度不存在问题。在原地操作时,它们可能会成为问题,因为存储转发停滞。(但是请注意,处理我们自己的输出而不是原始输入还是可以的,因为toupper是幂等的)。

根据周围代码的需求以及目标微体系结构,有很多范围可以针对不同的用例进行调整。让编译器为清理部分发出漂亮的代码是棘手的。使用ffs(3)(在x86上编译为bsf或tzcnt)使用似乎不错,但显然,该位需要重新考虑,因为我在编写了大部分答案后发现一个错误(请参阅FIXME注释)。

可以通过movqmovd加载/存储来获得甚至更小的字符串的矢量加速。根据您的用例进行定制。


UTF-8:

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

对于UTF-8,但主要由UTF-8的ASCII子集组成的文本,这可能很好:通常情况下具有高性能,并且在所有情况下均具有正确的行为。但是,当存在大量非ASCII字符时,它可能比一直保持在标量UTF-8感知循环中更糟糕。

如果不利之处很严重,那么以牺牲其他语言为代价来提高英语速度并不是一个面向未来的决定。


区域感知:

在土耳其语区域设置(tr_TR)中,来自的正确结果toupper('i')'İ'(U0130),而不是'I'(纯ASCII)。请参阅Martin Bonnertolower()关于Windows速度慢的问题的评论

我们还可以检查一个例外列表并在那里回退到标量,例如多字节UTF8输入字符。

由于存在如此多的复杂性,SSE4.2 PCMPISTRM或某些功能也许可以一次完成很多检查。


20

字符串中是否包含ASCII或国际字符?

如果是后一种情况,“大写”并不是那么简单,它取决于所使用的字母。有两院制和一院制的字母表。只有双头字母具有大小写不同的字符。另外,还有一些复合字符,如拉丁大写字母“DZ”(\ u01F1“DZ”),它采用所谓的首字母大写。这意味着仅第一个字符(D)被更改。

我建议您研究ICU以及简单案例映射和完整案例映射之间的区别。这可能会有所帮助:

http://userguide.icu-project.org/transforms/casemappings


7
或德国eszet(sp?),看起来像希腊字母beta,意思是“ ss”。没有单个德语字符表示“ SS”,即大写字母。大写的德语单词“ street”更长了一个字符。
David Thornley,2009年

6
另一个特殊情况是希腊字母sigma(Σ),它有两个小写版本,具体取决于它是否在单词(ς)的末尾(σ)。然后是特定于语言的规则,例如土耳其语具有区分大小写的I↔ı和İcasei。
dan04'1

1
“大包装”称为折叠箱。
哥伦布2015年

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

4
如果您无权提高第二种解决方案,则可能是最好的解决方案。**第一个解的参数后的星星会做什么?
山姆·布林克

1
我很确定这**是尝试在代码语法中使用粗体字留下的错字。
MasterHD

1
toupper用负数调用时,此代码将调用未定义的行为。
罗兰·伊利格

17

以下对我有用。

#include <algorithm>
void  toUpperCase(std::string& str)
{
    std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}

int main()
{
   std::string str = "hello";
   toUpperCase(&str);
}

请注意,std :: transform是在<algorithm>中定义的
edj

是。这个#include是必需的,#include <algorithm>
Pabitra Dash

1
toupper用负数调用时,此代码将调用未定义的行为。
罗兰·伊利格

user648545提供的答案的副本– -1
Piotr Dobrogost

@PiotrDobrogost我不知道user648545给出的答案。我还没有复制它。当我比较两个方法时,尽管两个函数都使用库函数转换,但方法签名完全不同。
Pabitra Dash

13

使用lambda。

std::string s("change my case");

auto to_upper = [] (char_t ch) { return std::use_facet<std::ctype<char_t>>(std::locale()).toupper(ch); };

std::transform(s.begin(), s.end(), s.begin(), to_upper);

2
拜伦,不用担心其他评论。像您一样,用新的(现代)解决方案回答旧问题是完全可以的。
Kyberias 2014年

13

如果仅使用ASCII字符,则速度更快:

for(i=0;str[i]!=0;i++)
  if(str[i]<='z' && str[i]>='a')
    str[i]-=32;

请注意,此代码运行速度更快,但仅适用于ASCII,而不是“抽象”解决方案。

如果您需要UNICODE解决方案或更常规的抽象解决方案,请寻求其他答案并使用C ++字符串方法。


1
该问题被标记为C++,但是您C在此处写了一个答案。(我不是
下降投票者

6
我在这里写了一个C答案和一个C ++答案,因为C ++被编写为与C源代码完全兼容,所以任何C解决方案也是C ++正确的解决方案
Luca

但是给出一个尊重C ++方式的答案要好得多。
Dmitriy Yurchenko 2014年

标准的c ++方法是将std :: transform与toupper一起使用。那是更少的代码,并且肯定是可移植的。此代码依赖于“事实”,即系统将使用ascii作为字符编码机制。不确定所有系统均基于此编码,因此不确定该系统是否可移植。
AlexTheo

1
为什么您决定使用ASCII代码代替包含在其中的字符'
HolyBlackCat

11

只要您只使用ASCII并且可以提供指向RW内存的有效指针,C语言中就会有一个简单而有效的单行代码:

void strtoupper(char* str)
{ 
    while (*str) *(str++) = toupper((unsigned char)*str);
}

这对于要标准化为相同字符大小写的简单字符串(如ASCII标识符)特别有用。然后,您可以使用缓冲区构造一个std:string实例。


有人指出,此答案适用于ac字符串,而不是std :: string
EvilTeach

这具有明显的固有安全漏洞。我不会的
拜伦'18

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

s.size()的类型为std :: size_t,根据实现的不同,AFAIK可以很容易地将其取消签名
odinthenerd 2014年

我认为没有任何现代实现中对std :: string :: size的结果进行签名。考虑到这一点,无论从语义上还是在实践上,都没有负数之类的东西,我将选择size_t至少是一个32位无符号整数。
user1329482 2015年

没有理由不写for (size_t i = 0 ...。也没有充分的理由使它难以阅读。这还将首先复制字符串,然后在其上循环。@Luke的答案在某些方面更好,除了不利用'a'字符常量。
彼得·科德斯

9
#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()的调用才会受到这种惩罚。查找语言环境通常涉及锁定。

在替换自动,使用新的非常量str.data()并添加空格以中断模板关闭(“ >>”到“>>”)后,这也适用于C ++ 98,如下所示:

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

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

因为您已经知道长度,所以不建议使用back_inserter;使用std :: string result(src.size()); std :: transform(src.begin(),src.end(),result.begin(),up_char);
维克多·塞尔

完全可以肯定,您知道这一点。
维克多·瑟尔

@Viktor Sehr,@ bayda:我知道这已经2岁了,但是为什么不两全其美呢?使用reserveback_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; }
埃文·特兰


2

尝试toupper()功能(#include <ctype.h>)。它接受字符作为参数,字符串由字符组成,因此您必须遍历每个单独的字符,这些字符组合在一起构成字符串


toupper使用负数调用时,此建议会引发未定义的行为。您应该已经提到了对的必要强制转换unsigned char
罗兰·伊利格

2

这是C ++ 11的最新代码

std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });

toupper用负数调用时,此代码将调用未定义的行为。
罗兰·伊利格

1

使用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();

1

答案@dirkgently是非常鼓舞人心的,但我想强调的是,由于这种担心是如下图所示,

像from中的所有其他函数一样,如果参数的值既不能表示为无符号字符也不等于EOF,则std :: toupper的行为是不确定的。要在纯字符(或带符号的字符)中安全使用这些函数,应首先将参数转换为无符号字符。
参考std :: toupper

的正确用法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!

0

不确定是否有内置功能。试试这个:

包括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用负数调用时,此代码将调用未定义的行为。
罗兰·伊利格

0

我的解决方案(将第6位清除为alpha):

#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用负数调用时,此代码将调用未定义的行为。
罗兰·伊利格

否...请在降低投票权之前检查您的权利。Islower仅适用于非负值...
Antonin GAVREL

-1

此页面上的所有这些解决方案都比他们需要的难。

做这个

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循环。

请记住,字符串大小也会返回分隔符,因此请在循环测试中使用<而不是<=。

输出将是:您要转换的一些字符串


4
我看不出这比boost :: toupper解决方案更简单。你能详细说明吗?
tr9sh 2012年

2
已经有很多简单的tolower循环,并且大多数使用标准循环变量名称,例如i,而不是怪异的forLoop
彼得·科德斯

-1

不使用任何库:

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

上面的代码仅适用于ASCII兼容的编码。问题(不是您的答案)都没有提到此限制。其中之一应该。
罗兰·伊利格

-1

如果只关心8位字符(除了米兰·巴布斯科夫,所有其他答案都假定为8位字符),则可以通过在编译时使用元编程生成查找表来获得最快的速度。在ideone.com上,它的运行速度比库函数快7倍,比手写版本(http://ideone.com/sb1Rup)快3倍。它也可以通过特征进行定制,而不会降低速度。

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


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

-1

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

-3

我使用这个解决方案。我知道您不应该修改该数据区...。但是我认为这主要是为了解决缓冲区溢出错误和空字符...。

void to_upper(const std::string str) {
    std::string::iterator it;
    int i;
    for ( i=0;i<str.size();++i ) {
        ((char *)(void *)str.data())[i]=toupper(((char *)str.data())[i]);
    }
}

I know you're not supposed to modify that data area-您不应该修改哪个数据区?
user93353

3
这很晚了,但是到底是什么呢?可以用str[i] = toupper(str[i]);完全正确的方法代替那条疯狂的路线(嗯,不是完全正确的方法,但是它可以修复大多数错误)。
克里斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.