web-dev-qa-db-ja.com

Std :: stringを小文字に変換する方法

std::stringを小文字に変換したいのですが。私は関数tolower()を知っていますが、過去にこの関数に問題があり、std::stringを使用すると各文字を繰り返す必要があるため、とにかく理想的ではありませんでした。

100%の時間で動作する代替手段はありますか?

681
Konrad

this から:

#include <algorithm>
#include <string> 

std::string data = "Abc"; 
std::transform(data.begin(), data.end(), data.begin(), ::tolower);

あなたは実際に各キャラクターを反復処理するのをやめようとはしていません。それ以外の場合、文字が小文字か大文字かを知る方法はありません。

あなたが本当にtolower()を嫌っているのなら、ここに私はあなたが使うことをお勧めしない、移植性のない代替手段があります:

char easytolower(char in) {
  if(in <= 'Z' && in >= 'A')
    return in - ('Z' - 'z');
  return in;
}

std::transform(data.begin(), data.end(), data.begin(), easytolower);

特にUTF-8のようなマルチバイトエンコーディングを使用している場合、::tolower()は1バイト文字ごとの置換しかできないことに注意してください。

823
Stefan Mai

これには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);
293
Rob

tl; dr

ICUライブラリを使用


まず、質問に答える必要があります:std::stringencodingとは何ですか? ISO-8859-1ですか?または、おそらくISO-8859-8ですか?またはWindowsコードページ1252?大文字から小文字への変換に使用しているものは何でも知っていますか?(または0x7fを超える文字に対して悲惨に失敗しますか?)

std::stringをコンテナとしてUTF-8(8ビットエンコーディングの中で唯一の正しい選択)を使用している場合、マルチバイトを格納しているので、あなたはまだ物事を制御していると信じてしまいます。マルチバイトの概念を認識していないコンテナ内の文字シーケンス。 .substr()のような単純なものでさえ、時を刻む時限爆弾です。 (マルチバイトシーケンスを分割すると、無効な(サブ)文字列が生成されるため。)

anyエンコーディングでstd::toupper( 'ß' )のようなものを試すとすぐに、あなたは深刻な問題に直面します。 (標準ライブラリではこれを「正しく」実行することはできないため、ここで必要な"SS"ではなく、one結果文字のみを配信できます。) [1]別の例はstd::tolower( 'I' )で、ロケールに応じて異なる結果を生成します。ドイツでは、'i'が正しいでしょう。トルコでは、'ı'(LATIN SMALL LETTER DOTLESS I)が期待される結果です(これもUTF-8エンコーディングでは1バイト以上です)。

次に、標準ライブラリは、ソフトウェアが実行されているマシンのロケールがsupportedに依存しているという点があります。そうでない場合はどうしますか。 t?

reallyを探しているのは、このすべてを正しく処理できる文字列クラスであり、それはnotstd::basic_string<>バリアントのいずれか

(C++ 11注:std::u16stringstd::u32stringbetterですが、完全ではありません。C++ 20はstd::u8stringをもたらしました、しかし、これらはすべてエンコードを指定することです。他の多くの点で、正規化、照合などのUnicodeメカニズムをまだ知らないままです。

BoostlooksNice、API賢明ですが、Boost.Localeは基本的に ICU のラッパーです。 IfBoostはcompiledwith ICU support ... if itそうではなく、Boost.Localeは標準ライブラリ用にコンパイルされたロケールサポートに制限されています。

そして、信じてください、gettingICUでコンパイルするためのBoostは、時々本当に痛いことがあります。 (Windows用のコンパイル済みバイナリはないため、アプリケーションと一緒に提供する必要があり、thatはまったく新しいワームの缶を開きます... )

個人的には、馬の口から直接Unicodeを完全にサポートし、 ICU ライブラリを直接使用することをお勧めします。

#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>

#include <iostream>

int main()
{
    char const * someString = "Eidenges\xe4\xdf";
    icu::UnicodeString someUString( someString, "ISO-8859-1" );
    // Setting the locale explicitly here for completeness.
    // Usually you would use the user-specified system locale.
    std::cout << someUString.toLower( "de_DE" ) << "\n";
    std::cout << someUString.toUpper( "de_DE" ) << "\n";
    return 0;
}

コンパイル(この例ではG ++を使用):

g++ -Wall example.cpp -licuuc -licuio

これは与える:

eidengesäß
EIDENGESÄSS

[1] 2017年、ドイツ正書法評議会は、曖昧さを避けるための従来の「SS」変換のオプションとして、「ẞ」U + 1E9Eローマ字大文字SHARP Sを公式に使用できると判断しました。パスポート(名前は大文字で表記)。委員会の決定により時代遅れになった私の好例.

210
DevSolar

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

29
Patrick Ohly

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);
}
25
incises

