909

std::stringaを小文字に変換したい。機能は承知しておりますtolower()std::stringただし、過去にこの関数に問題がありましたが、各文字を反復処理する必要があるため、とにかく理想的ではありません。

100% の確率で機能する代替手段はありますか?

4

27 に答える 27

1036

あまりよくない質問からの適応:

#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 のようなマルチバイト エンコーディングを使用している場合、多くのスクリプトには適していません。

于 2008-11-24T11:59:33.050 に答える
363

Boost は、このための文字列アルゴリズムを提供します

#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);
于 2008-11-24T11:57:21.557 に答える
296

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::u16stringandの方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>ベースのソリューションではそれを実現できません。

于 2014-06-05T15:06:39.723 に答える
38

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);
}
于 2013-10-09T08:00:34.853 に答える
33

文字列に 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を参照してください。

于 2012-10-10T07:24:12.917 に答える
28

参照変数を使用した範囲ベースの for ループを使用する別のアプローチ

string test = "Hello World";
for(auto& c : test)
{
   c = tolower(c);
}

cout<<test<<endl;
于 2017-01-10T19:53:45.383 に答える
22

これは 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;
}
于 2013-03-28T06:25:54.357 に答える
4

Boost の代替は POCO (pocoproject.org) です。

POCO には 2 つのバリアントがあります。

  1. 最初のバリアントは、元の文字列を変更せずにコピーを作成します。
  2. 2 番目のバリアントは、元の文字列をその場で変更します。
    「In Place」バージョンの名前には常に「InPlace」が含まれます。

両方のバージョンを以下に示します。

#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);
于 2013-09-18T20:20:37.243 に答える
4

大文字/小文字を実行する独自のテンプレート関数。

#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;
}
于 2019-05-18T14:40:40.290 に答える
3

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 に変換するために使用されるアプローチと同じであると認識する人もいます。

于 2014-01-08T17:48:19.410 に答える
2

100% の確率で機能する代替手段はありますか?

いいえ

小文字化の方法を選択する前に、自問する必要のある質問がいくつかあります。

  1. 文字列はどのようにエンコードされますか? プレーンASCII?UTF-8? 拡張 ASCII レガシー エンコーディングの何らかの形式ですか?
  2. とにかく小文字とはどういう意味ですか?ケース マッピング ルールは言語によって異なります。ユーザーのロケールにローカライズされたものが必要ですか? ソフトウェアが実行されるすべてのシステムで一貫して動作するものが必要ですか? ASCII 文字を小文字にして、他のすべてを通過させたいだけですか?
  3. どのようなライブラリが利用できますか?

これらの質問に対する答えが得られたら、ニーズに合ったソリューションを探し始めることができます。どこにいても誰にでも合う万能サイズはありません。

于 2019-01-28T21:31:21.767 に答える
2

簡単なものが必要な場合のマクロテクニックは次のとおりです。

#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文字だけではないものに取り組んでいる場合、依然として重要な考慮事項であることに注意してください。

于 2016-01-30T21:02:24.137 に答える
2

strlwrMicrosoft プラットフォームでは、一連の関数を使用できます: 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 );
}
于 2014-08-29T17:18:02.363 に答える