boost::regex を使って /~/g みたいなことをやる方法2007年06月04日 00時28分54秒

宿題をひとつ解きますた。

  1. Perl で言うところの /$pattern/g に相当する記述方法はないか?
    • boost::regex_grep() というのはあったらしいが、すでに deprecated 。Predicate とかいうコールバックらしきものの設定が必須な模様で、そういう意味でも使いにくい (Perl の grep 関数を意識してるのかな?)。
    • boost::regex コンストラクタで std::regex_constants::grep または std::regex_constants::egrep を指定した場合、どうなるか?
    • Perl の tr/.../.../ 相当の記述法も併せて検証する。ふつーに iterator 回せって? まぁそうなんだけどね。

boost::regex_iteratorboost::make_regex_iterator() を用いて生成する方法と、boost::basic_regex のコンストラクタに渡すパラメータ値 boost::regex_constants::egrep との併用でいけそうです (訂正: Tue Jun 5 22:22:23 JST 2007)。以下、サンプル。

#include <iostream>
#include <string>

#include <boost/regex.hpp>

using namespace std;
using boost::regex; using boost::smatch; using boost::sregex_iterator;
using boost::regex_constants::egrep;
using boost::make_regex_iterator;

int main()
{
    string text = "foo bar (baz) hoge (huga) o(yoy)o nan(to iu ko)toda-";

    for (sregex_iterator it = make_regex_iterator(text, regex("\\(([^\\)]+)\\)", egrep));
        it != sregex_iterator(); it++)
    {
        cout << (*it)[1] << endl;
    }
}

実行結果はこんな感じ。

C:\Program_1\vs8\regex_test_mb\debug>regex_test_mb.exe
baz
huga
yoy
to iu ko

C:\Program_1\vs8\regex_test_mb\debug>

Tue Jun 5 22:22:23 JST 2007 - 追記

boost::regex_constants::egrep は不要でした。

それから、一括置換を行いたい場合は、普通に boost::regex_replace() アルゴリズムを使えばいい模様です (逆に一括では置換したくない場合はどうすればいいんだ?)。boost::match_results::format() はあくまでマッチ結果を利用して Perl 風に置換のフォーマットを記述できるというただそれだけの代物のようです。

以下、総括的なサンプルソース。

#include <iostream>
#include <string>

#include <boost/regex.hpp>

using namespace std;
using boost::regex;
using boost::sregex_iterator;
using boost::make_regex_iterator;
using boost::regex_replace;

int main()
{
    string text = "foo bar (baz) hoge (huga) o(yoy)o nan(to iu ko)toda-";
    cout << text << endl;

    for (sregex_iterator it = make_regex_iterator(text, regex("\\(([^\\)]+)\\)"));
        it != sregex_iterator(); it++)
    {
        cout << (*it)[1] << endl;
        cout << it->format("[$1]") << endl;
    }

    cout << regex_replace(text, regex("\\(([^\\)]+)\\)"), "[$1]") << endl;
}

実行結果は以下のとおり。

C:\Program_1\vs8\regex_test_mb\debug>regex_test_mb.exe
foo bar (baz) hoge (huga) o(yoy)o nan(to iu ko)toda-
baz
[baz]
huga
[huga]
yoy
[yoy]
to iu ko
[to iu ko]
foo bar [baz] hoge [huga] o[yoy]o nan[to iu ko]toda-

C:\Program_1\vs8\regex_test_mb\debug>

Wed Jun 6 12:27:51 JST 2007 - 追記

s/~/eg みたいなことをやる方法もわかったので追記。よーするに regex_replace() アルゴリズムを自前でやっつける方法ですね。

その前に。まず、make_regex_iterator() は、regex_iterator のコンストラクタを呼べるのであれば、不要です。ていうか、効率を考えればむしろそうするべきです。make_regex_iterator() 自体は regex_iterator コンストラクタの単なる構文糖に過ぎません。

で、自前でテキスト置換を実装するにはマッチしなかった部分のフレーズが (適切なタイミングで) 得られる必要がありますが、それは match_results::prefix() で取得できます。また、マッチングのループで舐められることのなかった部分 (最後にマッチしたフレーズよりも後ろの部分) については、最後に評価された regex_iteratormatch_results::suffix() にて取得できます。

以上を踏まえた上で、サンプルソースを以下に示します。

#include <iostream>
#include <string>

#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>

using namespace std;
using boost::regex;
using boost::sregex_iterator;
using boost::make_regex_iterator;
using boost::regex_replace;
using boost::lexical_cast;

int main()
{
    string text = "foo bar (baz) hoge (huga) o(yoy)o nan(to iu ko)toda-";
    cout << text << endl;

    string modified;
    sregex_iterator last_it;
    for (sregex_iterator it(text.begin(), text.end(), regex("\\(([^\\)]+)\\)"));
        it != sregex_iterator(); it++)
    {
        modified += it->prefix() + it->format("[$1:") +
            lexical_cast<string>((*it)[1].str().length()) + "]";
        last_it = it;
    }
    modified += last_it == sregex_iterator() ? text : last_it->suffix();

    cout << modified << endl;
}

ひとつもマッチしなかった場合を考慮して、last_it をデフォルトコンストラクタにて生成される空イテレータと比較することを忘れてはいけません。

出力結果は以下のようになります。

C:\Program_1\vs8\regex_test_mb\debug>regex_test.exe
foo bar (baz) hoge (huga) o(yoy)o nan(to iu ko)toda-
foo bar [baz:3] hoge [huga:4] o[yoy:3]o nan[to iu ko:8]toda-

C:\Program_1\vs8\regex_test_mb\debug>

コメント

_ T.MURACHI ― 2007/06/04 15:54:36

boost::regex_replace() は内部で boost::match_results::format() を呼び出しているらしい。
http://www.boost.org/libs/regex/doc/regex_replace.html
http://www.boost.org/libs/regex/doc/match_results.html#format
これを上記のループに適用すれば s/~/g みたいなこともできる…のか? をあとで追記。
もしかしたら boost::regex_replace() と boost::regex_constants::egrep の組み合わせだけでできるのかもしれないけど。

_ T.MURACHI ― 2007/06/06 10:23:55

ここまでくると、あとは s/~/eg とかもやりたくなる罠。
regex_replace のソースでも眺めてみようかしら。。。

_ T.MURACHI ― 2007/06/06 11:48:56

match_results::prefix() でヒットしなかった部分が拾えるっぽい。
http://www.boost.org/libs/regex/doc/match_results.html#m8

これでいける。。。かな?

コメントをどうぞ

※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。

※投稿には管理者が設定した質問に答える必要があります。

名前:
メールアドレス:
URL:
次の質問に答えてください:
おいらがやっている会社の名前をひらがな4文字で。

コメント:

トラックバック

このエントリのトラックバックURL: http://harapeko.asablo.jp/blog/2007/06/04/1553570/tb

※なお、送られたトラックバックはブログの管理者が確認するまで公開されません。