これは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;
}
14
user2218467

参照変数を含むforループに基づく範囲を使用するもう1つの方法

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

cout<<test<<endl;
8
Gilson PJ

私が見る限りでは、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を使うつもりです。

7
Etherealone

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;
}
5
Atul Rokade

標準C++ローカライゼーションライブラリのstd::ctype::tolower()はこれを正しく行います。これは 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";
}
5
Sameer

Boostに代わるものがPOCO(pocoproject.org)です。

POCOには2つの種類があります。

  1. 最初の変種は、元の文字列を変更せずにコピーを作成します。
  2. 2番目の変種は元の文字列を元の位置に変更します。
    「インプレース」バージョンでは、常に名前に「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);
3
Jason Enochs

大文字を小文字に変換する方法があります テストの場合は行いません それはかなり簡単です。 isupper()関数/マクロがclocale.hを使用すると、場所に関連する問題を処理できますが、そうでない場合は、UtoL []を自分の心の内容に合わせていつでも微調整できます。

Cの文字は実際にはたった8ビットのintであることを考えると(今のところワイド文字セットは無視します)、代替の文字セットを保持する256バイトの配列を作成でき、変換関数では文字列の文字を変換配列.

ただし、1対1マッピングの代わりに、大文字の配列メンバーに小文字のBYTE int値を指定してください。 islower()とisupper() はここで役に立ちます。

enter image description here

コードはこのようになります...

#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;
}

このアプローチでは、同時に、あなたが変更したい他の文字を再マッピングすることができます。

最新のプロセッサで実行する場合、このアプローチには大きな利点があります。分岐を含むifテストがないため、分岐予測を行う必要がありません。これにより、他のループに対するCPUの分岐予測ロジックが節約され、パイプラインの機能停止を防ぐことができます。

このアプローチをEBCDICからASCIIへの変換に使用されたものと同じものとして認識している人もいます。

2
user2548100

単純なものが必要な場合は、ここにマクロ手法があります。

#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以外の文字で作業している場合でも重要な考慮事項です。

2
Volomike

100%の時間で動作する代替手段はありますか?

いいえ

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

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

あなたがそれらの質問に対する答えを得たら、あなたはあなたのニーズに合った解決策を探し始めることができます。どこにいても誰にとってもうまくいくようなサイズではありません。

1
plugwash

C++ 20以降標準ライブラリで利用可能になり、現在 GitHub _ range-v3として個別に利用可能な今後のRangesライブラリについては答えがないので、この変換を実行する方法を追加したいそれを使って。

文字列をその場で変更するには:

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として表現できず、EOFと等しくない場合、unsigned charの動作は未定義です。これらの関数を普通のchars(またはsigned chars)と共に安全に使用するには、まず引数を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;
}
1
L. F.
// 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;
}

詳細情報: http://www.cplusplus.com/reference/locale/tolower/

1
MoraRockey

Microsoftプラットフォームでは、strlwrファミリーの関数を使用できます。 http://msdn.Microsoft.com/ja-jp/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 );
}
0
Autodidact

C++では、stringに対してtolowerメソッドやtoupperメソッドは実装されていませんが、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;
}

文字列に対する文字ベースの操作の場合: 文字列内のすべての文字の場合

0
Mahipal

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

#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);
}
0
TarmoPikaro

コードスニペット

#include<bits/stdc++.h>
using namespace std;


int main ()
{
    ios::sync_with_stdio(false);

    string str="String Convert\n";

    for(int i=0; i<str.size(); i++)
    {
      str[i] = tolower(str[i]);
    }
    cout<<str<<endl;

    return 0;
}
0
rashedcs

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");
0
Jee lee

これは大文字を小文字に、そしてその逆に変換するもう一つの簡単なバージョンかもしれません。私は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;
}

注:特殊文字がある場合は、条件チェックを使用して処理する必要があります。

0
Praveer Kumar

回答を改善することができなかったのでコピーしてください。ありがとうSO


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

説明:

for(auto& c : test)範囲ベースのforループ のようなものです
for (range_declaration:range_expression)loop_statement

  1. range_declarationauto& c
    ここで auto指定子 は自動型推論に使用されます。そのため、型は変数初期化子から差し引かれます。

  2. range_expressiontestname__
    この場合の範囲は、文字列testname__の文字です。

文字列testname__の文字は、識別子cname__を介したforループ内の参照として使用できます。

0
goulashsoup