ユーザーガイド

本節では xpressive を使ったテキスト処理、パース処理の方法を説明する。xpressive の特定のコンポーネントについて詳細な情報を探している場合は、リファレンスの節を見よ。

はじめに

xpressive とは何か

xpressive は正規表現のテンプレートライブラリである。正規表現・正規式(regex とも1)は実行時に動的に解析される文字列としても(動的正規表現)、またはコンパイル時に解析される式テンプレート2 としても(静的正規表現)記述できる。動的正規表現の利点は、実行時にユーザーが入力したり、初期化ファイルから読み取りが可能なことである。静的正規表現には利点がいくつかある。文字列ではなく C++ 式テンプレートなのでコンパイル時に構文チェックを受ける。また、プログラム内のコードとデータを参照可能なので、正規表現マッチの最中にコードを呼び出すこともできる。加えて静的束縛されるので、コンパイラは静的正規表現についてより高速なコードを生成する可能性がある。

xpressive のこの 2 本立ての機能は独特かつ強力である。静的 xpressive は Spirit パーサフレームワークのようなものである。Spirit と同様、式テンプレートを使った静的正規表現で文法を構築できる(Spirit と異なり、xpressive はパターンマッチを探索するためにあらゆる可能性を試行する網羅的なバックトラックを行う)。動的正規表現は Boost.Regex のようなものである。実際、xpressive のインターフェイスは Boost.Regex を使ったことのある人にとっては親しみやすいはずである。xpressive の革新的な点は、静的正規表現と動的正規表現を同じプログラム内(同じ式内でも!)で混ぜてマッチできることである。動的正規表現を静的正規表現に組み込むこともその逆も可能である。組み込んだ正規表現はパターンマッチに必要な検索やバックトラックに対して完全に機能する。

Hello, world!

理論は十分だ。xpressive スタイルの Hello World を見よう。

#include <iostream>
#include <boost/xpressive/xpressive.hpp>

using namespace boost::xpressive;

int main()
{
    std::string hello( "hello world!" );

    sregex rex = sregex::compile( "(\\w+) (\\w+)!" );
    smatch what;

    if( regex_match( hello, what, rex ) )
    {
        std::cout << what[0] << '\n'; // マッチ全体
        std::cout << what[1] << '\n'; // 1番目の捕捉
        std::cout << what[2] << '\n'; // 2番目の捕捉
    }

    return 0;
}

このプログラムは以下を出力する。

hello world!
hello
world

このコードでまず注意すべきは、xpressive の型がすべて boost::xpressive 名前空間にあるということである。

注釈

本文書における残りのほとんどの例では using namespace boost::xpressive; ディレクティブを省略しているが、実際には必要である。

次に注意すべきは正規表現オブジェクトの型が sregex ということである。Boost.Regex に馴染んでいるのであれば、今まで使っていたものとは違うという点に気をつけなければならない。「sregex」の「s」は「string」のことであり、この正規表現は std::string オブジェクト内でパターンを探索するのに使用するということを表している。この違いとそれが意味するところについては後で述べる。

正規表現オブジェクトをどのように初期化するかに注目する。

sregex rex = sregex::compile( "(\\w+) (\\w+)!" );

正規表現オブジェクトを文字列から作成する場合、basic_regex::compile といったファクトリメソッドを呼び出さなければならない。これもまた、xpressive が他のオブジェクト指向正規表現ライブラリと異なっている点である。他のライブラリでは正規表現は文字列の強化版のような扱いだが、xpressive では正規表現は文字列ではなく、ドメイン固有言語における小さなプログラムである。文字列はそのような言語の表現の 1 つにすぎない。もう1つの表現が式テンプレートである。例えば上のコード行は以下と等価である。

sregex rex = (s1= +_w) >> ' ' >> (s2= +_w) >> '!';

これは同じ正規表現を表しているが、静的 xpressive が定義するドメイン固有の組み込み言語を用いている点が異なる。

見てのとおり、静的正規表現の構文には標準的な Perl の構文と顕著に違う点がある。これは C++ 構文の制約によるもので、最も大きな違いは「後続」を表す >> の使用である。例えば Perl では部分式を続けて書くことができる。

abc

しかし C++ では部分式を分離する演算子がなければならない。

a >> b >> c

Perlでは括弧 () は特別な意味をもつ。これらはグループ化を行うが、$1$2 といった後方参照を作成するという副作用がある。C++ では括弧を多重定義して副作用を与えることはできない。そこで同じ効果を得るために s1s2 という特殊なトークンを使用する。これらに代入を行うことで後方参照を作成する(xpressive では部分マッチ(sub-match)という)。

他に注意すべき点として、1 回以上の繰り返しを表す + 演算子の位置が後置から前置になっているということがある。これは C++ が後置の + 演算子をもたないためである。よって、

"\\w+"

これは以下と同じである。

+_w

他のすべての違いについては後で触れる。

1

訳注 Regular expression の省略形ですが、翻訳版では省略せず「正規表現」「正規式」と書きます。

2

Expression Templates(英語)を参照。

xpressive のインストール

xpressive の入手

xpressive の入手方法は 2 つある。第 1 のより簡単な方法は Boost の最新版をダウンロードすることである。http://sf.net/projects/boost へ行き、“Download” リンクをたどるだけである。

2 番目の方法は Boost の Subversion リポジトリに直接アクセスすることである。http://svn.boost.org/trac/boost へ行き、そこにある匿名 Subversion アクセス方法に従うとよい。Boost Subversion にあるのは不安定版である。

xpressive を使ったビルド

xpressive はヘッダのみのテンプレートライブラリであり、あなたのビルドスクリプトを書き直したり個別のライブラリファイルにリンクする必要はない。#include <boost/xpressive/xpressive.hpp> とするだけでよい。使用するのが静的正規表現だけであれば、xpressive_static.hpp だけをインクルードすることでコンパイル時間を短縮できる。同様に動的正規表現だけを使用するのであれば xpressive_dynamic.hpp をインクルードするとよい。

静的正規表現とともに意味アクションやカスタム表明を使用したければ、regex_actions.hpp も追加でインクルードする必要がある。

必要要件

xpressive を使用するには Boost 1.34.1 以降が必要である。

サポートするコンパイラ

  • Visual C++ 7.1 以降

  • GNU C++ 3.4 以降

  • Intel for Linux 8.1 以降

  • Intel for Windows 10 以降

  • tru64cxx 71 以降

  • MinGW 3.4 以降

  • HP C/C++ A.06.14 以降

Boost の退行テスト結果のページにある最新テスト結果を参照するとよい。

注釈

質問、コメント、バグ報告は eric <at> boost-consulting <dot> com に送ってほしい。

クイックスタート

xpressive で何かするのに知っておくべきことはそう多くない。xpressive が提供する型とアルゴリズムの 5 セント旅行に出かけよう。

xpressive のツールボックス

ツール

説明

basic_regex<>

コンパイル済みの正規表現を保持する。basic_regex<> は xpressive で最も重要な型である。xpressive で何かする場合は basic_regex<> 型のオブジェクトを作成することから始める。

match_results<>sub_match<>

match_results<> は、regex_matchregex_search 操作の結果を保持する。sub_match<> オブジェクトのベクタのように振舞う。個別の sub_match<> オブジェクトはマーク済み部分式(Perlにおける後方参照)を保持する。基本的にはマーク済み部分式の開始と終了を表すイテレータの組にすぎない。

regex_match

文字列が正規表現にマッチするか調べる。regex_match が成功するのは、文字列全体の先頭から終端までが正規表現にマッチする場合である。regex_matchmatch_results<> を与えると、見つかったマーク済み部分式が書き込まれる。

regex_search

正規表現にマッチする部分文字列を文字列内で検索する。regex_search は文字列内のあらゆる位置でマッチを検索する。文字列の先頭から開始し、マッチを見つけるか文字列内をすべて走査すると終了する。regex_match と同様、regex_searchmatch_results<> を与えると、見つかったマーク済み部分式が書き込まれる。

regex_replace

入力文字列、正規表現、置換文字列を与えると、regex_replace は入力文字列内の正規表現にマッチした部分を置換文字列で置換した新しい文字列を構築する。置換文字列にはマーク済み部分式への参照を含めることができる。

regex_iterator<>

文字列内の正規表現にマッチする位置を見つける STL 互換のイテレータ。regex_iterator<> を参照はがしすると match_results<> が返る。regex_iterator<> をインクリメントすると次のマッチを検索する。

regex_token_iterator<>

regex_iterator<> と似ているが、regex_token_iterator<> を参照はがしすると文字列が返る。既定では正規表現にマッチした部分文字列全体が返るが、一度にいずれかあるいはすべてのマーク済み部分式を 1 つずつ返すように設定することもできる。また、文字列の正規表現にマッチしなかった部分を返すよう設定することもできる。

regex_compiler<>

basic_regex<> オブジェクトのファクトリ。文字列を正規表現に「コンパイル」する。basic_regex<> クラスは内部で regex_compiler<> を使用するファクトリメソッドをもっているので、大抵の場合 regex_compiler<> を直接取り扱う必要はない。しかし、basic_regex<> オブジェクトを異なる std::locale で作成するなど変わったことをする必要がある場合は、regex_compiler<> を明示的に使用しなければならない。

xpressive が提供するツール群について少しは分かったと思う。次の 2 つの質問に答えれば正しいツールを選択できるだろう。

  1. データを走査するのに使うイテレータの型は何か。

  2. データを使って何をしたいのか。

イテレータの型

xpressive において、ほとんどのクラスはイテレータ型を引数にもつテンプレートである。正しい型を簡単に選択できるように xpressive は共通の typedef をいくつか定義している。以下の表を見ればイテレータ型から正しい型が分かる。

xpressive の typedef とイテレータ型の対応

std::string::const_iterator

char const *

std::wstring::const_iterator

wchar_t const *

basic_regex

sregex

cregex

wsregex

wcregex

match_results

smatch

cmatch

wsmatch

wcmatch

regex_compiler

sregex_compiler

cregex_compiler

wsregex_compiler

wcregex_compiler

regex_iterator

sregex_iterator

cregex_iterator

wsregex_iterator

wcregex_iterator

regex_token_iterator

sregex_token_iterator

cregex_token_iterator

wsregex_token_iterator

wcregex_token_iterator

機械的な名前付け規約に注意していただきたい。これらの型の多くは一緒に使用するため、名前付け規約は一貫性という点で助けになる。例えば sregex があれば一緒に使うのは smatch という具合である。

これら 4 つのイテレータ型以外については、テンプレートを直接使用しイテレータ型を指定するとよい。

タスク

パターンを使うのは 1 度か、複数回か。検索か置換か。xpressive はこれらをすべてカバーし、他にも多くの機能がある。以下が早見表である。

処理とツール

次を行うには…

以下を使用せよ

regex_match アルゴリズム

regex_search アルゴリズム

regex_replace アルゴリズム

regex_iterator<> クラス

regex_token_iterator<> クラス

regex_token_iterator<> クラス

これらのアルゴリズムとクラスの厄介な詳細はリファレンスの節で述べる。

ちなみに

上の表の各処理をクリックすると、xpressive を使った完全なプログラム例が表示される。

正規表現オブジェクトの作成

xpressive を使う場合、最初に行うのが basic_regex<> オブジェクトの作成である。本節では静的・動的の 2 つの表現方法による正規表現作成の基本を見ていく。

静的正規表現

概要

xpressive が他の C/C++ 正規表現ライブラリと一線を画すのは、C++ の式を用いて正規表現を記述する機能による。xpressive は演算子の多重定義と式テンプレートという技術を使って、パターンマッチのための小言語を C++ に組み込むことでこれを実現している。これら「静的正規表現」には文字列ベースのものに比較して多くの利点がある。特に以下の点を挙げておく。

  • コンパイル時に構文がチェックされる。実行時に構文エラーで失敗することがない。

  • 他の C++ データ、コード、他の正規表現を自然に参照できる。正規表現の外部での文法構築、および正規表現マッチの一部として実行されるユーザー定義アクションの束縛が簡単になる。

  • 静的束縛され、インライン化と最適化が促進される。静的正規表現は状態表、仮想関数、バイトコード、関数ポインタによる呼び出しといったコンパイル時に解決できないものを必要としない。

  • 検索対象が文字列に限定されない。例えば、数値配列からパターンを探索する静的正規表現を宣言できる。

  • 静的正規表現の組み立ては C++ の式を使うので、合法な C++ の式規則の制約を受ける。残念ながら、「伝統的な」正規表現構文をすべてきれいに C++ に対応させられるわけではない。そういうわけで、無理な対応は試みず C++ として合法な構文を用意する。

構築と代入

静的正規表現の作成は、basic_regex<> 型のオブジェクトへの代入により行う。例えば、以下は std::string 型のオブジェクトに対してパターンを探索する正規表現を定義する。

sregex re = '$' >> +_d >> '.' >> _d >> _d;

代入の動作も似たようなものである。

文字と文字列リテラル

静的正規表現において、文字と文字列リテラルはそれ自身にマッチする。例えば上の正規表現において '$''.' は、それぞれ文字 '$''.' にマッチする。Perlにおいて $. がメタ文字であるからといって混乱しないでいただきたい。xpressive ではリテラルは常にそれ自身を表す。

静的正規表現でリテラルを使用する場合は、少なくとも片方のオペランドはリテラル以外であることに注意しなければならない。例えば以下は正しい正規表現ではない

sregex re1 = 'a' >> 'b';         // エラー!
sregex re2 = +'a';               // エラー!

二項 >> 演算子の2つのオペランドが両方ともリテラル、また単項 + 演算子のオペランドもリテラルになっている。よってこれらの文は組み込みの C++ 二項右シフト、単項プラス演算子をそれぞれ呼び出す。これは期待した動作ではない。演算子の多重定義が機能するには、少なくとも 1 つのオペランドがユーザー定義型でなければならない。xpressive の as_xpr ヘルパ関数を使うと式を正規表現の世界に「引き込み」、演算子の多重定義に正しい演算子を見つけるよう強制できる。上の 2 つは次のように書くべきだ。

sregex re1 = as_xpr('a') >> 'b'; // OK
sregex re2 = +as_xpr('a');       // OK

結合と選択

すでに見てきたように、静的正規表現における部分式は結合演算子 >> で分離されていなければならない。この演算子は「~の後に」などと読み替えるとよい。 3

// 後ろに数字が続く 'a' にマッチ
sregex re = 'a' >> _d;

選択(分岐)は | 演算子を使用する。Perl と同様の動作をする。この演算子は「または」などと読み替えるとよい。例えば、

// 1 文字以上の数字、または単語構成文字にマッチ
sregex re = +( _d | _w );

グループ化と捕捉

Perl では括弧 () は特別な意味をもつ。これらはグループ化を行うが、$1$2 といった後方参照を作成するという副作用がある。C++ では括弧を多重定義して副作用を与えることはできない。そこで同じ効果を得るために s1s2 という特殊なトークンを使用する。これらに代入を行うことで後方参照を作成する。後方参照は Perl の \\1\\2 のような使い方で式中で使用できる。例えば以下の HTML タグのマッチを探索する正規表現を考えよう。

"<(\\w+)>.*?</\\1>"

静的正規表現では、次のようになる。

'<' >> (s1= +_w) >> '>' >> -*_ >> "</" >> s1 >> '>'

s1 への代入により後方参照を捕捉し、パターンの後ろのほうでマッチする終了タグを探索するのに s1 を使っていることに注意していただきたい。

ちなみに

後方参照を捕捉せずにグループ化けを行う

  • xpressive では、後方参照を捕捉せずにグループ化を行うには s1 なしで () を使うだけでよい。これは Perl の捕捉なしのグループ化構造 (?:) と等価である。

3

訳注 原文は “followed by”。無理に日本語にしないほうがいいかもしれません…。

動的正規表現

概要

静的正規表現は一級品だが、ときにはもっと別の…、つまり動的正規表現が必要な場合もある。正規表現検索・置換機能を備えたテキストエディタを開発中だとしよう。正規表現は、実行時にエンドユーザーからの入力として受け付けなければならない。文字列を正規表現に解析する方法が必要であり、xpressive の動的正規表現がそれに相当する。これらは静的正規表現と同じコアコンポーネントから構築するが、遅延束縛のため実行時にパターンを指定できる。

構築と代入

動的正規表現を作成する方法は2つある。basic_regex::compile 関数によるものと regex_compiler<> クラステンプレートによるものである。既定のロカールでよければ basic_regex::compile を使うとよい。別のロカールを指定する必要がある場合は、regex_compiler<> を使用する。正規表現文法の節で、regex_compiler<> の他の使用について述べる。

以下は basic_regex::compile の使用例である。

sregex re = sregex::compile( "this|that", regex_constants::icase );

以下は regex_compiler<> を使った同じ例である。

sregex_compiler compiler;
sregex re = compiler.compile( "this|that", regex_constants::icase );

basic_regex::compileregex_compiler を使って実装している。

動的 xpressive の構文

動的構文は合法な C++ の式規則による制約を受けないので、動的正規表現については慣れ親しんだ構文が使える。そういうわけで動的正規表現については xpressive は、正規表現を標準ライブラリに追加することになった John Maddock の草案に従った。本質的には ECMAScript により標準化された構文であり、国際化のための細かい変更を加えてある。

構文の網羅的な文書は他にあるので、ここでは仕様の複製はせず、既存の標準を参照するにとどめる。

国際化

静的正規表現と同様、動的正規表現の国際化サポートは別の std::locale を指定することによる。これを行うには regex_compiler<> を使用しなければならない。regex_compiler<> クラスは imbue 関数をもつ。regex_compiler<> オブジェクトに対してカスタムの std::locale を使って imbue を呼び出すと、それ以降に regex_compiler<> でコンパイルした正規表現オブジェクトはそのロカールを使用するようになる。例えば、

std::locale my_locale = /* ここでロカールオブジェクトを初期化する */;
sregex_compiler compiler;
compiler.imbue( my_locale );
sregex re = compiler.compile( "\\w+|\\d+" );

この正規表現は、組み込みの文字集合 \w および \d を処理するのに my_locale を使用する。

マッチと検索

概要

正規表現オブジェクトの作成が終わったら、regex_match および regex_search アルゴリズムで文字列からパターンを検索する。本節では正規表現のマッチと検索の基本について述べる。Boost.Regex ライブラリの regex_match および regex_search の振る舞いについて理解しているなら、xpressive 版でも同様の動作をすると考えてよい。

文字列が正規表現にマッチするか調べる

regex_match アルゴリズムは正規表現が与えられた入力にマッチするか調べる。

警告

regex_match アルゴリズムは、正規表現が入力全体の先頭から終端までマッチした場合のみ成功する。正規表現が入力の一部分だけにマッチする場合は regex_match は偽を返す。文字列から正規表現にマッチする部分文字列を探す場合は、regex_search アルゴリズムを使うとよい。

入力は std::string、C 形式の null 終端文字列、イテレータの組といった双方向範囲である。いずれの場合でも、入力シーケンスを走査するイテレータ型は正規表現オブジェクトの宣言に使用したイテレータ型と一致していなければならない(イテレータに対する正しい正規表現の型は、クイックスタートの表を見れば分かる)。

cregex cre = +_w;  // C 形式の文字列にマッチ
sregex sre = +_w;  // std::string にマッチ

if( regex_match( "hello", cre ) )              // OK
    { /*...*/ }

if( regex_match( std::string("hello"), sre ) ) // OK
    { /*...*/ }

if( regex_match( "hello", sre ) )              // エラー! イテレータが一致していない!
    { /*...*/ }

regex_match アルゴリズムは省略可能な出力引数として match_results<> 構造体を受け付ける。この引数が与えられると、regex_match アルゴリズムは正規表現のどの部分が入力のどの部分にマッチしたかの情報を match_results<> 構造体に書き込む。

cmatch what;
cregex cre = +(s1= _w);

// regex_match の結果を "what" に格納する
if( regex_match( "hello", what, cre ) )
{
    std::cout << what[1] << '\n'; // "o" を印字する
}

regex_match アルゴリズムはさらに省略可能な引数として match_flag_type ビットマスクを受け付ける。match_flag_type を与えると、マッチをどのように行うかある程度制御できる。このフラグの完全なリストと意味については match_flag_type のリファレンスを見よ。

std::string str("hello");
sregex sre = bol >> +_w;

// match_not_bol の意味は、「"bol"(行頭)は [begin,begin) にマッチしない」
if( regex_match( str.begin(), str.end(), sre, regex_constants::match_not_bol ) )
{
    // ここには絶対にこない!
}

regex_match の使い方に関する完全なプログラム例はここにある。利用可能な多重定義の完全なリストは regex_match のリファレンスを見よ。

部分文字列のマッチを検索する

入力シーケンスに正規表現にマッチする部分シーケンスが含まれているか調べるには regex_search を使用する。regex_search は入力シーケンスの先頭で正規表現マッチを試行し、マッチを見つけるかシーケンスの終端に到達するまでシーケンスを走査する。

その他のすべての面で regex_search の動作は regex_match と似たようなものである(上を見よ)。std::string 、C 形式の null 終端文字列、イテレータの範囲といった双方向範囲を取り扱うという点が特にそうである。正規表現のイテレータ型と入力シーケンスの型を一致させなければならない、ということについても同様の注意が必要である。regex_match と同様、match_results<> 構造体を与えて検索結果を受け取ったり、match_flag_type ビットマスクを使ってマッチをどのように行うかを制御できる。

regex_search の使い方に関する完全なプログラム例はここにある。利用可能な多重定義の完全なリストは regex_search のリファレンスを見よ。

結果へのアクセス

概要

regex_match および regex_search の成否が分かるだけでは十分でない場合もある。regex_matchregex_searchmatch_results<> 型のオブジェクトを渡すと、アルゴリズムが完全に成功した後 match_results<> に、正規表現のどの部分がシーケンスのどの部分にマッチしたかの追加情報が入る。Perl ではこれらの部分シーケンスを後方参照といい、変数 $1$2 、…に格納される。xpressive では sub_match<> 型のオブジェクトであり、match_results<> 構造体に格納される。これらは sub_match<> オブジェクトのベクタとして振舞う。

match_results

さて、正規表現アルゴリズムに match_results<> オブジェクトを渡し、アルゴリズムが成功したとする。結果を調べたくなるところだ。match_results<> オブジェクトを使ってすることといえば、その内部に格納されている sub_match<> オブジェクトへ添字を介してアクセスすることがほとんどである。しかし match_results<> オブジェクトには他にも少し使い道がある。

what という名前の match_results<> オブジェクトに格納されている情報にアクセスする方法を以下の表に示す。

match_results<> のアクセス子

アクセス子

効果

what.size()

部分マッチの総数を返す。マッチ全体は 0 番目の部分マッチとして格納されるため、アルゴリズムが成功した場合は結果は常に 0 より大きい。

what[n]

n 番目の部分マッチを返す。

what.length(n)

n 番目の部分マッチの長さを返す。what[n].length() と同じ。

what.str(n)

n 番目の部分マッチから構築した std::basic_string<> を返す。what[n].str() と同じ。

what.prefix

入力シーケンスの先頭から全体マッチ先頭までの部分シーケンスを表す sub_match<> オブジェクトを返す。

what.suffix

全体マッチの終端から入力シーケンスの終端までの部分シーケンスを表す sub_match<> オブジェクトを返す。

what.regex_id()

この match_results<> オブジェクトで最後に使用した basic_regex<> オブジェクトの regex_id を返す。

match_results<> オブジェクトには他にも使い道があるが、文法と入れ子マッチの項であらためて述べることにする。

sub_match

match_results<> オブジェクトに添字を介してアクセスすると sub_match<> オブジェクトが得られる。sub_match<> は基本的にはイテレータの組である。定義は以下のようになっている。

template< class BidirectionalIterator >
struct sub_match
    : std::pair< BidirectionalIterator, BidirectionalIterator >
{
    bool matched;
    // ...
};

std::pair<> を公開継承しているため、sub_match<>BidirectionalIterator 型の first および second データメンバをもつ。これらは、この sub_match<> が表す部分シーケンスの先頭と終端である。また sub_match<> は論理型の matched データメンバをもち、この sub_match<> が完全マッチに関与する場合に真となる。

名前を sub とした場合の、sub_match<> オブジェクトに格納されている情報にアクセスする方法を以下の表に示す。

sub_match<> アクセス子

アクセス子

効果

sub.length()

部分マッチの長さを返す。std::distance(sub.first, sub.second) と同じ。

sub.str()

部分マッチから構築した std::basic_string<> を返す。std::basic<char_type>(sub.first, sub.second) と同じ。

sub.compare(str)

部分マッチと str の文字列比較を行う。strstd::basic_string<> 、C 形式の null 終端文字列、別の部分マッチのいずれでもよい。sub.str().compare(str) と同じ。

効果の無効化

結果は入力シーケンス内のイテレータとして格納される。入力シーケンスが無効になるとマッチ結果もまた無効となる。例えば std::string オブジェクトに対してマッチを行った場合、結果が有効なのは、次にその std::string オブジェクトの非 const メンバ関数を呼び出すまでの間だけである。それ以降は match_results<> オブジェクトに格納されている結果は無効となるため、使用してはならない。

文字列の置換

正規表現が威力を発揮するのはテキスト検索のときだけではない。テキストの操作においても有効である。最もありふれたテキスト操作の 1 つが、「検索して置換」である。xpressive は検索と置換のために regex_replace アルゴリズムを提供する。

regex_replace()

regex_replace を用いた「検索して置換」処理は簡単である。必要なのは入力シーケンス、正規表現オブジェクト、および書式化文字列か書式化オブジェクトだけである。regex_replace には複数のバージョンがあり、入力シーケンスを std::string のような双方向コンテナとして受け付けて結果を同じ型の新しいコンテナで返すものや、入力を null 終端文字列で受け付けて std::string を返すもの、イテレータの組で受け付けて結果を出力イテレータに書き込むものがある。置換は書式化シーケンスを含む文字列か書式化オブジェクトで指定する。文字列ベースの置換について、単純な使用例を以下に示す。

std::string input("This is his face");
sregex re = as_xpr("his");                // "his" をすべて検索し、...
std::string format("her");                // ... "her" で置換する

// regex_replace() の対文字列版を使用
std::string output = regex_replace( input, re, format );
std::cout << output << '\n';

// regex_replace() の対イテレータ版を使用
std::ostream_iterator< char > out_iter( std::cout );
regex_replace( out_iter, input.begin(), input.end(), re, format );

上のプログラムは以下を印字する。

Ther is her face
Ther is her face

"his"すべて "her" に置換されることに注意していただきたい。

regex_replace の使い方に関する完全なプログラム例はここにある。利用可能な多重定義の完全なリストは regex_replace のリファレンスを見よ。

置換のオプション

regex_replace アルゴリズムは、省略可能なビットマスク引数により書式化を制御する。使用可能なビットマスク値を以下に示す。

書式化フラグ

フラグ

意味

format_default

ECMA-262 の書式化シーケンスを使用する(後述)。

format_first_only

すべてのマッチの中で最初のものだけを置換する。

format_no_copy

入力シーケンス内の、正規表現にマッチしなかった部分を出力シーケンスにコピーしない。

format_literal

書式化文字列をリテラル(即値)として扱う。エスケープシーケンスを一切解釈しなくなる。

format_perl

Perl の書式化シーケンスを使用する(後述)。

format_sed

sed の書式化シーケンスを使用する(後述)。

format_all

Perl の書式化シーケンス、および Boost 固有の書式化シーケンスを使用する。

これらのフラグは xpressive::regex_constants 名前空間内にある。置換の引数が文字列ではなく関数オブジェクトである場合は、format_literalformat_perlformat_sed および format_all は無視される。

ECMA-262 書式化シーケンス

上記のフラグを指定せずに書式化文字列を渡した場合は、ECMAScript の標準である ECMA-262 の定義が使われる。ECMA-262 モードで使用するエスケープシーケンスを以下に示す。

書式化エスケープシーケンス

エスケープシーケンス

意味

$1$2 、…

部分マッチ

&

マッチ全体

$`

マッチの前

$'

マッチの後

$$

リテラルの文字 '$'

その他、$ で始まるシーケンスは、単純にそれ自身を表す。例えば書式化文字列が $a であれば、出力シーケンスに「$a」が挿入される。

sed 書式化シーケンス

regex_replaceformat_sed フラグを指定した場合に使用するエスケープシーケンスを以下に示す。

sed 書式化エスケープシーケンス

エスケープシーケンス

意味

12 、…

部分マッチ

&

マッチ全体

a

リテラルの 'a'

e

リテラルの char_type(27)

f

リテラルの 'f'

n

リテラルの 'n'

r

リテラルの 'r'

t

リテラルの 't'

v

リテラルの 'v'

xFF

リテラルの char_type(0xFF)F は 16 進数字

x{FFFF}

リテラルの char_type(0xFFFF)F は 16 進数字</td>

cX

制御文字 X

Perl 書式化シーケンス

regex_replaceformat_perl フラグを指定した場合に使用するエスケープシーケンスを以下に示す。

Perl 書式化エスケープシーケンス

エスケープシーケンス

意味

$1$2 、…

部分マッチ

&

マッチ全体

$`

マッチの前

$'

マッチの後

$$

リテラルの '$'

a

リテラルの 'a'

e

リテラルの char_type(27)

f

リテラルの 'f'

n

リテラルの 'n'

r

リテラルの 'r'

t

リテラルの 't'

v

リテラルの 'v'

xFF

リテラルの char_type(0xFF)F は 16 進数字

x{FFFF}

リテラルの char_type(0xFFFF)F は 16 進数字

cX

制御文字 X

l

次の文字を小文字にする

L

次に \E が現れるまで残りの置換を小文字にする

u

次の文字を大文字にする

U

次に \E が現れるまで残りの置換を大文字にする

E

\L\U の効果を終了する

12 、…

部分マッチ

g<name>

名前付き後方参照 name

Boost 固有の書式化シーケンス

regex_replaceformat_all を指定した場合に使用するエスケープシーケンスは上に挙げた format_perl と同じである。さらに以下の形式の条件式を使用する。

?Ntrue-expression:false-expression

