std::string
小文字に変換したい。この機能は知っtolower()
ていますが、以前はこの機能に問題std::string
がありました。いずれにしても、を使用して各文字を反復処理する必要があるため、これは理想的ではありません。
100%動作する代替手段はありますか?
std::string
小文字に変換したい。この機能は知っtolower()
ていますが、以前はこの機能に問題std::string
がありました。いずれにしても、を使用して各文字を反復処理する必要があるため、これは理想的ではありません。
100%動作する代替手段はありますか?
回答:
それほどよくない質問からの改作:
#include <algorithm>
#include <cctype>
#include <string>
std::string data = "Abc";
std::transform(data.begin(), data.end(), data.begin(),
[](unsigned char c){ return std::tolower(c); });
あなたは本当に各キャラクターを反復せずに逃げるつもりはありません。それ以外の場合、文字が小文字か大文字かを知る方法はありません。
あなたが本当に嫌いならtolower()
、これは私があなたに使うことをお勧めしない特別なASCIIのみの代替です:
char asciitolower(char in) {
if (in <= 'Z' && in >= 'A')
return in - ('Z' - 'z');
return in;
}
std::transform(data.begin(), data.end(), data.begin(), asciitolower);
tolower()
では、1バイト文字ごとの置換しかできないことに注意してください。これは、特にUTF-8のようなマルチバイトエンコーディングを使用している場合、多くのスクリプトには不適切です。
char
れ::tolower(int)
ます)。負の値を渡さないようにする必要があります。
::tolower
クラッシュする可能性があります。非ASCII入力のUBです。
#include <boost/algorithm/string.hpp>
std::string str = "HELLO, WORLD!";
boost::algorithm::to_lower(str); // modifies str
#include <boost/algorithm/string.hpp>
const std::string str = "HELLO, WORLD!";
const std::string lower_str = boost::algorithm::to_lower_copy(str);
to_lower_copy
tl; dr
ICUライブラリを使用します。そうしないと、変換ルーチンは、存在していることにさえ気付いていない可能性のあるケースを黙って中断します。
最初にあなたは質問に答える必要があります:あなたのエンコーディングは何std::string
ですか?ISO-8859-1ですか?それともISO-8859-8ですか?またはWindowsコードページ1252?あなたが大文字から小文字に変換するために使用しているものは何でもそれを知っていますか?(またはそれは文字以上の惨めに失敗し0x7f
ますか?)
std::string
コンテナーとしてUTF-8(8ビットエンコーディングの中で唯一の健全な選択)を使用している場合、マルチバイト文字シーケンスをコンテナーに格納しているため、自分がまだ制御していると信じ込んでいます。これはマルチバイトの概念を認識していません。.substr()
時限爆弾のように単純なものでさえ。(マルチバイトシーケンスを分割すると、無効な(サブ)文字列になるためです。)
そしてstd::toupper( 'ß' )
、どのようなエンコーディングでも、のようなものを試すとすぐに、深刻な問題に直面します。(標準ライブラリではこれを「正しく」行うことは単純に不可能であり、1つの結果文字しか配信できず、"SS"
ここでは必要ありません。)[1]別の例は、ロケールに応じてstd::tolower( 'I' )
異なる結果を生成するはずです。ドイツで'i'
は正しいでしょう。トルコでは、'ı'
(ラテン小文字のドットI)が期待される結果です(これも、UTF-8エンコーディングでは1バイト以上です)。さらに別の例は、ギリシャ語であるシグマ大文字、'∑'
小文字、'σ'
それは言葉の最後を除いて... 'ς'
。
だから、任意の、一度に文字の上に働く、あるいは最悪の場合変換バイトを一度には、設計によって破壊されます。
次に、標準ライブラリが実行できることは、ソフトウェアが実行されているマシンでサポートされているロケールに依存しているという点があります...そうでない場合はどうしますか?
それでは、あなたがしている本当にを探しているすべて正しくこれに対処することのできる文字列クラスで、それはありませんいずれかのstd::basic_string<>
変種。
(C ++ 11の注:std::u16string
およびstd::u32string
はより良いですが、まだ完全ではありません。C++ 20はをもたらしましたstd::u8string
が、これらはすべてエンコードを指定することです。他の多くの点で、正規化、照合などのUnicodeの仕組みをまだ知らないままです。 。)
Boost は見栄えがよく、API的にも優れていますが、Boost.Localeは基本的にICUのラッパーです。場合はブーストがされてコンパイルさ ICUサポートとそうでない場合は...、Boost.Localeは標準ライブラリ用にコンパイルされたロケールのサポートに限定されています。
そして、私を信じてきブーストは、ICUでコンパイルすることは時々本当の痛みをすることができます。(Windows用の事前にコンパイルされたバイナリはないため、アプリケーションと一緒にそれらを提供する必要があり、それによってまったく新しいワームの缶が開きます...)
したがって、個人的には、完全なUnicodeサポートを馬の口から直接取得し、ICUライブラリを直接使用することをお勧めします。
#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>
#include <iostream>
int main()
{
/* "Odysseus" */
char const * someString = u8"ΟΔΥΣΣΕΥΣ";
icu::UnicodeString someUString( someString, "UTF-8" );
// Setting the locale explicitly here for completeness.
// Usually you would use the user-specified system locale,
// which *does* make a difference (see ı vs. i above).
std::cout << someUString.toLower( "el_GR" ) << "\n";
std::cout << someUString.toUpper( "el_GR" ) << "\n";
return 0;
}
コンパイル(この例ではG ++を使用):
g++ -Wall example.cpp -licuuc -licuio
これは与える:
ὀδυσσεύς
単語の途中でΣ<->σ変換し、単語の最後でΣ<->ς変換することに注意してください。<algorithm>
ベースのソリューションはそれを与えることができません。
[1] 2017年、ドイツ語表記法審議会は、パスポートなどのあいまいさを回避するための従来の「SS」変換の横のオプションとして、「U」U + 1E9Eラテン大文字Sを正式に使用できると決定しました(名前が大文字の場合) )。私の美しい頼りになる例は、委員会の決定によって廃止されました...
toupper
そしてtolower
、まだ単一の文字に取り組んでいます。文字列クラスには正規化の概念がまだありません(たとえば、「ü」が「uを分音符号付き」または「u +結合分音符号」としてエンコードされているかどうか)、または文字列が分離されている場合とされていない場合。リストは続く。u8stringは(他の標準文字列クラスと同様に)「パススルー」に適しています。ただし、Unicode を処理する場合は、 ICU が必要です。
C ++ 11の範囲ベースのforループを使用すると、より単純なコードは次のようになります。
#include <iostream> // std::cout
#include <string> // std::string
#include <locale> // std::locale, std::tolower
int main ()
{
std::locale loc;
std::string str="Test String.\n";
for(auto elem : str)
std::cout << std::tolower(elem,loc);
}
文字列にASCII範囲外のUTF-8文字が含まれている場合、boost :: algorithm :: to_lowerはそれらを変換しません。UTF-8が関係する場合は、boost :: locale :: to_lowerを使用する方がよいでしょう。http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.htmlを参照してください
これは、Stefan Maiの応答のフォローアップです。変換の結果を別の文字列に配置する場合は、を呼び出す前に、ストレージスペースを事前に割り当てる必要がありますstd::transform
。STLは変換された文字を宛先のイテレーターに格納するため(ループの各反復で文字をインクリメントします)、宛先の文字列は自動的にサイズ変更されず、メモリを踏み外す危険があります。
#include <string>
#include <algorithm>
#include <iostream>
int main (int argc, char* argv[])
{
std::string sourceString = "Abc";
std::string destinationString;
// Allocate the destination space
destinationString.resize(sourceString.size());
// Convert the source string to lower case
// storing the result in destination string
std::transform(sourceString.begin(),
sourceString.end(),
destinationString.begin(),
::tolower);
// Output the result of the conversion
std::cout << sourceString
<< " -> "
<< destinationString
<< std::endl;
}
私が見る限り、Boostライブラリはパフォーマンスの面で本当に悪いです。私はそれらのunordered_mapをSTLにテストしましたが、平均で3倍遅くなりました(最良のケース2、最悪の場合は10倍)。また、このアルゴリズムは低すぎるように見えます。
違いが非常に大きいのでtolower
、「必要に応じて」ブーストと同等にするために追加する必要がある追加は、ブーストよりもはるかに高速になると確信しています。
私はAmazon EC2でこれらのテストを行ったため、テスト中にパフォーマンスが変化しましたが、まだアイデアは分かります。
./test
Elapsed time: 12365milliseconds
Elapsed time: 1640milliseconds
./test
Elapsed time: 26978milliseconds
Elapsed time: 1646milliseconds
./test
Elapsed time: 6957milliseconds
Elapsed time: 1634milliseconds
./test
Elapsed time: 23177milliseconds
Elapsed time: 2421milliseconds
./test
Elapsed time: 17342milliseconds
Elapsed time: 14132milliseconds
./test
Elapsed time: 7355milliseconds
Elapsed time: 1645milliseconds
-O2
このようにしました:
./test
Elapsed time: 3769milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3815milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3643milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 22018milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 3845milliseconds
Elapsed time: 569milliseconds
ソース:
string str;
bench.start();
for(long long i=0;i<1000000;i++)
{
str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
boost::algorithm::to_lower(str);
}
bench.end();
bench.start();
for(long long i=0;i<1000000;i++)
{
str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
for(unsigned short loop=0;loop < str.size();loop++)
{
str[loop]=tolower(str[loop]);
}
}
bench.end();
私は専用のマシンでテストする必要があると思いますが、私はこのEC2を使用するので、自分のマシンでテストする必要はありません。
std名前空間を気にせずに文字列を小文字に変換する最も簡単な方法は次のとおりです
1:スペース付き/なしの文字列
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
string str;
getline(cin,str);
//------------function to convert string into lowercase---------------
transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
cout<<str;
return 0;
}
2:スペースなしの文字列
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
string str;
cin>>str;
//------------function to convert string into lowercase---------------
transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
cout<<str;
return 0;
}
std::ctype::tolower()
標準のC ++ローカリゼーションライブラリから、これを正しく行います。以下は、tolowerのリファレンスページから抽出した例です。
#include <locale>
#include <iostream>
int main () {
std::locale::global(std::locale("en_US.utf8"));
std::wcout.imbue(std::locale());
std::wcout << "In US English UTF-8 locale:\n";
auto& f = std::use_facet<std::ctype<wchar_t>>(std::locale());
std::wstring str = L"HELLo, wORLD!";
std::wcout << "Lowercase form of the string '" << str << "' is ";
f.tolower(&str[0], &str[0] + str.size());
std::wcout << "'" << str << "'\n";
}
const
か?f.tolower()
新しい文字列に文字を入力する必要があるため、少し面倒になります(たとえば、を使用できないように見えます)。あなたが使用するtransform()
と、のようなものstd::bind1st( std::mem_fun() )
オペレータのため?
tolower
with locale
パラメータでは、への暗黙的な呼び出しがuse_facet
パフォーマンスのボトルネックになっているように見えるため、特にそうです。私の同僚の1人は、boost::iequals
(この問題がある)use_facet
ループの外で一度だけ呼び出されるバージョンに置き換えることで、数100%の速度向上を達成しました。
Boostの代替はPOCO(pocoproject.org)です。
POCOには2つのバリアントがあります。
両方のバージョンを以下に示します。
#include "Poco/String.h"
using namespace Poco;
std::string hello("Stack Overflow!");
// Copies "STACK OVERFLOW!" into 'newString' without altering 'hello.'
std::string newString(toUpper(hello));
// Changes newString in-place to read "stack overflow!"
toLowerInPlace(newString);
ifテストを行わずに大文字を小文字に変換する方法があり、それは非常に簡単です。isupper()関数/マクロでのclocale.hの使用により、位置に関連する問題に対処する必要がありますが、そうでない場合は、いつでもUtoL []を思いのままに調整できます。
Cの文字が実際には8ビット整数(現時点ではワイド文字セットを無視)であるとすると、代替文字セットを保持する256バイトの配列を作成でき、変換関数では、文字列内の文字を添え字として変換配列。
ただし、1対1のマッピングの代わりに、大文字の配列メンバーに小文字のBYTE int値を指定します。ここではislower()とisupper()が便利です。
コードは次のようになります...
#include <clocale>
static char UtoL[256];
// ----------------------------------------------------------------------------
void InitUtoLMap() {
for (int i = 0; i < sizeof(UtoL); i++) {
if (isupper(i)) {
UtoL[i] = (char)(i + 32);
} else {
UtoL[i] = i;
}
}
}
// ----------------------------------------------------------------------------
char *LowerStr(char *szMyStr) {
char *p = szMyStr;
// do conversion in-place so as not to require a destination buffer
while (*p) { // szMyStr must be null-terminated
*p = UtoL[*p];
p++;
}
return szMyStr;
}
// ----------------------------------------------------------------------------
int main() {
time_t start;
char *Lowered, Upper[128];
InitUtoLMap();
strcpy(Upper, "Every GOOD boy does FINE!");
Lowered = LowerStr(Upper);
return 0;
}
この方法では、同時に、変更したい他の文字を再マッピングすることができます。
最新のプロセッサで実行する場合、このアプローチには大きな利点が1つあります。分岐を構成するifテストがないため、分岐予測を行う必要はありません。これにより、他のループのCPUの分岐予測ロジックが節約され、パイプラインのストールを防ぐ傾向があります。
ここで、EBCDICをASCIIに変換するために使用されたものと同じアプローチを認識する人もいます。
答えのどれもが、C ++ 20以降標準ライブラリで提供され、今後の範囲のライブラリーを、言及していない、そして現在は個別に利用できるので、GitHubの上としてrange-v3
、私はそれを使用して、この変換を実行する方法を追加したいと思います。
文字列をインプレースで変更するには:
str |= action::transform([](unsigned char c){ return std::tolower(c); });
新しい文字列を生成するには:
auto new_string = original_string
| view::transform([](unsigned char c){ return std::tolower(c); });
(#include <cctype>
必要なRangesヘッダーとを忘れないでください。)
注:unsigned char
ラムダの引数としてのの使用は、cppreferenceに触発されており、次のように述べています。
の他のすべての関数と同様に、引数の値がとしても表現可能でも等しくない場合のの
<cctype>
動作std::tolower
は未定義です。これらの関数をプレーンs(またはs)で安全に使用するには、最初に引数を次のように変換する必要があります。unsigned char
EOF
char
signed char
unsigned char
char my_tolower(char ch) { return static_cast<char>(std::tolower(static_cast<unsigned char>(ch))); }
同様に、イテレータの値タイプが
char
またはの場合、標準アルゴリズムで直接使用しないでくださいsigned char
。代わりに、値をunsigned char
最初に変換します。std::string str_tolower(std::string s) { std::transform(s.begin(), s.end(), s.begin(), // static_cast<int(*)(int)>(std::tolower) // wrong // [](int c){ return std::tolower(c); } // wrong // [](char c){ return std::tolower(c); } // wrong [](unsigned char c){ return std::tolower(c); } // correct ); return s; }
大文字/小文字を実行する私自身のテンプレート関数。
#include <string>
#include <algorithm>
//
// Lowercases string
//
template <typename T>
std::basic_string<T> lowercase(const std::basic_string<T>& s)
{
std::basic_string<T> s2 = s;
std::transform(s2.begin(), s2.end(), s2.begin(), tolower);
return std::move(s2);
}
//
// Uppercases string
//
template <typename T>
std::basic_string<T> uppercase(const std::basic_string<T>& s)
{
std::basic_string<T> s2 = s;
std::transform(s2.begin(), s2.end(), s2.begin(), toupper);
return std::move(s2);
}
towlower
はUTF-16をサポートするワイド文字にforを使用しました。
簡単なものが必要な場合のマクロテクニックを次に示します。
#define STRTOLOWER(x) std::transform (x.begin(), x.end(), x.begin(), ::tolower)
#define STRTOUPPER(x) std::transform (x.begin(), x.end(), x.begin(), ::toupper)
#define STRTOUCFIRST(x) std::transform (x.begin(), x.begin()+1, x.begin(), ::toupper); std::transform (x.begin()+1, x.end(), x.begin()+1,::tolower)
ただし、この回答に関する@AndreasSpindlerのコメントは、ASCII文字だけではないものに取り組んでいる場合は、重要な考慮事項であることに注意してください。
void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
x
有効な式である可能性があります。これはたまたま正しくコンパイルされますが、マクロのために完全に偽の結果をもたらします。
// tolower example (C++)
#include <iostream> // std::cout
#include <string> // std::string
#include <locale> // std::locale, std::tolower
int main ()
{
std::locale loc;
std::string str="Test String.\n";
for (std::string::size_type i=0; i<str.length(); ++i)
std::cout << std::tolower(str[i],loc);
return 0;
}
100%動作する代替手段はありますか?
番号
小文字の方法を選択する前に、自問する必要があるいくつかの質問があります。
これらの質問に対する回答が得られたら、ニーズに合ったソリューションを探し始めることができます。あらゆる場所で機能するすべてに適合する1つのサイズはありません!
Microsoftプラットフォームstrlwr
では、関数ファミリーを使用できます。http ://msdn.microsoft.com/en-us/library/hkxwh33z.aspx
// crt_strlwr.c
// compile with: /W3
// This program uses _strlwr and _strupr to create
// uppercase and lowercase copies of a mixed-case string.
#include <string.h>
#include <stdio.h>
int main( void )
{
char string[100] = "The String to End All Strings!";
char * copy1 = _strdup( string ); // make two copies
char * copy2 = _strdup( string );
_strlwr( copy1 ); // C4996
_strupr( copy2 ); // C4996
printf( "Mixed: %s\n", string );
printf( "Lower: %s\n", copy1 );
printf( "Upper: %s\n", copy2 );
free( copy1 );
free( copy2 );
}
fplus :: to_lower_case()を使用します。
(fplus:https : //github.com/Dobiasd/FunctionalPlus。
http://www.editgym.com/fplus-api-search/で「to_lower_case」を検索します)
fplus::to_lower_case(std::string("ABC")) == std::string("abc");
回答を改善することは許可されていなかったため、コピーしてください。ありがとうSO
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
説明:
for(auto& c : test)
されているループの範囲はベース種類の:
for (
range_declaration
:
range_expression
)
loop_statement
range_declaration
:auto& c
ここでは、自動指定子は自動型推論に使用されます。したがって、型は変数初期化子から差し引かれます。
range_expression
:test
この場合の範囲はstringの文字test
です。
文字列の文字test
は、for loop through identifier内の参照として使用できますc
。
C ++には、tolowerまたはtoupperメソッドをstringに実装する必要はありませんが、charには使用できます。文字列の各文字を簡単に読み取って、必要なケースに変換し、文字列に戻すことができます。サードパーティのライブラリを使用しないサンプルコード:
#include<iostream>
int main(){
std::string str = std::string("How IS The Josh");
for(char &ch : str){
ch = std::tolower(ch);
}
std::cout<<str<<std::endl;
return 0;
}
文字列の文字ベースの操作の場合:文字列のすべての文字に対して
これは、大文字を小文字に、またはその逆に変換する別の単純なバージョンである可能性があります。VS2017コミュニティバージョンを使用して、このソースコードをコンパイルしました。
#include <iostream>
#include <string>
using namespace std;
int main()
{
std::string _input = "lowercasetouppercase";
#if 0
// My idea is to use the ascii value to convert
char upperA = 'A';
char lowerA = 'a';
cout << (int)upperA << endl; // ASCII value of 'A' -> 65
cout << (int)lowerA << endl; // ASCII value of 'a' -> 97
// 97-65 = 32; // Difference of ASCII value of upper and lower a
#endif // 0
cout << "Input String = " << _input.c_str() << endl;
for (int i = 0; i < _input.length(); ++i)
{
_input[i] -= 32; // To convert lower to upper
#if 0
_input[i] += 32; // To convert upper to lower
#endif // 0
}
cout << "Output String = " << _input.c_str() << endl;
return 0;
}
注:特殊文字がある場合は、条件チェックを使用して処理する必要があります。
私はstd :: transformを試しました、200年前のドルイドだけが理解できるabominable stl cripticコンパイルエラーです(から変換できませんflibidi flabidi fluに変換できません)
これは問題なく機能し、簡単に調整できます
string LowerCase(string s)
{
int dif='a'-'A';
for(int i=0;i<s.length();i++)
{
if((s[i]>='A')&&(s[i]<='Z'))
s[i]+=dif;
}
return s;
}
string UpperCase(string s)
{
int dif='a'-'A';
for(int i=0;i<s.length();i++)
{
if((s[i]>='a')&&(s[i]<='z'))
s[i]-=dif;
}
return s;
}