文字列を大文字に変換する方法私がグーグルから見つけた例は文字だけを扱う必要があります。
ブーストストリングアルゴリズム:
#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");
#include <algorithm>
#include <string>
std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);
C++ 11とtoupper()を使用した簡単な解決策。
for (auto & c: str) c = toupper(c);
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
メンバーが標準のアルゴリズムで直接消費するのには適していないマクロである可能性が高いことを意味します。
同じ例に関するもう1つの問題は、引数をキャストしないこと、またはこれが負でないことを確認しないことです。これは普通のchar
が署名されているシステムにとっては特に危険です。 (理由は、これがマクロとして実装されている場合、おそらくルックアップテーブルを使用し、引数をそのテーブルにインデックス付けすることです。負のインデックスはUBを返します。)
文字列にASCIIまたは国際文字がありますか。
後者の場合、「大文字」はそれほど単純ではなく、使用されるアルファベットによって異なります。二院制と一院制のアルファベットがあります。大文字と小文字で異なる文字を持つのは、2文字のアルファベットだけです。また、ラテン大文字の 'DZ'(\ u01F1 'DZ')のように、いわゆるタイトルケースを使用する複合文字もあります。これは、最初の文字(D)だけが変更されることを意味します。
ICU 、および単純マッピングとフルケースマッピングの違いを検討することをお勧めします。これは役に立つかもしれません:
この問題は、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.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 ++は文字ごとのループからロケール設定のものを持ち上げることができる。私のstd::string
からの読み取りとchar dstbuf[4096]
への書き込みのループはテストには意味があります。
glibc toupper
:6.67sを呼び出すループ(ただし、潜在的なマルチバイトUTF-8についてint
の結果をチェックしないでください。これはトルコ語に関係します。)
cmov
よりも高速です。ロケールが設定されているときにWindowsでtoupper()
が遅くなることについてのこの質問も参照してください 。
Boostが他のオプションよりも桁違いに遅いことにショックを受けました。私は-O3
が有効になっていることを再確認し、asmをシングルステップで実行してそれが何をしているのかを確認しました。 clang ++ 3.8とほぼ同じ速度です。それは文字ごとのループの内側に大きなオーバーヘッドがあります。 report
perfイベントのperf record
/cycles
の結果は次のとおりです。
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には、一度に1バイトずつループするよりもはるかに高速な効率的なstrlen
があるため、個別のベクトル化されたstrlenループとtoupperループは高速です。
ベースライン:終了する0をその場でチェックするループ。
Core2(Merom)2.4GHzでの40M繰り返しの時間。 gcc 5.2 -O3 -march=native
。 (Ubuntu 15.10) dst != src
(それでコピーを作成します)しかし、それらは重なりません(そして近くにありません)。両方とも整列しています。
一部の結果はclangとは少し異なります。
関数を呼び出すマイクロベンチマークループは別のファイルにあります。そうでなければ、インライン展開され、strlen()
がループから抜け出し、劇的に速く実行されます。 16文字の文字列(0.187秒)の場合.
これは、gccがあらゆるアーキテクチャに対して自動ベクトル化できるという大きな利点がありますが、小さな文字列の一般的なケースでは遅いという大きな欠点があります。
それで大きなスピードアップがあります、しかし、コンパイラ自動ベクトル化は素晴らしいコードを作りません、特に。最後の最大15文字のクリーンアップ用。
My case-flip関数 に基づいて、すべてのアルファベット文字の大文字と小文字を反転させます。これは "unsigned compare trick"を利用しています。ここでは、範囲シフトによる単一の符号なし比較でlow < a && a <= high
を実行できるため、low
より小さい値はhigh
より大きい値に折り返されます。 (low
とhigh
がそれほど離れていない場合、これは機能します。)
SSEには符号付き比較のみが含まれていますが、符号付き範囲の末尾に範囲をシフトして「符号なしの比較」トリックを使用することもできます。 +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
}
1つのベクトルに対して機能するこの関数があれば、文字列全体を処理するためにループ内で呼び出すことができます。既に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;
}
Core2(Merom)2.4GHzでの40M繰り返しの時間。 gcc 5.2 -O3 -march=native
。 (Ubuntu 15.10) dst != src
(それでコピーを作成します)しかし、それらは重なりません(そして近くにありません)。両方とも整列しています。
(実際にはループ内の_mm_store
ではなく、アドレスが揃っていてもstoreuがMerom上で遅くなるので、_mm_storeu
とタイミングを合わせています。Nehalem以降では問題ありません。失敗を修正する代わりに、コードを現状のまま残しました。場合によっては、末尾の0をコピーします。すべての時間を再設定したくないためです。)
そのため、16Bより長い短い文字列の場合、これは自動ベクトル化よりもはるかに高速です。長さがベクトル幅より1小さい場合、問題はありません。店での転送が失速しているために、それらがその場で動作しているときに問題になる可能性があります。 (ただし、toupperはべき等なので、元の入力ではなく、独自の出力を処理しても問題ありません)。
周囲のコードが何を望んでいるか、そしてターゲットとなるマイクロアーキテクチャに応じて、さまざまなユースケースに合わせてこれを調整する余地がたくさんあります。クリーンアップ部分に対してNiceコードをコンパイラーに発行させるのは難しいです。 ffs(3)
(x86ではbsfまたはtzcntにコンパイルします)を使用するのは良いようですが、この答えの大部分を書いた後にバグに気付いたので(FIXMEのコメントを見てください).
movq
またはmovd
ロード/ストアを使用すると、さらに小さい文字列でもベクトルの高速化を実現できます。ユースケースに合わせてカスタマイズしてください
上位ビットが設定されたバイトがベクターに含まれている場合は検出できます。その場合は、そのベクターのスカラutf-8対応ループにフォールバックします。 dst
ポインターはsrc
ポインターとは異なる量だけ進むことができますが、いったん整列されたsrc
ポインターに戻っても、まだ整列されていないベクトルストアをdst
に行うだけです。
UTF-8であるが、大部分がUTF-8のASCIIサブセットで構成されているテキストの場合、これは良いことです。一般的なケースでは高性能、すべてのケースで正しい動作。 ASCII以外のものがたくさんあるときは、常にスカラーUTF-8対応ループに留まるよりもおそらく悪いでしょう。
他の言語を犠牲にして英語を早くすることは、欠点が大きい場合には将来を見越した決断ではありません。
トルコ語ロケール(tr_TR
)では、toupper('i')
の正しい結果は'İ'
(プレーンASCII)ではなく'I'
(U0130)です。 Windowsでtolower()
が遅いという質問については、 Martin Bonnerのコメント を参照してください。
マルチバイトのUTF-8入力文字の場合のように、例外リストをチェックし、そこでスカラーにフォールバックすることもできます。
これほど複雑なので、SSE4.2 PCMPISTRM
などで、一度に多くのチェックを実行できる可能性があります。
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;
}
ASCII文字のみを使用した場合の速い方:
for(i=0;str[i]!=0;i++)
if(str[i]<='z' && str[i]>='a')
str[i]-=32;
このコードは高速に実行されますが、ASCIIでのみ機能し、「抽象的」な解決策ではありません。
あなたがUNICODEの解決策あるいはもっと慣習的で抽象的な解決策を必要とするならば、他の答えを求めてC++文字列の方法で働きなさい。
以下は私のために働きます。
#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::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);
あなたがASCIIのみで大丈夫で、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が呼び出しごとにロケールを調べる必要があるためです。これは、呼び出しごとに別のスレッドによって変更されている可能性があるからです。そしてロケールを調べるのは、一般的にロックをかけることです。
これは、autoを置き換え、新しい非const 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);
これがC++ 11の最新のコードです。
std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });
toupper()
関数(#include <ctype.h>
)を試してください。それは引数として文字を受け入れます、文字列は文字で構成されているので、一緒になったときに文字列を構成する個々の文字ごとに繰り返す必要があります。
私の解決策(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++;
}
}
組み込み関数があるかどうかわからない。これを試して:
Ctype.h OR 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
}