std::string
aを小文字に変換したい。機能は承知しておりますtolower()
。std::string
ただし、過去にこの関数に問題がありましたが、各文字を反復処理する必要があるため、とにかく理想的ではありません。
100% の確率で機能する代替手段はありますか?
std::string
aを小文字に変換したい。機能は承知しております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);
は 1 バイト文字ごとの置換しかできないことに注意してくださいtolower()
。これは、特に UTF-8 のようなマルチバイト エンコーディングを使用している場合、多くのスクリプトには適していません。
#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);
tl;dr
ICU ライブラリを使用します。そうしないと、変換ルーチンは、おそらく存在していることに気付いていない場合でも、黙って中断します。
最初に質問に答える必要があります: あなたの のエンコーディングは何std::string
ですか? ISO-8859-1ですか?それとも ISO-8859-8 でしょうか? または Windows コードページ 1252? 大文字から小文字に変換するために使用しているものは何でもそれを知っていますか? (または文字数オーバーで惨めに失敗する0x7f
か?)
コンテナーとしてUTF-8 ( 8 ビット エンコーディングの中で唯一の適切な選択) を使用しstd::string
ている場合、自分がまだ物事を制御していると信じているのであれば、すでに自分自身を欺いていることになります。マルチバイトの概念を認識していないコンテナにマルチバイト文字シーケンスを格納しています。また、コンテナに対して実行できる操作のほとんども認識していません! マルチバイトシーケンスの途中で分割するため、単純なものでも.substr()
無効な (サブ) 文字列になる可能性があります。
std::toupper( 'ß' )
、または任意のエンコーディングで何かを試すとすぐに、問題が発生しstd::tolower( 'Σ' )
ます。1)、標準は一度に 1 つの文字に対してのみ動作するため、正しいように変換することはできません。そして 2)、標準は一度に 1 文字しか処理しないため、が単語の途中にあるのか (どこが正しいのか)、それとも最後にあるのか ( ) を判断できません。もう 1 つの例は、ロケールに応じて異なる結果を生成するはずの です。ほぼすべての場所で予想されますが、トルコでは(LATIN SMALL LETTER DOTLESS I) が正しい答えです (これも UTF-8 では 1 バイト以上です)。エンコーディング)。ß
SS
Σ
σ
ς
std::tolower( 'I' )
i
ı
そのため、一度に 1 文字、またはさらに悪いことに一度に 1バイトで機能する大文字と小文字の変換は、意図的に壊れています。std::
これには、現時点で存在するすべての亜種が含まれます。
次に、標準ライブラリができることは、ソフトウェアが実行されているマシンでサポートされているロケールに依存しているという点があります...そして、ターゲットロケールがサポートされていない場合はどうしますかクライアントのマシンで?
したがって、あなたが本当に探しているのは、これらすべてを正しく処理できる文字列クラスであり、それはどのバリアントでもありませんstd::basic_string<>
。
(C++11 の注: std::u16string
andの方std::u32string
が優れていますが、まだ完全ではありません。C++20std::u8string
では が導入されましたが、これらはすべてエンコーディングを指定するだけです。他の多くの点では、正規化、照合などの Unicode の仕組みをまだ知らないままです。.. .)
Boostは良さそうに見えますが、API に関しては、Boost.Locale は基本的にICUのラッパーです。Boost がICU サポート付きでコンパイルされている場合...そうでない場合、Boost.Locale は標準ライブラリ用にコンパイルされたロケール サポートに限定されます。
Boost を ICU でコンパイルするのは、時には本当に大変なことです。(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>
ベースのソリューションではそれを実現できません。
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を参照してください。
参照変数を使用した範囲ベースの for ループを使用する別のアプローチ
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
cout<<test<<endl;
これは 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 の代替は 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);
大文字/小文字を実行する独自のテンプレート関数。
#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 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 s2;
}
if tests を実行せずに大文字を小文字に変換する方法があり、非常に簡単です。isupper() 関数/マクロの clocale.h の使用は、場所に関連する問題を処理する必要がありますが、そうでない場合は、いつでも UtoL[] を心ゆくまで微調整できます。
C の文字が実際には単なる 8 ビットの int であることを考えると (現時点ではワイド文字セットを無視します)、別の文字セットを保持する 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 に変換するために使用されるアプローチと同じであると認識する人もいます。
100% の確率で機能する代替手段はありますか?
いいえ
小文字化の方法を選択する前に、自問する必要のある質問がいくつかあります。
これらの質問に対する答えが得られたら、ニーズに合ったソリューションを探し始めることができます。どこにいても誰にでも合う万能サイズはありません。
簡単なものが必要な場合のマクロテクニックは次のとおりです。
#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文字だけではないものに取り組んでいる場合、依然として重要な考慮事項であることに注意してください。
strlwr
Microsoft プラットフォームでは、一連の関数を使用できます: 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 );
}