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.cabc にマッチ
^ 行の先頭 ^abc → abcで始まる
$ 行の末尾 abc$ → abcで終わる
* 0回以上の繰り返し a*aaa にマッチ
+ 1回以上の繰り返し a+aaa にマッチ
? 0回または1回 a?a または空文字
{n} n回の繰り返し a{3}aaa にマッチ
[abc] 文字クラス [abc]abc にマッチ
[^abc] 否定文字クラス [^abc] → abc以外の文字にマッチ
\d 数字 \d0-9 にマッチ
\w 単語文字 \wa-zA-Z0-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 全検索
キャプチャグループ () でグループ化

📝 練習問題

  1. 初級(難易度 ⭐): std::regex で文字列が「数字のみ」かどうかを検証(^\d+$)し、「123」「12a3」「abc」をテストしてください。

  2. 中級(難易度 ⭐⭐): regex_search でテキストからメールアドレスを抽出してください(パターン:\w+@\w+\.\w+)。

  3. 上級(難易度 ⭐⭐⭐): regex_replace で「電話番号のマスキング」機能を実装してください。携帯番号の中央4桁を **** に置換。



次のレッスン:マルチスレッド基本(#43)

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%