N は部分マッチを表す 10 進数字である。この部分マッチがマッチ全体に含まれる場合は置換は true-expression となり、それ以外の場合は false-expression となる。このモードでは括弧 () でグループ化を行う。リテラルの括弧は ( のようにエスケープが必要である。

書式化オブジェクト

テキスト置換において、書式化文字列の表現能力が常に十分とは限らない。入力文字列を環境変数で置換して出力文字列にコピーする単純な例を考えよう。こういう場合は、書式化文字列ではなく書式化オブジェクトを使ったほうがよい。次のコードを考えよう。$(xyz) の形式で埋め込まれた環境変数を検索し、辞書に照らし合わせて見つかった置換文字列を算出する。

#include <map>
#include <string>
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
using namespace boost;
using namespace xpressive;

std::map<std::string, std::string> env;

std::string const &format_fun(smatch const &what)
{
    return env[what[1].str()];
}

int main()
{
    env["X"] = "this";
    env["Y"] = "that";

    std::string input("\"$(X)\" has the value \"$(Y)\"");

    // "$(XYZ)" のような文字列を検索し、env["XYZ"] の結果で置換する
    sregex envar = "$(" >> (s1 = +_w) >> ')';
    std::string output = regex_replace(input, envar, format_fun);
    std::cout << output << std::endl;

    return 0;
}

この場合、関数 format_fun を使って置換文字列をその場で算出している。この関数は現在のマッチ結果が入った match_results<> オブジェクトを受け取る。format_fun は「1 番目の部分マッチ」をグローバルな env 辞書のキーに使っている。上記コードは次を表示する。

"this" has the value "that"

書式化オブジェクトは単純な関数である必要はなく、クラス型のオブジェクトでもよい。また文字列を返す以外に、出力イテレータに置換結果を書き込んでもよい。以下は上記と機能的に等価なコードである。

#include <map>
#include <string>
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
using namespace boost;
using namespace xpressive;

struct formatter
{
    typedef std::map<std::string, std::string> env_map;
    env_map env;

    template<typename Out>
    Out operator()(smatch const &what, Out out) const
    {
        env_map::const_iterator where = env.find(what[1]);
        if(where != env.end())
        {
            std::string const &sub = where->second;
            out = std::copy(sub.begin(), sub.end(), out);
        }
        return out;
    }

};

int main()
{
    formatter fmt;
    fmt.env["X"] = "this";
    fmt.env["Y"] = "that";

    std::string input("\"$(X)\" has the value \"$(Y)\"");

    sregex envar = "$(" >> (s1 = +_w) >> ')';
    std::string output = regex_replace(input, envar, fmt);
    std::cout << output << std::endl;
}

書式化オブジェクトは、シグニチャが以下の表に示す 3 種類のどれか 1 つである呼び出し可能オブジェクト(関数か関数オブジェクト)でなければならない。表中の fmt は関数ポインタか関数オブジェクト、whatmatch_results<> オブジェクト、out は OutputIterator 、flagsregex_constants::match_flag_type の値である。

書式化オブジェクトのシグニチャ

書式化オブジェクトの呼び出し

戻り値の型

意味

fmt(what)

文字の範囲(std::string など)か null 終端文字列

正規表現にマッチした文字列を書式化オブジェクトが返した文字列で置換する。

fmt(what, out)

OutputIterator

書式化オブジェクトは置換文字列を out に書き込み、out を返す。

fmt(what, out, flags)

OutputIterator

書式化オブジェクトは置換文字列を out に書き込み、out を返す。flags 引数は regex_replace アルゴリズムに渡したマッチフラグの値。

書式化式

書式化文字列、書式化オブジェクトに加えて、regex_replace は書式化も受け付ける。書式化式は文字列を生成するラムダ式である。使用する構文は後述する意味アクションと同じである。文字列を regex_replace を用いて環境変数で置換する上の例を書式化式を使って書き直すと、次のようになる。

#include <map>
#include <string>
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
#include <boost/xpressive/regex_actions.hpp>
using namespace boost::xpressive;
int main()
{
    std::map<std::string, std::string> env;
    env["X"] = "this";
    env["Y"] = "that";

    std::string input("\"$(X)\" has the value \"$(Y)\"");

    sregex envar = "$(" >> (s1 = +_w) >> ')';
    std::string output = regex_replace(input, envar, ref(env)[s1]);
    std::cout << output << std::endl;

    return 0;
}

上のコードの ref(env)[s1] が書式化式で、1 番目の部分マッチの値 s1 を辞書 env のキーとするという意味となる。ここで xpressive::ref を使っているのは、ローカル変数 env への参照を遅延して s1 の置換対象が判明するまで添字演算を遅らせるためである。

文字列の分割とトークン分割

regex_token_iterator<> はテキスト操作の世界における GINSU 4 のナイフである。薄切りもさいの目切りも思いのまま! 本節では高度に設定可能な regex_token_iterator で入力シーケンスを分割する方法を述べる。

概要

regex_token_iterator<> は入力シーケンス、正規表現、省略可能な設定引数で初期化する。regex_token_iterator<>regex_search を使って、シーケンス内で最初に正規表現にマッチする位置を見つける。regex_token_iterator<> を参照はがしすると、std::basic_string 形式でトークンを返す。どの文字列を返すかは設定引数による。既定ではマッチ全体に相当する文字列を返すが、マーク済み部分式のみならずシーケンス内のマッチしなかった部分を返すことも可能である。regex_token_iterator<> をインクリメントすると次のトークンに移動する。次がどのトークンかは設定引数による。単純に現在のマッチにおける異なるマーク済み部分式の場合もあれば、次のマッチの全体か一部分である場合、マッチしなかった部分である場合もある。

以上のことからわかるように、regex_token_iterator<> には多くの機能がある。すべてを説明するのは難しいが、いくつか例を見れば理解できるだろう。

例 1:単純なトークン分割

この例では regex_token_iterator<> を使ってシーケンスを単語のトークンに切っている。

std::string input("This is his face");
sregex re = +_w;                      // 単語を検索する

// 入力中の単語をすべて走査する
sregex_token_iterator begin( input.begin(), input.end(), re ), end;

// すべての単語を std::cout に出力する
std::ostream_iterator< std::string > out_iter( std::cout, "\n" );
std::copy( begin, end, out_iter );

このプログラムは以下を表示する。

This
is
his
face

例 2:単純なトークン分割・リローデッド

この例も regex_token_iterator を使ってシーケンスを単語トークンに切っているが、正規表現を区切りとして使っている。regex_token_iterator コンストラクタの最後の引数に -1 を渡すと、入力内の正規表現にマッチしなかった部分がトークンとなる。

std::string input("This is his face");
sregex re = +_s;                      // 空白を検索する

// 入力中の非空白をすべて走査する。-1 に注意
sregex_token_iterator begin( input.begin(), input.end(), re, -1 ), end;

// すべての単語を std::cout に出力する
std::ostream_iterator< std::string > out_iter( std::cout, "\n" );
std::copy( begin, end, out_iter );

このプログラムは以下を出力する。

This
is
his
face

例 3:単純なトークン分割・レボリューションズ 5

この例も regex_token_iterator<> を使って日付の束が入ったシーケンスを年だけのトークンに切っている。regex_token_iterator コンストラクタの最後の引数に正の整数 N を渡すと、各マッチの N 番目のマーク済み部分式のみがトークンとなる。

std::string input("01/02/2003 blahblah 04/23/1999 blahblah 11/13/1981");
sregex re = sregex::compile("(\\d{2})/(\\d{2})/(\\d{4})"); // 日付を検索する

// 入力中のすべての年を走査をする。3(3 番目の部分式)に注意
sregex_token_iterator begin( input.begin(), input.end(), re, 3 ), end;

// すべての単語を std::cout に出力する
std::ostream_iterator< std::string > out_iter( std::cout, "\n" );
std::copy( begin, end, out_iter );

このプログラムは以下を出力する。

2003
1999
1981

例 4:あまり単純でないトークン分割

この例は 1 つ前のものと似ているが、年だけでなく月と日をトークンに入れている点が異なる。regex_token_iterator<> コンストラクタの最後の引数に整数の配列 {I,J,...} を渡すと、各マッチの I 番目、J 番目、…のマーク済み部分式がトークンとなる。

std::string input("01/02/2003 blahblah 04/23/1999 blahblah 11/13/1981");
sregex re = sregex::compile("(\\d{2})/(\\d{2})/(\\d{4})"); // 日付を検索する

// 入力中の年月日を走査する
int const sub_matches[] = { 2, 1, 3 }; // 日、月、年
sregex_token_iterator begin( input.begin(), input.end(), re, sub_matches ), end;

// すべての単語を std::cout に出力する
std::ostream_iterator< std::string > out_iter( std::cout, "\n" );
std::copy( begin, end, out_iter );

このプログラムは以下を出力する。

02
01
2003
23
04
1999
13
11
1981

sub_matches 配列により、regex_token_iterator<> は最初に 2 番目の部分マッチ、次に 1 番目の部分マッチ、最後に 3 番目の部分マッチの値を取る。イテレータをインクリメントすると regex_search を使って次のマッチを検索する。ここで処理が繰り返され、イテレータは 2 番目の部分マッチを取り、次に 1 番目…となる。

4

訳注 刃物メーカー(http://www.genuineginsu.com/)。GINSU のナイフはよく切れると評判らしいです。Wikipedia によるとテレビ CM が画期的なものだったとか。

5

訳注 マトリックスですね。

名前付き捕捉

概要

正規表現が複雑になると、番号付き捕捉を取り扱うのが苦痛になる場合がある。左括弧の数を数えてどの捕捉に対応しているのか調べるのはつまらない仕事である。さらに面白くないのは、正規表現を編集するだけで捕捉に新しい番号が割り振られて古い番号を使っていた後方参照が無効になることである。

他の正規表現エンジンでは、名前付き捕捉という機能でこの問題を解決している。この機能を使うと捕捉に名前を付けることができ、番号ではなく名前で捕捉を後方参照できる。xpressive も動的・静的正規表現の両方で名前付き捕捉をサポートする。

動的名前付き捕捉

動的正規表現については、xpressive は他の一般的な正規表現エンジンの名前付き捕捉の構文に従う。(?P<xxx>...) で名前付き捕捉を作成し、(?P=xxx) でこの捕捉を後方参照する。名前付き後方参照を作成し後方参照する例を以下に示す。

// 1 文字にマッチする "char" という名前付き捕捉を作成し、名前により後方参照する。
sregex rx = sregex::compile("(?P<char>.)(?P=char)");

上の正規表現は同じ文字が 2 つ続いた部分を検索する。

名前付き捕捉を使ってマッチか検索を行った後、捕捉の名前を使って match_results<> により名前付き捕捉にアクセスする。

std::string str("tweet");
sregex rx = sregex::compile("(?P<char>.)(?P=char)");
smatch what;
if(regex_search(str, what, rx))
{
    std::cout << "char = " << what["char"] << std::endl;
}

上のコードは以下を表示する。

char = e

名前付き捕捉を置換文字列から後方参照することも可能である。\\g<xxx> という構文である。文字列置換において名前付き捕捉を使用する例を以下に示す。

std::string str("tweet");
sregex rx = sregex::compile("(?P<char>.)(?P=char)");
str = regex_replace(str, rx, "**\\g<char>**", regex_constants::format_perl);
std::cout << str << std::endl;

名前付き捕捉を使用するには format_perl を指定しなければならないことに注意していただきたい。\\g<xxx> 構文を解釈するのは Perl の構文だけである。上のコードは以下を表示する。

tw**e**t

静的名前付き捕捉

静的正規表現を使う場合は、名前付き捕捉の作成と使用はより簡単である。mark_tag 型を使って s1s2 のような変数を作成するが、より意味のある名前を与えることができる。静的表現を使うと上の例は以下のようになる。6

mark_tag char_(1); // char_ は s1 の別名となる
sregex rx = (char_= _) >> char_;

マッチを行った後、mark_tagmatch_results<> の添字にして名前付き捕捉にアクセスする。

std::string str("tweet");
mark_tag char_(1);
sregex rx = (char_= _) >> char_;
smatch what;
if(regex_search(str, what, rx))
{
    std::cout << what[char_] << std::endl;
}

上のコードは以下を表示する。

char = e

regex_replace を使って文字列置換を行う場合、以下のように名前付き捕捉を使用して書式化式を作成できる。

std::string str("tweet");
mark_tag char_(1);
sregex rx = (char_= _) >> char_;
str = regex_replace(str, rx, "**" + char_ + "**");
std::cout << str << std::endl;

上のコードは以下を表示する。

tw**e**t

注釈

書式化式を使用するには <boost/xpressive/regex_actions.hpp> をインクルードしなければならない。

6

訳注 リファレンスの項にあるとおり、mark_tag の初期化に使用する整数は正規表現内で一意でなければなりません。

文法と入れ子マッチ

概要

正規表現を C++ の式で表現することの重要な利点の 1 つは、正規表現中から他の C++ コードやデータに容易にアクセスできることである。これにより、他の正規表現で不可能なプログラミングイディオムが可能になる。特に注意していただきたいのは、正規表現が他の正規表現を参照する機能で、これにより正規表現の外部で文法を構築できる。この節では正規表現を他の正規表現に値や参照で組み込む方法、正規表現が他の正規表現を参照したときの振る舞い、解析が成功した後の結果木にアクセスする方法を説明する。

値による正規表現の組み込み

basic_regex<> オブジェクトは値のセマンティクスをもつ。正規表現オブジェクトが別の正規表現定義の右辺に現れると、値による組み込みが起こるとみなされる。つまり、入れ子の正規表現のコピーが外側の正規表現に格納される。内側の正規表現は、パターンマッチ時に外側の正規表現により呼び出される。内側の正規表現をマッチに対して完全に消耗すると、マッチを成功させるためにバックトラックが起こる。

単語単位の正規表現検索機能をもつテキストエディタを考える。これを xpressive で実装すると次のようになる。

find_dialog dlg;
if( dialog_ok == dlg.do_modal() )
{
    std::string pattern = dlg.get_text();          // ユーザーが入力したパターン
    bool whole_word = dlg.whole_word.is_checked(); // ユーザーが単語単位のオプションを選択したか?

    sregex re = sregex::compile( pattern );        // パターンのコンパイル

    if( whole_word )
    {
        // 正規表現を単語の先頭、単語の終端表明で囲む
        re = bow >> re >> eow;
    }

    // ... re を使う ...
}

この行に注目する。

// 正規表現を単語の先頭、単語の終端表明で囲む
re = bow >> re >> eow;

この行は既存の正規表現を値で組み込んだ正規表現を新たに作成し、元の正規表現に代入している。元の正規表現のコピーが右辺にあるので、これは期待したとおりに動作する。つまり、新しい正規表現の振る舞いは元の正規表現を単語先頭と単語終端の表明で囲んだものとなる。

注釈

既定では正規表現オブジェクトは値で組み込まれるため、re = bow >> re >> eow は再帰正規表現を定義しないことに注意していただきたい。次の節では、正規表現を参照で組み込んで再帰正規表現を定義する方法を述べる。

参照による正規表現の組み込み

再帰正規表現および文脈自由文法を構築するには、値による正規表現の組み込みでは不十分である。正規表現を自己参照的にする必要がある。大半の正規表現エンジンにはそういった能力はないが、xpressive では可能である。

ちなみに

理論コンピュータ科学者は、自己参照的な正規表現は「正規(正則)」ではないと指摘するかもしれない。そういう意味では、厳密には xpressive は本当は正規表現エンジンではない。しかし Larry Wall がかつてこう言ったことがある。「項 [regular expression] は我々のパターンマッチエンジンとともに成長した。言語の必要性と戦うつもりはない。」

次のコードを考える。by_ref ヘルパを使って、数の合った入れ子の括弧にマッチする再帰正規表現を定義している。

sregex parentheses;
parentheses                          // 数の合った括弧群は...
    = '('                            // 最初に 1 つの開き括弧があり...
        >>                           // その後ろに...
         *(                          // 0 か 1 つ以上の...
            keep( +~(set='(',')') )  // 括弧以外のものの塊か...
          |                          // あるいは...
            by_ref(parentheses)      // 数の合った括弧群があり
          )                          //   (これだ、再帰している!)...
        >>                           // その後ろに...
      ')'                            // 1 つの閉じ括弧がある
    ;

数の合った入れ子のタグに対するマッチは重要なテキスト処理であり、「旧式の」正規表現では不可能なことの 1 つである。by_ref ヘルパがこれを可能にする。これによりある正規表現を別の正規表現から参照により組み込むことができる。右辺が parentheses を参照で保持しているので、parentheses に右辺を代入すると循環が生まれ再帰的に実行される。

文法の構築

正規表現が自己再帰的になりさえすれば、もう後戻りする必要はない。楽しみにしていたことがすべて可能になる。特に正規表現の外部で文法を構築できるようになる。text-book 文法の例を見よう。ちょっとした計算機だ。

sregex group, factor, term, expression;

group       = '(' >> by_ref(expression) >> ')';
factor      = +_d | group;
term        = factor >> *(('*' >> factor) | ('/' >> factor));
expression  = term >> *(('+' >> term) | ('-' >> term));

上で定義した正規表現 expression は正規表現としては非常に注目すべき動作をする。数式にマッチするのである。例えば入力文字列が foo 9*(10+3) bar であれば、このパターンは 9*(10+3) にマッチする。この正規表現がマッチするのは正しい形式の数式、つまり括弧の数が合っており、中置演算子が引数を2つもつ場合のみである。他の正規表現エンジンでこれを試してはいけませんぞ!

この正規表現文法をもっとよく見てみよう。循環していることに注意していただきたい。expressionterm を使って実装してあり、termfactor を使って実装してある。factorgroup を使って実装してあり、groupexpression を使って実装してある。というわけでループが閉じている。大抵の場合、循環文法の定義は正規表現オブジェクトの前方宣言とこれら未初期化の正規表現の参照による組み込みにより行う。上の文法では、未初期化の正規表現オブジェクトを参照する必要があるのは1箇所だけである。それが group の定義であり、by_ref を使って expression を参照により組み込んでいる。他の正規表現オブジェクトはすべて初期化済みで値が変化することもないため、値による組み込みで事足りている。

ちなみに

ヒント:可能な限り、値による組み込みを使え

通常、正規表現の組み込みは参照よりも値で行うほうが望ましい。そのほうが分かりやすいし、パターンマッチが少し高速になる。その上、値のセマンティクスは簡単で文法の推論が容易になる。正規表現の「コピー」の負荷については心配しないでいただきたい。各正規表現オブジェクトはコピー間で実装を共有する。

動的正規表現文法

regex_compiler<> を使用して動的正規表現の外部で文法を構築することもできる。名前付きの正規表現を作成し、他の正規表現から名前で参照するのである。各 regex_compiler<> インスタンスは名前と正規表現の対応を保持する。

名前付き動的正規表現を作成するには、正規表現の先頭に (?$name=) を付ける。name は正規表現の名前である。名前付き正規表現を他の正規表現から名前で参照するには (?$name) とする。名前付き正規表現は他の正規表現から参照する時点では存在していなくても構わないが、正規表現を使用する時点では存在していなければならない。

以下のコード片は、動的正規表現文法を使って上の計算機の例を実装している。

using namespace boost::xpressive;
using namespace regex_constants;

sregex expr;

{
     sregex_compiler compiler;
     syntax_option_type x = ignore_white_space;

            compiler.compile("(? $group  = ) \\( (? $expr ) \\) ", x);
            compiler.compile("(? $factor = ) \\d+ | (? $group ) ", x);
            compiler.compile("(? $term   = ) (? $factor )"
                             " ( \\* (? $factor ) | / (? $factor ) )* ", x);
     expr = compiler.compile("(? $expr   = ) (? $term )"
                             "   ( \\+ (? $term ) | - (? $term )   )* ", x);
}

std::string str("foo 9*(10+3) bar");
smatch what;

if(regex_search(str, what, expr))
{
     // "9*(10+3)" を印字する:
     std::cout << what[0] << std::endl;
}

静的正規表現の場合と同様、入れ子の正規表現を呼び出すと入れ子のマッチ結果が作成される(以下の「入れ子の結果」を見よ)。結果はマッチした文字列の完全な解析木である。静的正規表現と異なり、動的正規表現は常に値ではなく参照による組み込みとなる。

循環パターンにコピーにメモリ管理まで、まあ何てこと!

上の計算機の例で非常に複雑なメモリ管理の問題が持ち上がる。4 つの正規表現オブジェクトは直接・間接的に、また値・参照でお互いを参照している。このうちの 1 つを関数から返し、残りがスコープの外に出るとどうなるのか? 参照はどうなるのか? 答えは、正規表現オブジェクトは内部に参照カウントを持つため必要な限り正規表現による参照は保持される、である。よって正規表現オブジェクトを値で渡しても、それがスコープの外に行ってしまった正規表現オブジェクトを参照していたとしても問題は起きない。

参照カウントに詳しい人はおそらくその唯一の弱点についてもご存知と思う。循環参照である。正規表現オブジェクトを参照カウントすると、計算機の例で作成したような循環はどうなるのか? リークが起こるのか? 答えはノーであり、リークは起きない。basic_regex<> オブジェクトは技巧的な参照追跡コードを使っており、最後の外部参照が無くなったときに循環正規表現文法はクリーンアップされる。そういうわけで心配無用だ。好きなだけ循環文法を作成したり、正規表現オブジェクトを渡したりコピーしていただきたい。高速かつ高効率で、リークや懸垂参照(dangling references)が起きないことが保証されている。

入れ子の正規表現と部分マッチのスコープ

正規表現を入れ子にすると部分マッチのスコープの問題が持ち上がる。内側と外側の両方の正規表現が同じ部分マッチのベクタを読み書きすると、混乱が起こる。外側の正規表現が書き込んだ部分マッチを内側の正規表現が台無しにするわけだ。例えば、これはどうなるか。

sregex inner = sregex::compile( "(.)\\1" );
sregex outer = (s1= _) >> inner >> s1;

外側の正規表現が書き込んだ部分マッチを内側の正規表現が上書きしているが、おそらくこのコードの作者が意図するところではないだろう。内側の正規表現がユーザーから入力である場合は、特に大問題である。内側の正規表現が部分マッチのベクタを破壊するかどうか知る方法が無いのである。これは明らかに許容できるものではない。

代わりにどうするのかというと、入れ子の正規表現を呼び出すたびに自身のスコープを形成する。つまり入れ子の正規表現はそれぞれ対象となる部分マッチのベクタについて自分用のコピーを取得するため、外側の正規表現の部分マッチを内側の正規表現が台無しにする可能性は無くなる。例えば上で定義した正規表現 outer は、当然 ABBA にマッチする。

入れ子の結果

入れ子の正規表現が自身の部分マッチをもつのであれば、マッチ成功後にそれらにアクセスする方法があってしかるべきである。regex_matchregex_search の後、match_results<> 構造体は入れ子の結果を表す木の頂点のように振舞う。match_results<> クラスは、入れ子の正規表現の結果を表す match_results<> 構造体の順序付きシーケンスを返す nested_results メンバ関数を提供する。入れ子の結果の順序は、入れ子の正規表現がマッチした順序と同じである。

前に見た、数の合った入れ子の括弧の正規表現を例にとる。

sregex parentheses;
parentheses = '(' >> *( keep( +~(set='(',')') ) | by_ref(parentheses) ) >> ')';

smatch what;
std::string str( "blah blah( a(b)c (c(e)f (g)h )i (j)6 )blah" );

if( regex_search( str, what, parentheses ) )
{
    // マッチ全体を表示する
    std::cout << what[0] << '\n';

    // 入れ子の結果を表示する
    std::for_each(
        what.nested_results().begin(),
        what.nested_results().end(),
        output_nested_results() );
}

このプログラムは以下を表示する。

( a(b)c (c(e)f (g)h )i (j)6 )
    (b)
    (c(e)f (g)h )
        (e)
        (g)
    (j)

結果がどのように入れ子になるか、それらが見つかった順に格納されていることが分かったと思う。

ちなみに

の節にある output_nested_results の定義を見よ。

入れ子の結果のフィルタリング

1 つの正規表現の中に複数の入れ子の正規表現があり、どの結果がどの正規表現に対応するのか知りたい場合がある。basic_regex::regex_idmatch_results::regex_id が役に立つ場面である。入れ子の結果を走査しているときに、結果の正規表現 ID と目的の正規表現オブジェクトの ID を比較するとよい。

これを少し容易にするために、xpressive は特定の入れ子正規表現に相当する結果だけを列挙する述語を提供している。これが regex_id_filter_predicate であり、Boost.Iterator とともに使用することを意図している。以下のように使用する。

sregex name = +alpha;
sregex integer = +_d;
sregex re = *( *_s >> ( name | integer ) );

smatch what;
std::string str( "marsha 123 jan 456 cindy 789" );

if( regex_match( str, what, re ) )
{
    smatch::nested_results_type::const_iterator begin = what.nested_results().begin();
    smatch::nested_results_type::const_iterator end   = what.nested_results().end();

    // 名前(name)か整数(integer)だけを選択する述語フィルタを宣言する
    sregex_id_filter_predicate name_id( name.regex_id() );
    sregex_id_filter_predicate integer_id( integer.regex_id() );

    // 正規表現 name の結果だけを走査する
    std::for_each(
        boost::make_filter_iterator( name_id, begin, end ),
        boost::make_filter_iterator( name_id, end, end ),
        output_result
        );

    std::cout << '\n';

    // 正規表現 integer の結果だけを走査する
    std::for_each(
        boost::make_filter_iterator( integer_id, begin, end ),
        boost::make_filter_iterator( integer_id, end, end ),
        output_result
        );
}

ここで output_resultssmatch を受け取りマッチ全体を表示する単純な関数である。特定の入れ子正規表現に相当する結果だけを選択するのに regex_id_filter_predicatebasic_regex::regex_idBoost.Iteratorboost::make_filter_iterator とともに使っている点に注意していただきたい。このプログラムは以下を表示する。

marsha
jan
cindy
123
456
789

意味アクションとユーザー定義表明

概要

入力文字列を解析し、そこから std::map<> を構築したいとする。このような場合、正規表現では不十分である。正規表現マッチの部分で何かをしたい。xpressive は、静的正規表現の部分に意味アクションを結びつける方法を提供する。本節ではその方法を説明する。

意味アクション(Semantic Actions)

以下のコードを考える。xpressive の意味アクションを使って単語と整数の組からなる文字列を解析し、std::map<> に詰め込んでいる。

#include <string>
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
#include <boost/xpressive/regex_actions.hpp>
using namespace boost::xpressive;

int main()
{
    std::map<std::string, int> result;
    std::string str("aaa=>1 bbb=>23 ccc=>456");

    // => で区切られた単語と整数にマッチし、
    // 結果を std::map<> に詰め込む
    sregex pair = ( (s1= +_w) >> "=>" >> (s2= +_d) )
        [ ref(result)[s1] = as<int>(s2) ];

    // 空白で区切られた 1 つ以上の単語・整数の組にマッチする。
    sregex rx = pair >> *(+_s >> pair);

    if(regex_match(str, rx))
    {
        std::cout << result["aaa"] << '\n';
        std::cout << result["bbb"] << '\n';
        std::cout << result["ccc"] << '\n';
    }

    return 0;
}

このプログラムは以下を印字する。

1
23
456

正規表現 pair は、パターンとアクションの 2 つの部分からなる。単語のマッチを 1 番目の部分マッチで捕捉し、=> で区切られた整数のマッチを 2 番目の部分マッチで捕捉するというのがパターンが表現するところである。アクションは角括弧 [ ref(result)[s1] = as<int>(s2) ] の内側である。これは 1 番目の部分マッチを results 辞書の添字に使用し、そこに2番目の部分マッチを整数に変換した結果を代入するという意味である。

注釈

静的正規表現で意味アクションを使用するには、<boost/xpressive/regex_actions.hpp> をインクルードしなければならない。

このコードはどのように動作するのだろう? 静的正規表現の残りの部分だけ見ると括弧の間は式テンプレートになっている。これでアクションがコード化され、後で実行される。式 ref(result)result への遅延参照を作成する。より大きな式である ref(result)[s1] は辞書に対する添字操作の遅延である。後でこのアクションを実行すると s1 は 1 番目の sub_match<> で置換される。同様に as<int>(s2) を実行すると s2 は 2 番目の sub_match<> で置換される。as<> アクションは引数を Boost.Lexical_cast を使って要求の型に変換する。アクション全体の効果としては、新しい単語・整数の組を辞書に挿入する、となる。

注釈

<boost/ref.hpp> の関数 boost::ref<boost/xpressive/regex_actions.hpp>boost::xpressive::ref には重大な違いがある。前者は通常の参照とほぼ同様の振る舞いをする素の reference_wrapper<> を返す。一方 boost::xpressive::ref が返すのは、遅延実行する式内で使用する遅延参照である。これが、results1 を受け取る operator[] をもたないにも関わらず ref(result)[s1] とする理由である。

部分マッチのプレースホルダ s1s2 に加えて、アクションが結び付けられている部分式にマッチした文字列を後方参照するのにアクション内で使用するプレースホルダー _ がある。例えば以下の正規表現は数字列にマッチし、それらを整数として解釈して結果をローカル変数に代入する。

int i = 0;
// ここで _ は (+_d) にマッチしたすべての文字を後方参照する
sregex rex = (+_d)[ ref(i) = as<int>(_) ];

アクションの遅延実行

アクションを正規表現のある部分に結び付けてマッチを行うとは、実際にはどういう意味なのか? アクションが実行されるのはいつなのか? アクションが繰り返し部分式の一部である場合は、アクションが実行される回数は1度なのか複数回なのか? また部分式が最初はマッチしていたが正規表現の残りの部分がマッチせず最終的に失敗した場合は、アクションはまったく実行されないのか?

答えは既定では、アクションは遅延実行される、である。部分式が文字列にマッチすると、そのアクションはアクションが参照する部分マッチの現在の値とともに待ち行列に置かれる。マッチアルゴリズムがバックトラックしなければならなくなると、アクションは必要に応じて待ち行列から取り出される。アクションが実際に実行されるのは、正規表現全体のマッチが成功した後だけである。regex_match が制御を返す直前の段階で、これらは待ち行列に追加した順番で一度にすべて実行される。

例として、以下の数字を見つけるたびにカウンタを増やす正規表現を考える。

int i = 0;
std::string str("1!2!3?");
// 感嘆符の付いた数字は数えるが、疑問符付きのものは数えない。
sregex rex = +( _d [ ++ref(i) ] >> '!' );
regex_search(str, rex);
assert( i == 2 );

アクション ++ref(i) は 3 回(数字が見つかるたびに 1 回ずつ)待ち行列に入る。しかし実行されるのは 2 回だけ(後ろに ! 文字がある数字 1 字について 1 回ずつ)である。? 文字に遭遇するとマッチアルゴリズムはバックトラックを行い、待ち行列から最後のアクションを削除する。

アクションの即時実行

意味アクションを即時実行したい場合は、そのアクションを含む部分式を keep で包む。keep は当該部分式についてバックトラックを無効にし、その部分式の待ち行列に入っているあらゆるアクションを keep の終了とともに実行する。これにより、あたかも keep 内の部分式が別の正規表現オブジェクトにコンパイルされ、keep のマッチングが regex_search を個別に呼び出して実行されたかのようになる。結果この部分式は文字にマッチしアクションを実行するが、バックトラックも巻き戻しもしない。例えば上の例を以下のように書き換えたとする。

int i = 0;
std::string str("1!2!3?");
// 数字をすべて数える。
sregex rex = +( keep( _d [ ++ref(i) ] ) >> '!' );
regex_search(str, rex);
assert( i == 3 );

部分式 _d[++ref(i)]keep で包んだ。こうすることでこの正規表現が数字にマッチするとアクションが待ち行列に入り、! 文字のマッチを試行する前に即時実行されるようになる。この場合、アクションは 3 回実行される。

注釈

keep と同様、beforeafter 内のアクションも、その部分式がマッチしたときに早期実行される。

遅延関数

ここまで変数と演算子からなる意味アクションの記述方法について見てきたが、意味アクションから関数を呼び出す方法についてはどうだろう? xpressive にはそのための機構がある。

まず関数オブジェクト型を定義する。以下の例は引数に対して push を呼び出す関数オブジェクトである。

struct push_impl
{
    // 戻り値の型(tr1::result_of のために必要)
    typedef void result_type;

    template<typename Sequence, typename Value>
    void operator()(Sequence &seq, Value const &val) const
    {
        seq.push(val);
    }
};

次に xpressive の function<> テンプレートを使って push という名前の関数オブジェクトを定義する。

// グローバルな "push" 関数オブジェクト。
function<push_impl>::type const push = {{}};

初期化はいささか奇妙に見えるが、push を静的に初期化するためである。これは実行時に構築する必要はないということを意味する。以下のように push を意味アクション内で使用する。

std::stack<int> ints;
// 数字がマッチしたら int へキャストし、スタックに積む。
sregex rex = (+_d)[push(ref(ints), as<int>(_))];

この方法だとメンバ関数の呼び出しがただの関数呼び出しに見えてしまうことに気付くと思う。意味アクションを、よりメンバ関数呼び出しらしく見えるように記述する方法がある。

sregex rex = (+_d)[ref(ints)->*push(as<int>(_))];

xpressive は ->* を認識し、この式を上のコードとまったく同等に扱う。

関数オブジェクトが引数によって戻り値の型を変えなければならない場合は、result_type 型定義の代わりに result<> メンバテンプレートを使用するとよい。std::pair<>sub_match<>first メンバを返す first 関数オブジェクトの例である。

// 組の第 1 要素を返す関数オブジェクト。
struct first_impl
{
    template<typename Sig> struct result {};

    template<typename This, typename Pair>
    struct result<This(Pair)>
    {
        typedef typename remove_reference<Pair>
            ::type::first_type type;
    };

    template<typename Pair>
    typename Pair::first_type
    operator()(Pair const &p) const
    {
        return p.first;
    }
};

// OK、first(s1) により s1 が参照する部分マッチの先頭を指すイテレータを得る。
function<first_impl>::type const first = {{}};

ローカル変数を参照する

上の例で見たように、xpressive::ref を使用するとアクション内からローカル変数を参照できる。この変数は正規表現による参照に保持されるが、これらの参照が懸垂しないよう注意が必要である。例えば以下のコードでは、bad_voodoo が制御を返すと i に対する参照が懸垂する。

sregex bad_voodoo()
{
    int i = 0;
    sregex rex = +( _d [ ++ref(i) ] >> '!' );
    // エラー! rex はローカル変数を参照により参照しており、
    // bad_voodoo() が制御を返した後に懸垂する。
    return rex;
}

意味アクションを記述するときは、すべての参照が懸垂しないよう注意を払わなければならない。1 つの方法は変数を、正規表現が値により保持する共有ポインタにすることである。

sregex good_voodoo(boost::shared_ptr<int> pi)
{
    // val() を使って shared_ptr を値で保持する:
    sregex rex = +( _d [ ++*val(pi) ] >> '!' );
    // OK、rex は整数への参照カウントを保持する。
    return rex;
}

上のコードでは、xpressive::val を使って共有ポインタを値で保持している。アクション内のローカル変数は既定では値で保持されるため、通常この処理は必要ないが、この場合は必要である。アクションを ++*pi と記述してしまうと即時実行されてしまう。これは ++*pi が式テンプレートでないためである(++*val(pi) は式テンプレートである)。

アクション内の変数をすべて refval で包むのはうんざりするかもしれない。これを容易にするために xpressive は reference<> および value<> テンプレートを提供している。対応を以下の表に示す。

reference<> と value<>

これは…

…以下と等価である

int i = 0;

sregex rex = +( _d [ ++ref(i) ] >> '!' );
int i = 0;
reference<int> ri(i);
sregex rex = +( _d [ ++ri ] >> '!' );
boost::shared_ptr<int> pi(new int(0));

sregex rex = +( _d [ ++*val(pi) ] >> '!' );
boost::shared_ptr<int> pi(new int(0));
value<boost::shared_ptr<int> > vpi(pi);
sregex rex = +( _d [ ++*vpi ] >> '!' );

上で見たように reference<> を使用する場合、始めにローカル変数を宣言してから reference<> する。local<> を使用するとこの 2 段階を 1 つにまとめられる。

local<> 対 reference<>

これは…

…以下と等価である

local<int> i(0);

sregex rex = +( _d [ ++i ] >> '!' );
int i = 0;
reference<int> ri(i);
sregex rex = +( _d [ ++ri ] >> '!' );

上の例を local<> を使用して書き直すと以下のようになる。

local<int> i(0);
std::string str("1!2!3?");
// 感嘆符の付いた数字は数えるが、疑問符付きのものは数えない。
sregex rex = +( _d [ ++i ] >> '!' );
regex_search(str, rex);
assert( i.get() == 2 );

local::get を使ってローカル変数の値にアクセスしていることに注意していただきたい。また reference<> 同様、local<> が懸垂参照を作成する可能性があることに注意が必要である。

非ローカル変数を参照する

この節の最初で、正規表現を使って単語・整数の組からなる文字列を解析して std::map<> に詰め込む例を見た。この例では辞書と正規表現を定義しておき、いずれかがスコープから出る前にそれらを使う必要があった。正規表現を先に定義しておき、異なる複数の辞書に書き込みたい場合はどうすればよいだろうか? 正規表現オブジェクトに辞書に対する参照を直接組み込むのではなく、regex_match アルゴリズムに辞書を渡すようにしてはどうか。プレースホルダを定義し、意味アクション内で辞書そのものの代わりに使用する。後でいずれかの正規表現アルゴリズムを呼び出すときに実際の辞書オブジェクトへ参照を束縛できる。以下のようにする。

// 辞書オブジェクトのプレースホルダを定義する:
placeholder<std::map<std::string, int> > _map;

// => で区切られた単語と整数にマッチし、
// 結果を std::map<> に詰め込む
sregex pair = ( (s1= +_w) >> "=>" >> (s2= +_d) )
    [ _map[s1] = as<int>(s2) ];

// 空白で区切られた 1 つ以上の単語・整数の組にマッチする。
sregex rx = pair >> *(+_s >> pair);

// 解析する文字列
std::string str("aaa=>1 bbb=>23 ccc=>456");

// 結果を書き込む実際の辞書:
std::map<std::string, int> result;

// _map プレースホルダを実際の辞書に束縛する
smatch what;
what.let( _map = result );

// マッチを実行し結果の辞書に書き込む
if(regex_match(str, what, rx))
{
    std::cout << result["aaa"] << '\n';
    std::cout << result["bbb"] << '\n';
    std::cout << result["ccc"] << '\n';
}

このプログラムは以下を表示する。

1
23
456

placeholder<> を使って _map を定義しており、これが std::map<> 変数の代理となる。意味アクション内でこのプレースホルダを辞書として使用できる。次に match_results 構造体を定義して what.let( _map = result ); で実際の辞書をプレースホルダに束縛する。regex_match 呼び出しは、意味アクション内のプレースホルダを result への参照で置換したかのように振舞う。

注釈

意味アクション内のプレースホルダは実際には実行時に変数への参照で置換されない。正規表現オブジェクトはいずれの正規表現アルゴリズムでも変更されることはないので、複数のスレッドで使用しても安全である。

regex_iterator<>regex_token_iterator<> を使用する場合は、遅延束縛されたアクションの引数は少し異なる。正規表現イテレータのコンストラクタは、引数の束縛を指定する引数を受け付ける。変数をそのプレースホルダに束縛するのに使用する let 関数がある。以下のコードに方法を示す。

// 辞書オブジェクトのプレースホルダを定義する:
placeholder<std::map<std::string, int> > _map;

// => で区切られた単語と整数にマッチ
sregex pair = ( (s1= +_w) >> "=>" >> (s2= +_d) )
    [ _map[s1] = as<int>(s2) ];

// 解析する文字列
std::string str("aaa=>1 bbb=>23 ccc=>456");

// 結果を書き込む実際の辞書:
std::map<std::string, int> result;

// regex_iterator を作成し、すべてのマッチを検索する
sregex_iterator it(str.begin(), str.end(), pair, let(_map=result));
sregex_iterator end;

// すべてのマッチについて結果の辞書に書き込む
while(it != end)
    ++it;

std::cout << result["aaa"] << '\n';
std::cout << result["bbb"] << '\n';
std::cout << result["ccc"] << '\n';

このプログラムは以下を出力する。

1
23
456

ユーザー定義表明

正規表現の表明については慣れたものだろう。Perl だと表明の例として ^$ があり、それぞれ文字列の先頭・終端にマッチする。xpressive では新たに表明を定義できる。カスタム表明は、マッチの成否を判断する時点で真でなければならない条件である。カスタム表明をチェックするには xpressive の check 関数を使用する。

カスタム表明を定義する方法はいくつかある。一番簡単なのは関数オブジェクトを使うことである。長さが 3 文字か 6 文字のいずれかである部分文字列にマッチする部分式が必要であるとする。そのような述語を以下の構造体で定義する。

// 部分マッチが長さ 3 文字か 6 文字であれば真となる述語。
struct three_or_six
{
    bool operator()(sub_match const &sub) const
    {
        return sub.length() == 3 || sub.length() == 6;
    }
};

この述語を正規表現で使うには以下のようにする。

// 3 文字か 6 文字の単語にマッチする。
sregex rx = (bow >> +_w >> eow)[ check(three_or_six()) ] ;

上の正規表現は長さが 3 文字か 6 文字の単語全体にマッチする。述語 three_or_six は、カスタム表明が結び付けられた部分式にマッチした部分を後方参照する sub_match<> を受け取る。

注釈

カスタム表明はマッチの成否に関与する。遅延実行されるアクションとは異なり、カスタム表明は正規表現エンジンがマッチを検索するときに即時実行される。

カスタム表明は意味アクションと同じ構文を用いてインライン定義することもできる。以下は同じカスタム表明をインラインで書き直したものである。

// 3 文字か 6 文字の単語にマッチする。
sregex rx = (bow >> +_w >> eow)[ check(length(_)==3 || length(_)==6) ] ;

上記において、length() は引数の length メンバ関数を呼び出す遅延関数であり、_sub_match を受け取るプレースホルダである。

カスタム表明のインライン記述は、コツが分かってしまえば非常に強力である。(あまり厳密でない意味での)正しい日付にのみマッチする正規表現を書いてみよう。

int const days_per_month[] =
    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

mark_tag month(1), day(2);
// 「月/日/年」形式の正しい日付を検索する。
sregex date =
    (
        // 月は 1 以上 12 以下でなければならない
        (month= _d >> !_d)     [ check(as<int>(_) >= 1
                                    && as<int>(_) <= 12) ]
    >>  '/'
        // 日は 1 以上 31 以下でなければならない
    >>  (day=   _d >> !_d)     [ check(as<int>(_) >= 1
                                    && as<int>(_) <= 31) ]
    >>  '/'
        // 年は 1970 以上 2038 以下とする
    >>  (_d >> _d >> _d >> _d) [ check(as<int>(_) >= 1970
                                    && as<int>(_) <= 2038) ]
    )
    // 月ごとの実際の日数を確認する!
    [ check( ref(days_per_month)[as<int>(month)-1] >= as<int>(day) ) ]
;

smatch what;
std::string str("99/99/9999 2/30/2006 2/28/2006");

if(regex_search(str, what, date))
{
    std::cout << what[0] << std::endl;
}

このプログラムは以下を印字する。

2/28/2006

インラインのカスタム表明を使って年・月・日の値の範囲チェックを行っていることに注意していただきたい。99/99/99992/30/2006 は正しい日付ではないため、この正規表現はマッチしない(99 の月は存在しないし、2 月には 30 日はない)。

記号表と属性

概要

xpressive の正規表現で記号表を構築するには、std::map<> を使うだけでよい。辞書のキーはマッチした文字列であり、辞書の値は意味アクションが返すデータである。xpressive の 属性 a1a2 、…、a9 はマッチしたキーに相当する値を保持し、意味アクション内で使用する。記号が見つからなかった場合の属性の既定値を指定することも可能である。

記号表

xpressive の記号表は単純に std::map<> であり、キーは文字列型、値は何でもよい。例えば以下の正規表現は、map1 のキーにマッチし対応する値を属性 a1 に代入する。次に意味アクションにおいて、属性 a1 に格納した値を結果の整数に代入する。

int result;
std::map<std::string, int> map1;
// ...(辞書を埋める)
sregex rx = ( a1 = map1 ) [ ref(result) = a1 ];

次のコード例は数値の名前を整数に変換する。説明は以下に示す。

#include <string>
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
#include <boost/xpressive/regex_actions.hpp>
using namespace boost::xpressive;

int main()
{
    std::map<std::string, int> number_map;
    number_map["one"] = 1;
    number_map["two"] = 2;
    number_map["three"] = 3;
    // number_map の文字列でマッチを行い
    // 整数値を 'result' に格納する
    // 見つからなければ -1 を 'result' に格納する
    int result = 0;
    cregex rx = ((a1 = number_map ) | *_)
        [ ref(result) = (a1 | -1)];

    regex_match("three", rx);
    std::cout << result << '\n';
    regex_match("two", rx);
    std::cout << result << '\n';
    regex_match("stuff", rx);
    std::cout << result << '\n';
    return 0;
}

このプログラムは以下を印字する。

3
2
-1

このプログラムは始めに、数の名前をキー文字列とし対応する整数を値とする数値の辞書を構築している。次に記号表の探索結果を表す属性 a1 を使って静的正規表現を構築している。意味アクション内では属性を整数変数 result に代入している。記号が見つからなければ既定値の -1result に代入する。記号が見つからなくてもマッチが成功するために、ワイルドカード *_ を使っている。

この例のより完全版は libs/xpressive/example/numbers.cpp にある。7 このコードは「999,999,999」以下の数の名前(「ダース」のような特殊な数の名前が混ざっていてもよい)を数値に変換する。

記号表のマッチは既定では大文字小文字を区別するが、式を icase で囲むことにより大文字小文字を区別しないようにできる。

属性

1 つの正規表現内で使用できる属性は最大 9 つであり、a1a2 、…、a9 という名前で boost::xpressive 名前空間内にある。属性の型は代入元の辞書の 2 番目の要素と同じである。属性の既定値は意味アクション内で (a1 | default-value) のような構文で指定する。

属性のスコープは適切に設定されるため、( (a1=sym1) >> (a1=sym2)[ref(x)=a1] )[ref(y)=a1] のようなとてつもないこともできる。内側の意味アクションは内側の a1 を参照し、外側の意味アクションは外側の属性を参照する。これらは型が異なっていてもよい。

注釈

xpressive は検索を高速化するために、辞書から不可視の 3 分探索木を構築する。BOOST_DISABLE_THREADS を定義した場合、この不可視の 3 分木は検索後に「毎回自身を再構築」し、前回の検索頻度に基づいて次回の検索効率を向上する。

7

この例を寄贈してくれた David Jenkins に感謝する。

地域化と正規表現特性

概要

文字列に対する正規表現マッチにおいて、ロカール依存の情報が必要になる場合がよくある。例えば、大文字小文字を区別しない比較はどのように行うのか? ロカール依存の振る舞いは特性(traits)クラスが取り扱う。xpressive は cpp_regex_traits<>c_regex_traits<> および null_regex_traits<> の 3 つの特性クラステンプレートを提供する。1 番目のものは std::locale をラップし、2 番目のものはグローバルな C ロカールをラップする。3 番目は非文字データを検索するのに使用する控えの特性型である。すべての特性テンプレートは正規表現特性のコンセプトに適合する。

既定の正規表現特性を設定する

既定では xpressive はすべてにパターンに対して cpp_regex_traits<> を使用する。これにより、すべての正規表現オブジェクトはグローバルな std::locale を使用する。BOOST_XPRESSIVE_USE_C_TRAITS を定義してコンパイルすると、xpressive の既定は c_regex_traits<> になる。

動的正規表現でカスタムの特性を使用する

カスタムの特性オブジェクトを使う動的正規表現を作成するには、regex_compiler<> を使わなければならない。基本的な方法を以下の例に示す。

// グローバルな C ロカールを使う regex_compiler を宣言する
regex_compiler<char const *, c_regex_traits<char> > crxcomp;
cregex crx = crxcomp.compile( "\\w+" );

// カスタムの std::locale を使う regex_compiler を宣言する
std::locale loc = /* ... ここでロカールを作成する ... */;
regex_compiler<char const *, cpp_regex_traits<char> > cpprxcomp(loc);
cregex cpprx = cpprxcomp.compile( "\\w+" );

regex_compiler オブジェクトは正規表現のファクトリとして動作する。これらは一度ロカールを与えておくと、以降作成する正規表現はそのロカールを使用するようになる。

静的正規表現でカスタムの特性を使用する

個々の静的正規表現に異なる特性群を使用したい場合は、imbue 特殊パターン修飾子を使用する。例えば、

// グローバルな C ロカールを使う正規表現を定義する
c_regex_traits<char> ctraits;
sregex crx = imbue(ctraits)( +_w );

// カスタムの std::locale を使う正規表現を定義する
std::locale loc = /* ... ここでロカールを作成する ... */;
cpp_regex_traits<char> cpptraits(loc);
sregex cpprx1 = imbue(cpptraits)( +_w );

// 上記の短縮形
sregex cpprx2 = imbue(loc)( +_w );

imbue パターン修飾子はパターン全体を囲まなければならない。静的正規表現の一部だけを imbue するとエラーになる。例えば、

// エラー! 正規表現の一部だけを imbue() することはできない
sregex error = _w >> imbue(loc)( _w );

null_regex_traits で非文字データを検索する

xpressive の静的正規表現では、パターンの検索は文字シーケンス内に限定されない。生のバイト、整数、その他文字のコンセプトに適合するものであれば何でも検索できる。このような場合、null_regex_traits<> を使うと簡単である。正規表現特性のコンセプトの控えの実装であり、文字クラスを無視し、大文字小文字に関する変換を一切行わない。

例えば整数列からパターンを検索する静的正規表現は、null_regex_traits<> を使って以下のように記述できる。

// 検索する整数データ
int const data[] = {0, 1, 2, 3, 4, 5, 6};

// 整数を検索する null_regex_traits<> オブジェクトを作成する...
null_regex_traits<int> nul;

// 正規表現オブジェクトに null_regex_traits を指示する...
basic_regex<int const *> rex = imbue(nul)(1 >> +((set= 2,3) | 4) >> 5);
match_results<int const *> what;

// 整数の配列からパターンを検索する...
regex_search(data, data + 7, what, rex);

assert(what[0].matched);
assert(*what[0].first == 1);
assert(*what[0].second == 6);

ヒント集

以下のヒント集に従うと、xpressive の効率を最大限に引き出せる。

パターンのコンパイルは一度とし、再利用せよ

正規表現のコンパイル(動的、静的によらない)は、マッチや検索の実行より何倍ものコストを要する。可能であれば basic_regex<> のコンパイルは一度だけにし、あとは再利用せよ(事あるごとに再作成してはならない)。

basic_regex<> オブジェクトはいかなる正規表現アルゴリズムによっても変更されないので、正規表現(と所属するすべての文法)の初期化が完了しさえすれば完全にスレッド安全である。パターンの再利用で一番簡単な方法は、basic_regex<> オブジェクトを static const にすることである。

match_results<> オブジェクトを再利用せよ

match_results<> オブジェクトは動的に確保したメモリをキャッシュする。そのため、正規表現検索を何度も行う場合は同じ match_results<> オブジェクトを再利用するほうがずっとよい。

注意:match_results<> オブジェクトはスレッド安全でないため、スレッドを超えて再利用してはならない。

match_results<> オブジェクトを引数に取るアルゴリズムを使用せよ

これも同様である。検索を複数回行う場合は、match_results<> オブジェクトを引数に取る正規表現アルゴリズムを使用し、毎回同じ match_results<> オブジェクトを使用すべきだ。match_results<> オブジェクトを与えないと一時オブジェクトが作成され、アルゴリズムが結果を返すときに破棄される。オブジェクトがキャッシュしていたメモリは解放され、次回また再確保されてしまう。

null 終端文字列に対してはイテレータの範囲を引数に取るアルゴリズムを使用せよ

xpressive は regex_match および regex_search アルゴリズムについて、C 形式の null 終端文字列を操作する多重定義を提供している。イテレータの範囲を引数に取る多重定義を使用すべきだ。null 終端文字列を正規表現アルゴリズムに渡すと、終端のイテレータを計算するために strlen が呼び出されてしまう。文字列の長さが事前に分かっているのであれば、[begin,end) 組を取る正規表現を呼び出してこのオーバーヘッドを回避できる。

静的正規表現を使用せよ

静的正規表現は同じ内容の動的版に対して、平均で約 10% から 15% 高速である。これだけでも静的版に慣れておく価値がある。

regex_constants::syntax_option_type::optimize を理解せよ

optimize フラグを正規表現コンパイラに渡すと、パターンの解析により多くの時間をかけるようになる。この結果、パターンによっては実行が高速になるが、コンパイル時間が長くなり、しばしばパターンが要するメモリの量が増える。パターンを再利用するのであれば optimize は効果があると考えてよい。パターンを一度しか使用しないのであれば、optimize は避けるべきだ。

よくある落とし穴

xpressive の落とし穴に足を踏み入れないように、以下のことを覚えておくとよい。

文法は単一のスレッドで作成せよ

静的正規表現では正規表現を入れ子にして文法を構築するが、外側の正規表現をコンパイルすると外側と内側の両方の正規表現オブジェクト、およびそれらが直接・間接的に参照するすべての正規表現オブジェクトが更新される。そのため、グローバルな正規表現オブジェクトが文法に関与すると危険である。単一のスレッドから正規表現文法を構築するのが最善である。一度構築してしまえば、正規表現文法は複数のスレッドから問題なく実行できる。

入れ子の数量子に注意せよ

これは多くの正規表現エンジンに共通の落とし穴であり、パターンによっては指数的に効率が悪化する。よくあるのは (a*)* のようにパターン内の数量子付きの項が他の数量子に入れ子になっているというものだが、多くの場合発見しにくいのが問題である。数量子が入れ子になっているパターンには注意せよ。

コンセプト

CharT の要件

BidiIterTbasic_regex<> のテンプレート引数とすると、iterator_traits<BidiIterT>::value_typeCharT である。型 CharT は自明な(trivial)既定コンストラクタ、コピーコンストラクタ、代入演算子、およびデストラクタをもたなければならない。さらにオブジェクトに関しては以下の要件を満たさなければならない。cCharT 型、c1c2CharT const 型、iint 型である。

CharT の要件

戻り値の型

表明、備考、事前・事後条件

CharT c

CharT

既定のコンストラクタ(自明でなければならない)。

CharT c(c1)

CharT

コピーコンストラクタ(自明でなければならない)。

c1 = c2

CharT

代入演算子(自明でなければならない)。

c1 == c2

bool

c1 の値が c2 と同じであれば true

c1 != c2

bool

c1c2 が等値でなければ true

c1 < c2

bool

c1 の値が c2 より小さければ true

c1 > c2

bool

c1 の値が c2 より大きければ true

c1 <= c2

bool

c1c2 より小さいか等値であれば true

c1 >= c2

bool

c1c2 より大きいか等値であれば true

intmax_t i = c1

int

CharT は整数型に変換可能でなければならない。

CharT c(i);

CharT

CharT は整数型から構築可能でなければならない。

特性の要件

以下の表において XCharT 型の文字コンテナについて型と関数を定義する特性クラスである。uX 型のオブジェクト、vconst X 型のオブジェクト、pconst CharT* 型の値、I1I2Input Iteratorcconst CharT 型の値、sX::string_type 型のオブジェクト、csconst X::string_type 型のオブジェクト、bbool 型の値、iint 型の値、F1F2const CharT* 型の値、locX::locale_type 型のオブジェクト、chconst char のオブジェクトである。

特性の要件

戻り値の型

表明、備考、事前・事後条件

X::char_type

CharT

basic_regex<> クラステンプレートを実装する文字コンテナ型。

X::string_type

std::basic_string<CharT>std::vector<CharT>

なし。

X::locale_type

(実装定義)

特性クラスが使用するロカールを表現する、コピー構築可能な型。

X::char_class_type

(実装定義)

個々の文字分類(文字クラス)を表現するビットマスク型。この型の複数の値をビット和すると別の有効な値を得る。

X::hash(c)

unsigned char

0 以上 UCHAR_MAX 以下の値を生成する。

v.widen(ch)

CharT

指定した char のワイド版を CharT で返す。

v.in_range(r1, r2, c)

bool

任意の文字 r1r2 について、r1 <= c && c <= r2 であれば true を返す。r1 <= r2 でなければならない。

v.in_range_nocase(r1, r2, c)

bool

任意の文字 r1r2 について、v.translate_nocase(d) == v.translate_case(c) かつ r1 <= d && d <= r2 となる文字 d が存在すれば true を返す。r1 <= r2 でなければならない。

v.translate(c)

X::char_type

c と等価、つまり v.translate(c) == v.translate(d) となるような文字 d を返す。

v.translate_nocase(c)

X::char_type

大文字小文字を区別せずに比較したとき c と等価、つまり v.translate_nocase(c) == v.translate_nocase(C) となるような文字 C

v.transform(F1, F2)

X::string_type

イテレータ範囲 [F1, F2) が示す文字シーケンスのソートキーを返す。文字シーケンス [G1, G2) が文字シーケンス [H1, H2) の前にソートされる場合に v.transform(G1, G2) < v.transform(H1, H2) とならなければならない。

v.transform_primary(F1, F2)

X::string_type

イテレータ範囲 [F1, F2) が示す文字シーケンスのソートキーを返す。大文字小文字を区別せずにソートして文字シーケンス [G1, G2) が文字シーケンス [H1, H2) の前に現れる場合に v.transform_primary(G1, G2) < v.transform_primary(H1, H2) とならなければならない。

v.lookup_classname(F1, F2)

X::char_class_type

イテレータ範囲 [F1, F2) が示す文字シーケンスを、isctype に渡せるビットマスク型に変換する。lookup_classname が返した値同士でビット和を取っても安全である。文字シーケンスがXが解釈できる文字クラス名でなければ 0 を返す。文字シーケンス内の大文字小文字の違いで戻り値が変化することはない。

v.lookup_collatename(F1, F2)

X::string_type

イテレータ範囲 [F1, F2) が示す文字シーケンスが構成する照合要素を表す文字シーケンスを返す。文字シーケンスが正しい照合要素でなければ空文字列を返す。

v.isctype(c, v.lookup_classname(F1, F2))

bool

文字 c が、イテレータ範囲 [F1, F2) が示す文字クラスのメンバであれば真を返す。それ以外は偽を返す。

v.value(c, i)

int

文字 c が基数 i で有効な数字であれば、数字 c の基数 i での数値を返す。8 それ以外の場合は -1 を返す。

u.imbue(loc)

X::locale_type

ロカール locu に指示する。u が直前まで使用していたロカールを返す。

v.getloc()

X::locale_type

v が使用中のロカールを返す。

謝辞

この節は Boost.Regex ドキュメントの同じページと、正規表現を標準ライブラリに追加することになった草案をもとに作成した。

8

i の値は 8 、10 、16 のいずれかである。

以下に 6 つの完全なプログラム例を挙げる。

文字列全体が正規表現にマッチするか調べる

導入項にもあった例である。利便性のために再掲する。

#include <iostream>
#include <boost/xpressive/xpressive.hpp>

using namespace boost::xpressive;

int main()
{
    std::string hello( "hello world!" );

    sregex rex = sregex::compile( "(\\w+) (\\w+)!" );
    smatch what;

    if( regex_match( hello, what, rex ) )
    {
        std::cout << what[0] << '\n'; // マッチ全体
        std::cout << what[1] << '\n'; // 1 番目の捕捉
        std::cout << what[2] << '\n'; // 2 番目の捕捉
    }

    return 0;
}

このプログラムは以下を出力する。

hello world!
hello
world

文字列が正規表現にマッチする部分文字列を含むか調べる

この例では、カスタムの mark_tag を使ってパターンを読みやすくしている点に注意していただきたい。後で mark_tagmatch_results<> の添字に使っている。

#include <iostream>
#include <boost/xpressive/xpressive.hpp>

using namespace boost::xpressive;

int main()
{
    char const *str = "I was born on 5/30/1973 at 7am.";

    // s1 、s2 、... よりも意味のある名前でカスタムの mark_tags を定義する
    mark_tag day(1), month(2), year(3), delim(4);

    // この正規表現は日付を検索する
    cregex date = (month= repeat<1,2>(_d))           // 先頭に月があり ...
               >> (delim= (set= '/','-'))            // その後ろに区切りがあり ...
               >> (day=   repeat<1,2>(_d)) >> delim  // さらに後ろに日と、同じ区切りがあり ...
               >> (year=  repeat<1,2>(_d >> _d));    // 最後に年がある。

    cmatch what;

    if( regex_search( str, what, date ) )
    {
        std::cout >> what[0]     >> '\n'; // マッチ全体
        std::cout >> what[day]   >> '\n'; // 日
        std::cout >> what[month] >> '\n'; // 月
        std::cout >> what[year]  >> '\n'; // 年
        std::cout >> what[delim] >> '\n'; // 区切り
    }

    return 0;
}

このプログラムは以下を出力する。

5/30/1973
30
5
1973

正規表現にマッチした部分文字列をすべて置換する

以下のプログラムは文字列内の日付を検索し、擬似 HTML でマークアップする。

#include <iostream>
#include <boost/xpressive/xpressive.hpp>

using namespace boost::xpressive;

int main()
{
    std::string str( "I was born on 5/30/1973 at 7am." );

    // 本質的には前の例と同じ正規表現だが、動的正規表現を使っている
    sregex date = sregex::compile( "(\\d{1,2})([/-])(\\d{1,2})\\2((?:\\d{2}){1,2})" );

    // Perl と同様、$& は正規表現にマッチした部分文字列を参照する
    std::string format( "<date>$&</date>" );

    str = regex_replace( str, date, format );
    std::cout << str << '\n';

    return 0;
}

このプログラムは以下を出力する。

I was born on <date>5/30/1973</date> at 7am.

正規表現にマッチする部分文字列をすべて検索し、1 つずつ辿る

以下のプログラムはワイド文字列から単語を検索する。wsregex_iterator を使う。wsregex_iterator を参照はがしすると wsmatch オブジェクトが得られることに注意していただきたい。

#include <iostream>
#include <boost/xpressive/xpressive.hpp>

using namespace boost::xpressive;

int main()
{
    std::wstring str( L"This is his face." );

    // 単語全体を検索する
    wsregex token = +alnum;

    wsregex_iterator cur( str.begin(), str.end(), token );
    wsregex_iterator end;

    for( ; cur != end; ++cur )
    {
        wsmatch const &what = *cur;
        std::wcout << what[0] << L'\n';
    }

    return 0;
}

このプログラムは以下を出力する。

This
is
his
face

文字列をそれぞれ正規表現にマッチするトークンに分割する

以下のプログラムは文字列からレースのタイムを検索し、はじめに分、次に秒を表示する。regex_token_iterator<> を使っている。

#include <iostream>
#include <boost/xpressive/xpressive.hpp>

using namespace boost::xpressive;

int main()
{
    std::string str( "Eric: 4:40, Karl: 3:35, Francesca: 2:32" );

    // レースのタイムを検索する
    sregex time = sregex::compile( "(\\d):(\\d\\d)" );

    // 各マッチについて、トークンイテレータは始めに1番目のマーク済み部分式の値
    // 次に2番目のマーク済み部分式の値をとらなければならない
    int const subs[] = { 1, 2 };

    sregex_token_iterator cur( str.begin(), str.end(), time, subs );
    sregex_token_iterator end;

    for( ; cur != end; ++cur )
    {
        std::cout << *cur << '\n';
    }

    return 0;
}

このプログラムは以下を出力する。

4
40
3
35
2
32

正規表現を区切りとして文字列を分割する

以下のプログラムは HTML でマークアップされたテキストからマークアップを除去する。HTML タグにマッチする正規表現と、文字列内の正規表現にマッチしなかった部分を返す regex_token_iterator<> を使っている。

#include <iostream>
#include <boost/xpressive/xpressive.hpp>

using namespace boost::xpressive;

int main()
{
    std::string str( "Now <bold>is the time <i>for all good men</i>"
                     " to come to the aid of their</bold> country." );

    // HTML タグを検索する
    sregex html = '<' >> optional('/') >> +_w >> '>';

    // 以下のようにトークンイテレータに -1 を与えると
    // 正規表現にマッチ*しなかった*文字列部分を表示する。
    sregex_token_iterator cur( str.begin(), str.end(), html, -1 );
    sregex_token_iterator end;

    for( ; cur != end; ++cur )
    {
        std::cout << '{' << *cur << '}';
    }
    std::cout << '\n';

    return 0;
}

このプログラムは以下を出力する。

{Now }{is the time }{for all good men}{ to come to the aid of their}{ country.}

入れ子になった結果木を表示する

入れ子になった結果木を表示する方法を以下のヘルパクラスで示す。

// 入れ子になった結果を字下げ付きで std::cout に出力する
struct output_nested_results
{
    int tabs_;

    output_nested_results( int tabs = 0 )
        : tabs_( tabs )
    {
    }

    template< typename BidiIterT >
    void operator ()( match_results< BidiIterT > const &what ) const
    {
        // はじめに字下げする
        typedef typename std::iterator_traits< BidiIterT >::value_type char_type;
        char_type space_ch = char_type(' ');
        std::fill_n( std::ostream_iterator<char_type>( std::cout ), tabs_ * 4, space_ch );

        // マッチを出力する
        std::cout << what[0] << '\n';

        // 入れ子のマッチを出力する
        std::for_each(
            what.nested_results().begin(),
            what.nested_results().end(),
            output_nested_results( tabs_ + 1 ) );
    }
};