std::regex(正規表現)
『C++』では <regex> ヘッダで提供される std::regex を使うと、正規表現によるパターンマッチ・検索・置換を行えます。std::regex_match・std::regex_search・std::regex_replace の3つの関数が主な操作の入口であり、マッチ結果は std::smatch(std::string 用)で受け取ります。
構文
// ========================================
// std::regex の基本構文
// ========================================
#include <regex>
#include <string>
// 正規表現オブジェクトを作成します
std::regex pattern("パターン文字列");
// ① regex_match: 文字列全体がパターンに一致するか調べます
std::string str = "対象文字列";
bool matched = std::regex_match(str, pattern);
// ② regex_search: 文字列の中にパターンが含まれるか調べます
std::smatch m;
bool found = std::regex_search(str, m, pattern);
// m[0] にマッチ全体、m[1] 以降にキャプチャグループが入ります
// ③ regex_replace: パターンに一致した部分を置換します
std::string result = std::regex_replace(str, pattern, "置換後文字列");
// イテレータでマッチを全件走査します
std::sregex_iterator it(str.begin(), str.end(), pattern);
std::sregex_iterator end;
while (it != end) {
std::smatch match = *it;
// match[0] がマッチ全体です
++it;
}
主な関数・型一覧
| 関数・型 | 概要 |
|---|---|
| std::regex | 正規表現パターンを保持するオブジェクトです。コンストラクタでパターン文字列を渡します。 |
| std::regex_match | 文字列全体がパターンに一致するかどうかを返します。部分一致では false になります。 |
| std::regex_search | 文字列の中にパターンと一致する部分が含まれるかを調べます。最初の一致を std::smatch に格納します。 |
| std::regex_replace | パターンに一致した部分をすべて置換した新しい文字列を返します。 |
| std::smatch | std::string に対するマッチ結果を格納するコンテナです。m[0] がマッチ全体、m[n] が n 番目のキャプチャグループです。 |
| std::sregex_iterator | 文字列全体を走査して、すべてのマッチを順に取り出すイテレータです。 |
| std::regex_constants::icase | 大文字・小文字を区別しないフラグです。std::regex の第2引数に指定します。 |
サンプルコード
kof_regex.cpp
// ========================================
// kof_regex.cpp
// KOF(ザ・キング・オブ・ファイターズ)の
// ファイター名一覧から std::regex を使って
// パターンマッチ・検索・置換を行うサンプルです
// ========================================
#include <iostream>
#include <regex>
#include <string>
#include <vector>
int main() {
// ========================================
// KOF ファイター情報(名前・チーム)
// ========================================
std::vector<std::string> fighters = {
"草薙京 / 草薙チーム",
"八神庵 / 八神チーム",
"テリー・ボガード / 餓狼チーム",
"不知火舞 / 餓狼チーム",
"アンディ・ボガード / 餓狼チーム",
"草薙柴舟 / ボスキャラクター"
};
// ========================================
// ① regex_match: 文字列全体の一致を確認します
// 「草薙」で始まるエントリかどうかを判定します
// ========================================
std::cout << "=== regex_match: 草薙で始まるエントリ ===" << std::endl;
std::regex matchPattern("草薙.*"); // 「草薙」で始まる任意の文字列です
for (int i = 0; i < (int)fighters.size(); i++) {
bool isMatch = std::regex_match(fighters[i], matchPattern);
std::cout << fighters[i] << " → " << (isMatch ? "一致" : "不一致") << std::endl;
}
std::cout << std::endl;
// ========================================
// ② regex_search: 文字列中の部分一致を検索します
// 「ボガード」という文字列が含まれるエントリを探します
// ========================================
std::cout << "=== regex_search: ボガードが含まれるエントリ ===" << std::endl;
std::regex searchPattern("ボガード");
for (int i = 0; i < (int)fighters.size(); i++) {
std::smatch m;
if (std::regex_search(fighters[i], m, searchPattern)) {
std::cout << "発見: " << fighters[i] << std::endl;
std::cout << " マッチ箇所: \"" << m[0] << "\"" << std::endl;
}
}
std::cout << std::endl;
// ========================================
// ③ キャプチャグループ: 名前とチームを分離します
// 「名前 / チーム名」の形式から名前部分とチーム部分を取り出します
// ========================================
std::cout << "=== キャプチャグループ: 名前とチームを分離 ===" << std::endl;
// (.+?) が名前、(.+) がチーム名に対応するキャプチャグループです
std::regex capturePattern("(.+?) / (.+)");
for (int i = 0; i < (int)fighters.size(); i++) {
std::smatch m;
if (std::regex_search(fighters[i], m, capturePattern)) {
std::cout << "名前: " << m[1] << " チーム: " << m[2] << std::endl;
}
}
std::cout << std::endl;
// ========================================
// ④ regex_replace: 一致した部分を置換します
// 「チーム」を「TEAM」に置き換えます
// ========================================
std::cout << "=== regex_replace: チーム → TEAM に置換 ===" << std::endl;
std::regex replacePattern("チーム");
for (int i = 0; i < (int)fighters.size(); i++) {
std::string replaced = std::regex_replace(fighters[i], replacePattern, "TEAM");
std::cout << replaced << std::endl;
}
std::cout << std::endl;
// ========================================
// ⑤ sregex_iterator: 複数マッチを全件走査します
// 文章中に含まれるすべての人名を取り出します
// ========================================
std::cout << "=== sregex_iterator: 人名をすべて抽出 ===" << std::endl;
std::string story =
"草薙京と八神庵は宿敵同士ですが、テリー・ボガードや"
"不知火舞、アンディ・ボガードとともに共闘することもあります。";
// 「・」を含むカタカナ名と、漢字2〜4文字の名前を OR で指定します
std::regex namePattern("[ァ-ヶー・]{2,}|[一-龥]{2,4}[京庵]");
std::sregex_iterator it(story.begin(), story.end(), namePattern);
std::sregex_iterator end;
int count = 1;
while (it != end) {
std::smatch match = *it;
std::cout << count << ": " << match[0] << std::endl;
++count;
++it;
}
std::cout << std::endl;
// ========================================
// ⑥ icase フラグ: 大文字・小文字を区別しない検索です
// ========================================
std::cout << "=== icase フラグ: 大文字小文字を無視して検索 ===" << std::endl;
std::vector<std::string> teamTags = {
"TEAM_KUSANAGI",
"team_yagami",
"Team_Bogard",
"TEAM_SHIRANUI"
};
// icase フラグで大文字・小文字を区別しません
std::regex icasePattern("team_bogard", std::regex_constants::icase);
for (int i = 0; i < (int)teamTags.size(); i++) {
bool found = std::regex_search(teamTags[i], icasePattern);
std::cout << teamTags[i] << " → " << (found ? "一致" : "不一致") << std::endl;
}
return 0;
}
# コンパイルします g++ -std=c++11 kof_regex.cpp -o kof_regex && ./kof_regex === regex_match: 草薙で始まるエントリ === 草薙京 / 草薙チーム → 一致 八神庵 / 八神チーム → 不一致 テリー・ボガード / 餓狼チーム → 不一致 不知火舞 / 餓狼チーム → 不一致 アンディ・ボガード / 餓狼チーム → 不一致 草薙柴舟 / ボスキャラクター → 一致 === regex_search: ボガードが含まれるエントリ === 発見: テリー・ボガード / 餓狼チーム マッチ箇所: "ボガード" 発見: アンディ・ボガード / 餓狼チーム マッチ箇所: "ボガード" === キャプチャグループ: 名前とチームを分離 === 名前: 草薙京 チーム: 草薙チーム 名前: 八神庵 チーム: 八神チーム 名前: テリー・ボガード チーム: 餓狼チーム 名前: 不知火舞 チーム: 餓狼チーム 名前: アンディ・ボガード チーム: 餓狼チーム 名前: 草薙柴舟 チーム: ボスキャラクター === regex_replace: チーム → TEAM に置換 === 草薙京 / 草薙TEAM 八神庵 / 八神TEAM テリー・ボガード / 餓狼TEAM 不知火舞 / 餓狼TEAM アンディ・ボガード / 餓狼TEAM 草薙柴舟 / ボスキャラクター === sregex_iterator: 人名をすべて抽出 === 1: 草薙京 2: 八神庵 3: テリー・ボガード 4: 不知火舞 5: アンディ・ボガード === icase フラグ: 大文字小文字を無視して検索 === TEAM_KUSANAGI → 不一致 team_yagami → 不一致 Team_Bogard → 一致 TEAM_SHIRANUI → 不一致
よくあるミス: regex_match と regex_search を使い分けない
std::regex_match は文字列全体がパターンと一致する場合のみ true を返します。文字列の一部にパターンが含まれるかを調べたい場合は std::regex_search を使います。
// NG:
std::string entry = "八神庵 / 八神チーム";
std::regex pattern("八神庵");
bool result = std::regex_match(entry, pattern);
// result は false になります(文字列全体が「八神庵」ではないため)
OK: 部分一致を調べるには std::regex_search を使います。
// OK: bool result = std::regex_search(entry, pattern); // result は true になります(文字列中に「八神庵」が含まれているため)
よくあるミス: ループ内で regex オブジェクトを毎回生成する
std::regex オブジェクトの構築はパターンのコンパイルを伴うため、コストが高い処理です。ループ内で毎回生成すると、パフォーマンスが著しく低下します。
// NG:
std::vector<std::string> fighters = {"草薙京", "八神庵", "テリー・ボガード"};
for (int i = 0; i < (int)fighters.size(); i++) {
std::regex pattern("八神.*"); // ループのたびに regex を構築します(非効率)
if (std::regex_match(fighters[i], pattern)) {
std::cout << fighters[i] << std::endl;
}
}
OK: std::regex はループの外で1回だけ生成します。
// OK:
std::regex pattern("八神.*"); // ループの外で1回だけ構築します
for (int i = 0; i < (int)fighters.size(); i++) {
if (std::regex_match(fighters[i], pattern)) {
std::cout << fighters[i] << std::endl;
}
}
概要
std::regex は C++11 で標準ライブラリに追加された正規表現サポートです。std::regex_match は文字列全体がパターンと一致するかどうかを確認するのに対し、std::regex_search は文字列の一部に一致があれば true を返します。マッチ結果は std::smatch(std::string 向け)に格納でき、m[0] がマッチ全体、m[1] 以降がキャプチャグループに対応します。複数のマッチをすべて取り出したい場合は std::sregex_iterator を使ってイテレータで走査します。std::regex_replace は一致箇所をまとめて別の文字列に置き換えた結果を返します。フラグには std::regex_constants::icase(大文字・小文字を無視)などが用意されています。正規表現オブジェクトの構築はコストが高いため、同じパターンを繰り返し使う場合は std::regex を一度だけ生成してループ外で保持しておくと効率的です。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。