std::string の小文字を大文字に変換する - C++ プログラミング

PROGRAM


std::string の小文字を大文字に変換する

std::string に格納してある文字列の小文字を大文字に変換したいようなときには <algorithm> ヘッダーに定義されている std::transform 関数が使えます。

たとえば、格納されている文字列を大文字に変換したい場合を見て行きます。

 

既存の値を上書きする方法

既存の std::string インスタンスの文字列を直接置き換えてよければ、次のようにします。

// std::string 型の string のテキストを全て大文字に変換しています。

std::transform(string.cbegin(), string.cend(), string.begin(), toupper);

これで string インスタンス内の全ての文字が toupper によって変換されて上書きされます。

 

ちなみに std::transform に渡す引数は、それぞれ次の通りです。

第 1 引数 変換元の文字位置を示すイテレータを指定します。読み取り専用で大丈夫です。
第 2 引数 変換元の文字終了位置(最後の文字の次)を示すイテレータです。読み取り専用で大丈夫です。
第 3 引数 変換した文字を格納する最初の位置を示すイテレータです。書き込み可能である必要があります。
第 4 引数 1 文字の変換を行う関数ポインタ、または 1 文字を引数に受け取って変換後の値を返す () 演算子が定義された構造体のインスタンスです。

もちろん、第 4 引数で変換関数を指定しているので、ここを tolower にすれば大文字を小文字に変換することもできますし、変換関数を自作して任意の変換処理を行うことも可能です。

また、ここで引数に指定するイテレータについては STL の配列をイテレータで繰り返し処理する を見ると参考になると思います。

 

別の std::string に新規に書き出す方法

既存の std::string インスタンスを上書きしたくない場合には、新しい std::string インスタンスを作ってちょっと初期化を行えば、変換後の文字列をそこに書き込んで行くことができます。

// std::string 型の source のテキストを全て大文字に変換して destination に書き出しています。

std::string destination;

 

destination.resize(source.size());

std::transform(source.cbegin(), source.cend(), destination.begin(), toupper);

このように、変換後の文字列を格納するための std::string のインスタンスを用意して、第 3 引数にその開始位置を指定してあげます。

 

変換後の文字列を格納する std::string インスタンスを準備する上での注意点としては、単純にディフォルトコンストラクタで初期化しただけだと変換後の文字列が空文字になってしまうところです。

そのため、事前に resize 関数を使って領域を確保しておく必要があります。

 

上書きと新規の実行速度

std::string を変換するのに、上書きするのと別のところに記録するのと 2 通りの方法があるとなると、それらの実行効率が少し気になるところです。

そこで 21 文字程度の文字列を大文字に変換する処理を、上書きによる方法と、別のインスタンスへ書き出す方法とで、それぞれ実行速度を比較してみました。

 

1,000,000 回実行してみるとどちらも 1 秒前後、どちらが先に終わるかどうかは実行した時々でばらつきがある感じでした。

大抵は書き込み先の準備も必要になるだろうと思って、別のインスタンスに書き出すときに毎回一度 reset 関数を呼び出してみたのですけど、それでも上書きの方が早く終わることの方が多いような気がするものの、大きな差もなければ、タイミングによっては新規書き出しの方が早いときも何度もありました。

試しにそれぞれ 1 回だけ実行してみると、この時は何故か新規に書き出す方が速い…かと思ったのですが、順番を入れ替えてみたところ、どうやら後に実行した方が、決まって 2 〜 3 倍ほど速い様子でした。メモリキャッシュとかが影響してたりするのでしょうか。

 

このような感じから、上書き変換の方がなんとなく幾分速そうですけど、どちらを使っても選択に大きく左右されるようなことはそうそうなさそうです。

 

各単語の先頭文字を大文字に変換する

std::transform 関数では、変換で使用する関数として任意のものを指定できるので、たとえば各単語の先頭文字を大文字に変換するキャピタライズも、関数を用意すれば実現できます。

std::transform に渡す関数は 1 文字だけを受け取ってその変換を返すものであるため、キャピタライズのように現在状態が単語の先頭なのか途中なのかといった情報を保持するためにも、変換関数は構造体で用意します。

// std::transform で使うキャピタライズ機能を実装した C++ 構造体です。

struct tocapital

{

// m_beginning で単語の先頭かどうかを記録します。

bool m_beginning;

 

// 最初は単語の先頭として扱うように、コンストラクタで初期化します。

tocapital()

{

m_beginning = true;

}

 

// std::transform で 1 文字毎に呼び出される () 演算子です。

int operator()(int word)

{

int result;

 

if (std::isspace(word) != 0)

{

// 空白文字であれば、次の文字は先頭文字です。空白文字は変換を通さずそのまま返します。

m_beginning = true;

 

result = word;

}

else

{

// 空白文字でなければ、先頭文字かどうかで変換方法を切り替えます。

if (m_beginning)

{

// 先頭文字の場合は大文字に変換します。そして、以降の文字は先頭ではないと記録しておきます。

m_beginning = false;

 

result = toupper(word);

}

else

{

// 先頭ではない文字は小文字に変換します。

result = tolower(word);

}

}

 

return result;

}

};

このような構造体を実装しておいて、変換時には次のように std::transform に渡します。

// 独自に作成した変換関数(構造体)を使用して std::string 型の string のテキストをキャピタライズします。

std::transform(string.cbegin(), string.cend(), string.begin(), tocapital());

ここでの注意事項としては、変換関数として渡すところで "tocapital()" というように () を付けるところです。

このようにする理由は、今回の tocapital は構造体のため tocapital() として、そのインスタンスを作成する必要があるためです。これでインスタンスが初期化されて変換処理が始まり、変換時は 1 文字毎に operator() が実行されます。


[ もどる ]