簡體   English   中英

將 C++ 中的字符串轉換為大寫

[英]Convert a String In C++ To Upper Case

如何將字符串轉換為大寫。 我從谷歌搜索中找到的例子只需要處理字符。

#include <algorithm>
#include <string>

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

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

使用 C++11 和 toupper() 的簡短解決方案。

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

對於 ASCII 字符集,這個問題可以用 SIMD 向量化


加速比較:

在 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=CLANG=en_CA.UTF-8

    我沒有使用 std::string 以外的 RangeT 進行測試。 也許另一種形式的to_upper_copy優化得更好,但我認為它總是會為副本創建new / malloc空間,因此更難測試。 也許我所做的某些事情與正常用例不同,也許通常停止的 g++ 可以將區域設置內容從每個字符的循環中提升出來。 我從std::string讀取並寫入char dstbuf[4096]對測試很有意義。

  • 循環調用 glibc toupper6.67s (不過,不檢查潛在多字節 UTF-8 的int結果。這對土耳其語很重要。)

  • 僅 ASCII 循環: 8.79s (下面結果的基准版本。)顯然表查找比cmov快,無論如何表在 L1 中cmov熱。
  • 僅 ASCII 自動矢量化: 2.51s (120 個字符介於最壞情況和最好情況之間,見下文)
  • 僅 ASCII 手動矢量化: 1.35s

另請參閱有關設置區域設置時toupper()在 Windows 上運行緩慢的問題


我很震驚 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 (所以我們制作了一個副本),但它們不重疊(並且不在附近)。 兩者對齊。

  • 15 個字符的字符串:基線:1.08 秒。 autovec:1.34s
  • 16 個字符的字符串:基線:1.16s。 autovec:1.52s
  • 127 個字符的字符串:基線:8.91 秒。 autovec: 2.98s // 非向量清理需要處理 15 個字符
  • 128 個字符的字符串:基線:9.00 秒。 autovec:2.06s
  • 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.08 秒。 autovec:1.34 秒。 手動:1.29s
  • 16 個字符的字符串:基線:1.16s。 autovec:1.52s。 手動:0.335s
  • 31 字符字符串:手動:0.479s
  • 127 個字符的字符串:基線:8.91 秒。 autovec:2.98s。 手動:0.925s
  • 128 個字符的字符串:基線:9.00 秒。 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 Bonner對有關tolower()在 Windows 上運行緩慢的問題的評論

我們還可以檢查異常列表並回退到那里的標量,例如多字節 UTF8 輸入字符。

由於如此復雜,SSE4.2 PCMPISTRM或其他東西可能能夠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 庫頭文件 <ctype.h>、<wctype.h>、<string.h>、<wchar.h> 和 <stdlib.h> [...]

  • 這意味着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;
}

以下對我有用。

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

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

字符串中有 ASCII 或國際字符嗎?

如果是后一種情況,“大寫”就沒有那么簡單了,這取決於使用的字母表。 有兩院制和一院制字母表。 只有兩院制字母的大寫和小寫字符不同。 此外,還有一些復合字符,例如拉丁大寫字母 'DZ' (\DZ 'DZ'),它們使用所謂的title case 這意味着只有第一個字符 (D) 被更改。

我建議您查看ICU以及 Simple 和 Full Case Mappings 之間的區別。 這可能有幫助:

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

如果僅使用 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++ 字符串方法。

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

只要您對 ASCII-only 沒問題,並且可以提供指向 RW 內存的有效指針,C 中有一個簡單且非常有效的單行:

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

這對於像 ASCII 標識符這樣的簡單字符串尤其有用,您希望將其規范化為相同的字符大小寫。 然后您可以使用緩沖區來構造一個 std:string 實例。

//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;
}
#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());
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 );
std::string value;
for (std::string::iterator p = value.begin(); value.end() != p; ++p)
    *p = toupper(*p);

@dirkgently回答非常鼓舞人心,但我想強調的是,由於如下所示的關注,

與來自 的所有其他函數一樣,如果參數的值既不能表示為 unsigned char 也不等於 EOF,則 std::toupper 的行為是未定義的。 要安全地將這些函數與普通字符(或有符號字符)一起使用,應首先將參數轉換為無符號字符
: 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!

這是 C++11 的最新代碼

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

試試toupper()函數( #include <ctype.h> )。 它接受字符作為參數,字符串由字符組成,因此您必須迭代每個單獨的字符,當它們放在一起時構成字符串

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

錯誤:C++98 模式下不允許基於范圍的“for”循環

我的解決方案(清除 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++;
    }
}
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++(僅限 Windows)

_strupr_s(str, str.length());

C++(使用 Boost 庫)

boost::to_upper_copy(str)

基於Kyle_the_hacker 的----->回答我的額外內容。

Ubuntu

在終端中列出所有語言環境
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ë

Ubuntu Linux - 來自 VSCODE 的 WSL

Ubuntu Linux - WSL

視窗

在 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

結果

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ë

視窗

代碼 - main.cpp

此代碼僅在 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;

不確定是否有內置功能。 試試這個:

包括 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
}

不使用任何庫:

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 倍,比手寫版本 ( 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-generate-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;
}

如何將字符串轉換為大寫。 我從谷歌搜索中找到的示例僅涉及字符。

此頁面上的所有這些解決方案都比它們需要的更難。

做這個

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

記住字符串大小也會返回分隔符,因此在循環測試中使用 < 而不是 <= 。

輸出將是:您要轉換的一些字符串

我用這個解決方案。 我知道您不應該修改該數據區...。但是我認為這主要是為了解決緩沖區溢出錯誤和空字符。...大寫字母的內容不一樣。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM