C++: 正規表現
最終更新:2026-08-31
レッスン41では、ファイル操作について学びました。
ここでは、正規表現について学びます——テキスト処理の「魔法の杖」です。
検索、置換、バリデーション……正規表現はテキスト処理の強力なツールです。
1. 正規表現の基本
(1) 1.1 正規表現とは?
正規表現(Regex)はパターンを記述し、マッチング、検索、置換を行う強力なツールです。
たとえ話:
- ワイルドカード
*→ 単純なマッチング - 正規表現 → テキスト処理の「超人的な力」
(2) 1.2 C++の正規表現
C++11で正規表現が導入され、<regex> ヘッダで提供されます。
主な関数:
| 関数 | 機能 |
|---|---|
std::regex_match |
完全マッチ |
std::regex_search |
部分検索 |
std::regex_replace |
置換 |
std::regex_iterator |
全検索 |
2. 基本的な使い方
(1) 2.1 regex_match——完全マッチ
▶ サンプル 1:正規表現の応用(難易度 ⭐)
CPP
#include <iostream>
#include <regex>
#include <string>
int main() {
std::string phone = "13812345678";
std::regex pattern("^1[3-9]\\d{9}$"); // 携帯電話番号パターン
if (std::regex_match(phone, pattern)) {
std::cout << "有効な携帯番号です" << std::endl;
} else {
std::cout << "無効な携帯番号です" << std::endl;
}
return 0;
}
出力:
TEXT 📖 参照専用有効な携帯番号です
(2) 2.2 正規表現メタ文字
| メタ文字 | 説明 | 例 |
|---|---|---|
. |
任意の1文字 | a.c → abc にマッチ |
^ |
行の先頭 | ^abc → abcで始まる |
$ |
行の末尾 | abc$ → abcで終わる |
* |
0回以上の繰り返し | a* → aaa にマッチ |
+ |
1回以上の繰り返し | a+ → aaa にマッチ |
? |
0回または1回 | a? → a または空文字 |
{n} |
n回の繰り返し | a{3} → aaa にマッチ |
[abc] |
文字クラス | [abc] → a、b、c にマッチ |
[^abc] |
否定文字クラス | [^abc] → abc以外の文字にマッチ |
\d |
数字 | \d → 0-9 にマッチ |
\w |
単語文字 | \w → a-z、A-Z、0-9、_ にマッチ |
3. 検索と置換
(1) 3.1 regex_search——部分検索
▶ サンプル 2:メール検索(難易度 ⭐)
CPP
#include <iostream>
#include <regex>
#include <string>
int main() {
std::string text = "連絡先:abc@example.com test@gmail.com";
std::regex pattern("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}");
std::smatch match;
if (std::regex_search(text, match, pattern)) {
std::cout << "見つかったメール:" << match[0] << std::endl;
}
return 0;
}
(2) 3.2 regex_replace——置換
▶ サンプル:電話番号のマスキング(難易度 ⭐⭐)
CPP
#include <iostream>
#include <regex>
#include <string>
int main() {
std::string phone = "13812345678";
std::regex pattern("(\\d{3})\\d{4}(\\d{4})");
std::string result = std::regex_replace(phone, pattern, "$1****$2");
std::cout << "マスキング後:" << result << std::endl;
return 0;
}
出力:
TEXT 📖 参照専用マスキング後:138****5678
4. 全検索
(1) 4.1 regex_iterator
▶ サンプル:複数マッチ(難易度 ⭐⭐⭐)
CPP
#include <iostream>
#include <regex>
#include <string>
int main() {
std::string text = "連絡先:abc@example.com test@gmail.com";
std::regex pattern("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}");
auto begin = std::sregex_iterator(text.begin(), text.end(), pattern);
auto end = std::sregex_iterator();
std::cout << "見つかったメール:" << std::endl;
for (auto it = begin; it != end; ++it) {
std::cout << it->str() << std::endl;
}
return 0;
}
出力:
TEXT 📖 参照専用見つかったメール: abc@example.com test@gmail.com
5. キャプチャグループ
(1) 5.1 キャプチャとは?
() を使って、パターンの一部をキャプチャし、後で参照できます。
▶ サンプル:日付の解析(難易度 ⭐⭐)
CPP
#include <iostream>
#include <regex>
#include <string>
int main() {
std::string date = "2026-06-28";
std::regex pattern("(\\d{4})-(\\d{2})-(\\d{2})");
std::smatch match;
if (std::regex_match(date, match, pattern)) {
std::cout << "年:" << match[1] << std::endl;
std::cout << "月:" << match[2] << std::endl;
std::cout << "日:" << match[3] << std::endl;
}
return 0;
}
出力:
TEXT 📖 参照専用年:2026 月:06 日:28
6. 実践的なユースケース
(1) 6.1 入力バリデーション
| 種類 | 正規表現 |
|---|---|
| メールアドレス | [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} |
| 携帯番号(中国) | ^1[3-9]\d{9}$ |
| 身分証番号 | ^\d{17}[\dXx]$ |
| IPアドレス | ^(\d{1,3}\.){3}\d{1,3}$ |
(2) 6.2 データ抽出
▶ サンプル:HTMLリンクの抽出(難易度 ⭐⭐⭐)
CPP
#include <iostream>
#include <regex>
#include <string>
int main() {
std::string html = "<a href=\"https://example.com\">Example</a>";
std::regex pattern("<a href=\"([^\"]+)\"");
std::smatch match;
if (std::regex_search(html, match, pattern)) {
std::cout << "リンクURL:" << match[1] << std::endl;
}
return 0;
}
▶ サンプル 2:全メールアドレスの抽出(難易度 ⭐⭐)
CPP
#include <iostream>
#include <regex>
#include <string>
int main() {
std::string text = "Contact us at support@example.com or sales@company.org for help.";
std::regex emailPattern("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}");
std::sregex_iterator begin(text.begin(), text.end(), emailPattern);
std::sregex_iterator end;
std::cout << "見つかったメールアドレス:" << std::endl;
for (auto it = begin; it != end; ++it) {
std::cout << " " << it->str() << std::endl;
}
return 0;
}
出力:
TEXT 📖 参照専用見つかったメールアドレス: support@example.com sales@company.org
▶ サンプル 3:IPアドレス形式の検証(難易度 ⭐⭐)
CPP
#include <iostream>
#include <regex>
#include <string>
bool isValidIP(const std::string& ip) {
// パターン:1-3桁の数字が4つ、ドットで区切られる
std::regex pattern("^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$");
std::smatch match;
if (!std::regex_match(ip, match, pattern)) {
return false;
}
// 各オクテットが0-255の範囲内かチェック
for (size_t i = 1; i <= 4; i++) {
int octet = std::stoi(match[i].str());
if (octet > 255) return false;
}
return true;
}
int main() {
std::string ips[] = {"192.168.1.1", "256.1.1.1", "10.0.0.255", "invalid"};
for (const auto& ip : ips) {
std::cout << ip << ": " << (isValidIP(ip) ? "有効" : "無効") << std::endl;
}
return 0;
}
出力:
TEXT 📖 参照専用192.168.1.1: 有効 256.1.1.1: 無効 10.0.0.255: 有効 invalid: 無効
❓ よくある質問
Q:正規表現のパフォーマンスは? A: - コンパイルに時間がかかる → 再利用(
std::regexコンストラクタ)
std::regex_constants::optimizeフラグを使用
Q:バックスラッシュの扱いは? A:文字列リテラル(生文字リテラル)を使用(C++11):
CPP
// 通常の文字列(エスケープ必要)
std::regex pattern("\\\\d+");
// 生文字リテラル(推奨)
std::regex pattern(R"(\d+)");
Q:正規表現でHTMLをパースできる? A: お勧めしません。HTML/XMLなどの複雑な構造には、専用のパーサを使用してください。
📖 まとめ
| トピック | 要点 |
|---|---|
| regex_match | 完全マッチ |
| regex_search | 部分検索 |
| regex_replace | 置換 |
| regex_iterator | 全検索 |
| キャプチャグループ | () でグループ化 |
📝 練習問題
-
初級(難易度 ⭐):
std::regexで文字列が「数字のみ」かどうかを検証(^\d+$)し、「123」「12a3」「abc」をテストしてください。 -
中級(難易度 ⭐⭐):
regex_searchでテキストからメールアドレスを抽出してください(パターン:\w+@\w+\.\w+)。 -
上級(難易度 ⭐⭐⭐):
regex_replaceで「電話番号のマスキング」機能を実装してください。携帯番号の中央4桁を****に置換。
- 正規表現は強力なテキスト処理ツール
std::regexでパターンを定義std::regex_matchは完全マッチstd::regex_searchは部分検索std::regex_replaceは置換
次のレッスン:マルチスレッド基本(#43)