C++ で正規表現を使用する - C++ プログラミング

PROGRAM


C++ で正規表現を使用する

C++ でも C++11 規格で正規表現が標準サポートされました。

正規表現といえば文字列を効果的に判定・抽出する有名な仕組みなので、Xcode や Visual Studio でもいち早く採用されているので、多くの場面で安心して利用できます。

 

ただし、正規表現のパターンを書くのに相性の良い raw 文字列は Visual Studio 2012 ではサポートされていないところは残念です。

raw 文字列については raw 文字列を使ってエスケープ文字を省略する に整理してあります。

 

正規表現の使用準備

C++ で正規表現を利用するには、regex ヘッダーをインクルードします。

#include <regex>

これで、正規表現の処理で使う次の機能が使えるようになりました。

 

クラス

std::regex 正規表現のパターンやオプションを設定するクラスです。
const char* 型のパターンを扱います。
std::wregex 正規表現のパターンやオプションを設定するクラスです。
const wchar_t* 型のパターンを扱います。
std::cmatch マッチした情報を格納するクラスです。
const char* 型の情報を扱います。
std::wcmatch マッチした情報を格納するクラスです。
const wchar_t* 型の情報を扱います。
std::smatch マッチした情報を格納するクラスです。
std::string::const_iterator 型の情報を扱います。
std::wsmatch マッチした情報を格納するクラスです。
std::wstring::const_iterator 型の情報を扱います。

 

関数

std::regex_search 対象文字列の中にパターンが含まれるかを調べます。
std::regex_match 対象文字列の全体をパターンで表現できているかを調べます。
std::regex_replace 対象文字列のパターンに該当する部分を、別の文字列に置き換えた文字列を取得します。

 

パターンに該当するか判定する

パターンに該当するかどうかを判定するには、std::regex_search 関数か std::regex_search 関数を使います。

std::regex_search と std::regex_match との違いは次の通りです。

  • std::regex_search は、対象の文字列の中から、正規表現パターンに該当する文字列があるかを調べます。(検索, 部分一致)
  • std::regex_match は、対象の文字列が、正規表現パターンで表現できているかを調べます。(完全一致)

 

この関数を使用するためには、あらかじめ正規表現パターンを設定した std::regex クラスのインスタンスを生成します。

そして、std:regex_seach 関数または std::regex_match 関数に、対象文字列と std::regex クラスのインスタンスを渡します。条件にマッチした場合に、これらの関数は true を返します。

たとえば const char* 型で対象文字列を指定する場合は、次のようになります。

const char* string = "The class provides a general framework for holding regular expressions.";

std::regex regex("f\\w*");

 

if (std::regex_search(string, regex))

{

// マッチした場合に true を返します。

 

}

std::string 型で対象文字列を指定する場合は、イテレータを使って指定できます。

std::string string = std::string("The class provides a general framework for holding regular expressions.");

std::regex regex("f\\w*");

 

if (std::regex_search(string.begin(), string.end(), regex))

{

// マッチした場合に true を返します。

 

}

このようにして、文字列がパターンとマッチしたかを知ることができます。

 

文字列のどの部分がパターンに該当したかを取得する

マッチした箇所の情報を取得することもできます。

std::regex_search や std::regex_match の、対象文字列として渡した引数に続けてマッチ情報を格納するためのクラスを指定すると、そこにマッチした情報が格納されます。

const char* string = "The class provides a general framework for holding regular expressions.";

 

std::regex regex("f\\w*");

std::cmatch match;

 

std::regex_search(string, match, regex);

このようにすることで、正規表現がマッチした時に、変数 match にマッチした時の情報が取得されます。

 

このとき、この例ではマッチした情報を格納するクラスを std::cmatch クラスを指定していますが、扱う文字列の形式に応じたいくつかのクラスが用意されています。

std::cmatch const char* 型の情報を扱います。
std::wcmatch const wchar_t* 型の情報を扱います。
std::smatch std::string::const_iterator 型の情報を扱います。
std::wsmatch std::wstring::const_iterator 型の情報を扱います。

