C++ 正規表現で検索文字列の抽出/出現位置の判定【match_results/std::regex】

正規表現にマッチした文字列やその出現位置を調べたい場合には、std::match_resultsクラスを活用する必要があります。match_resultsは正規表現用の各種関数の第二引数に指定する形で利用します。

// #include <regex> // using cmatch = std::match_results<const char*>;

std::cmatch results;
if (std::regex_search("Shop99", results, std::regex("\\d+"))) {
  std::string s = results.str();      // 99 (マッチした文字列)
  auto position = results.position(); //  4 (マッチした文字列の位置)
  auto length   = results.length();   //  2 (マッチした文字列の長さ)
}
上記のようなC言語スタイルの文字列を検索する場合にはstd::match_results<const char*>の別名であるstd::cmatchを用います。std::string文字列型の場合にはstd::smatchを用います。

正規表現中の丸括弧()によってキャプチャされた文字列はサブマッチとして抽出できます。

std::string s = "Shop 99";
std::smatch results;
if (std::regex_match(s, results, std::regex("(\\w+) (\\d+)"))) {
  results[0].str(); // "Shop 99" (全体のサブマッチ)
  results[1].str(); // "Shop"    (`(\\w+)`によるサブマッチ)
  results[2].str(); // "99"      (`(\\d+)`によるサブマッチ)
}

std::regex_match関数は正規表現の完全一致を判定する関数です。std::regex_searchは部分一致を判定します。

参考: 正規表現による文字列検索(部分一致/完全一致)【std::regex_search/std::regex_match】

目次

スポンサーリンク

マッチした全ての文字列を取得する方法

部分一致した文字列のパターンを全て抽出したい場合には、繰り返し文を用いた部分検索、またはイテレータを用いた探索が必要となります。

イテレータによるパターン抽出(regex_iterator)

std::string文字列の検索を行う場合にはstd::sregex_iteratorイテレータを用いることができます。

std::vector<std::string> v = {};

std::string s = "Shop 99";
std::regex pt{"\\w+"};
std::sregex_iterator end, ite{s.begin(), s.end(), pt};
for (; ite != end ; ++ite) {
  v.push_back(ite->str());
}

// v == {"Shop", "99"}

C言語スタイルの文字列の場合はstd::cregex_iteratorイテレータを用います。

const char* s = "Shop 99";
std::regex pt{"\\w+"};
std::cregex_iterator end, ite{s, s + strlen(s), pt};
for (; ite != end; ++ite) {
  v.push_back(ite->str());
}

部分検索によるパターン抽出

文字列がマッチするたびに検索開始位置をずらしていく方法です。

std::vector<std::string> v = {};

std::string s = "Shop 99";
std::smatch m;
auto start = s.cbegin();
while (std::regex_search(start, s.cend(), m, std::regex("\\w+"))) {
  v.push_back(m.str());
  start = m[0].second;
}

// v == {"Shop", "99"}
m[0].secondはマッチした文字列の次の要素を指すポインタ/イテレータです。

C言語スタイルの文字列の場合は以下の方法を用います。

const char* s = "Shop 99";
std::cmatch m;
const char* start = s;
const char* end   = s + std::char_traits<char>::length(s);
while (std::regex_search(start, end, m, std::regex("\\w+"))) {
  v.push_back(m.str());
  start = m[0].second;
}

文字列別のmatch_results(cmatch, smatch, wcmatch, wsmatch)

C言語スタイルの文字列(const char*)とC++の文字列(std::string)でそれぞれ適切なmatch_resultsを用いる必要があります。

文字列クラス代替表現
const char*std::cmatchstd::match_results<const char*>
std::stringstd::smatchstd::match_results<string::const_iterator>
const wchar_t*std::wcmatchstd::match_results<const wchar_t*>
std::wstringstd::wsmatchstd::match_results<std::wstring::const_iterator>