これらの中から適切なものを選んで使用します。

 

マッチした情報を取り出す

引数に std::cmatch などのマッチ情報を取得する変数を渡して std::search 関数や std::match 関数を実行すると、引数に渡したマッチ情報を取得する変数に、マッチした文字列の情報が記録されます。

ここには複数の情報が記録されることがあり、マッチした数だけ、イテレータや [] 演算子を使って取得できます。

マッチした数を size 関数で取得したり、そもそもマッチしたかどうかを empty 関数で知ることもできます。

 

サブグループを含む正規表現では複数の情報が取得される

マッチした情報は、最初の情報が、パターン全体にマッチした文字列の情報になります。

続いて、正規表現でサブグループを指定している場合に、それらが順番にマッチした情報として記録されて行きます。

 

取得できる情報

マッチした情報のひとつひとつは std::csub_match や std::wcsub_match, std::ssub_match, std::wssub_match などの型で記録されているので、マッチした情報を取得する変数として指定した型に合わせて、これらを使って情報を取得します。

この std::sub_match 系の関数では、次のような関数を使って情報を取り出すことができます。

str マッチした文字列を取得します。
length マッチした文字列の長さを取得します。
first マッチした文字列の開始を示すイテレータです。
second マッチした文字列の末端を示すイテレータです。
compare 他のマッチ情報や文字列と比較する関数です。

 

繰り返し処理でマッチ情報を取得する

マッチした項目をひとつひとつ取り出して処理したい場合、std::match 系のクラスはイテレータを備えているので、C++11 で規定された "範囲に基づく for ループ" が使えるコンパイラならとても簡単です。

for (std::csub_match subMatch : match)

{

// ここで subMatch 変数を使って、各マッチ情報を取得できます。

 

}

"範囲に基づく for ループ" に対応していない場合は、通常の for ループを使って、次のようにプログラムします。

for (std::cmatch::const_iterator iterator = match.begin(); iterator != match.end(); iterator++)

{

// イテレータから取り出した subMatch を使って、各マッチ情報を取得します。

std::csub_match subMatch = *iterator;

 

}

他にも、std::match 系のクラスが持つ size 関数と [] 演算子を使って、普通の C 配列のように繰り返し処理する方法もあります。

これらの "範囲に基づく for ループ" なども含めた C++ でのループ処理のレパートリーは 配列を繰り返し処理する 3 つの方法 に整理してあるので参考にしてください。

 

パターンに該当した文字列を別の文字列に置き換える

正規表現でマッチした箇所を別の文字列に置き換えたい場合は std::regex_replace 関数を使用します。

std::string replacedString = std::regex_replace(string, regex, replacer);

このようにすることで、文字列 string を regex でパターンマッチして、マッチした箇所(全箇所)を文字列 replacer に置き換えます。

このとき、元の文字列は変更されません。

 

置き換える文字列に、次の特殊な文字列を使うことで、マッチしたテキストを置き換え文字に含めることもできます。

たとえば "$&" や "$0" を置き換える文字列に含めると、その部分が、マッチパターン全体にマッチしたテキストに置き換えられます。

サブグループを使った正規表現の場合には、"$1", "$2", "$3", ... というようにして、先頭から順に、それぞれのサブグループにマッチしたテキストに置き換えることができます。

 

大文字小文字の区別を無視してパターンマッチを行う場合

正規表現のパターンを、大文字小文字を区別しないで判定したい場合には、std::regex クラスの構築時に std::regex_constants::icase オプションを指定しておきます。

std::regex regex("THIS", std::regex_constants::icase);

こうすることで、この正規表現を使って検索や置換のパターンマッチを行ったときに、大文字小文字の違いを無視してマッチするかの判定がされるようになります。

元の文字列が変更されることはないので、std::regex_match や std::regex_search で取得したマッチ情報や、std::regex_replace を行ってマッチした情報に置き換えるような場合には、元のテキストの大文字小文字がそのままの形で取得できます。


[ もどる ]