文字列型std::stringに対する検索を行う際には、一時オブジェクトによる検索が行えなくなるため注意が必要です。

std::smatch m;

/* C++14以降は利用できない */
// error: call to deleted function 'regex_search'
std::regex_search(std::string("s"), m, std::regex("."));

/* 代替処理 */
std::string s = "s";
std::regex_search(s, m, std::regex("."));

std::regexなどのbasic_regexをregex_iteratorに渡す際にも同様の問題が発生します。

std::string s = "s";

/* C++14以降は利用できない */
// error: call to deleted constructor of 'std::sregex_iterator' (aka 'regex_iterator<__wrap_iter<const char *> >')
std::sregex_iterator{begin(s), end(s), std::regex(".")};
// error: call to deleted constructor of 'std::cregex_iterator' (aka 'regex_iterator<const char *>')
std::cregex_iterator{"", "", std::regex(".")};

/* 代替処理 */
std::regex pt{"."};
std::sregex_iterator{begin(s), end(s), pt};
std::cregex_iterator{"", "", pt};

match_resultsで取得できる値や情報、応用方法

std::cmatch m;
std::regex_search("abcd", m, std::regex("(b)(c)")); // true

/* マッチ */
m.str() == "bc"    ; // マッチした文字列
m.length() == 2    ; // マッチした文字列の長さ
m.position() == 1  ; // マッチした文字列の位置
m.empty() == false ; // マッチしたか否か

/* サブマッチ */
m.size() == 3      ; // サブマッチの数
m[0].str() == "bc" ; // サブマッチ(パターン全体)
m[1].str() == "b"  ; // サブマッチ(`(b)`)
m[2].str() == "c"  ; // サブマッチ(`(c)`)
m.str(2)   == "c"  ; // サブマッチ(`(c)`)
m[2].length() == 1 ; // サブマッチの文字列の長さ

m.prefix().str() == "a" ; // マッチした文字列の前方を表すサブマッチ
m.suffix().str() == "d" ; // マッチした文字列の後方を表すサブマッチ
m[0].second == m.suffix().first ; // マッチした場合は同じ値になる

/* サブマッチ - std::sub_match */
const std::sub_match<const char*>& sub_match = m[1];
std::string(sub_match) == "b"   ; // true
std::string sub_match_str = m[1]; // "b" == sub_match_str
// スーパークラスのメンバ関数(`std::pair<T, U>::first`)
const char* first  = sub_match.first;
const char* second = sub_match.second;
first  == std::string("bcd") ;    // true
second == std::string("cd")  ;    // true

/* 拡張for文/イテレータ */
for (const std::sub_match<const char*>& v : m) { // for (auto&& v : m) {}
  // "2:bc", "1:b", "1:c" の順に出力
  printf("%ld:%s\n", v.length(), v.str().c_str());
}

/* 型 */
using size_type = decltype(m.position());
static_assert(std::is_same<size_type, ptrdiff_t>::value, "");
static_assert(std::is_same<size_type, std::iterator_traits<const char*>::difference_type>::value, "");
std::cmatch results;
std::regex_search("ab", results, std::regex("a")) ;
results[0].matched == true ;
results[0].str()   == "a"  ;

auto prefix = results.prefix();
prefix.matched == false ;

auto suffix = results.suffix();
suffix.matched == true  ;
suffix.str()   == "b"  ;
std::cmatch m;
std::regex_search("ABcdEF", m, std::regex("(c)(d)"));
m == {
  matches: [
    {str: "cd", first: "cdEF", second: "EF" , matched: true}
    {str: "c",  first: "cdEF", second: "dEF", matched: true}
    {str: "d",  first: "dEF" , second: "EF" , matched: true}
  ]
  prefix: {str: "AB", first: "ABcdEF", second: "cdEF", matched: true}
  suffix: {str: "EF", first: "EF"    , second: "\0"  , matched: true}
};

広告