SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

連載記事

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

CodeZine BOOKS(コードジン・ブックス)は、CodeZineの連載からカットアップした、開発現場の課題解決に役立つ書籍シリーズです。

書籍に関する記事を見る

'); googletag.cmd.push(function() { googletag.pubads().addEventListener('slotRenderEnded', function(e) { var ad_id = e.slot.getSlotElementId(); if (ad_id == 'div-gpt-ad-1659428980688-0') { var ad = $('#'+ad_id).find('iframe'); if ($(ad).width() == 728) { var ww = $(window).width(); ww = ww*0.90; var style = document.createElement("style"); document.head.appendChild( style ); var sheet = style.sheet; sheet.insertRule( "#div-gpt-ad-1659428980688-0 iframe {-moz-transform: scale("+ww/728+","+ww/728+");-moz-transform-origin: 0 0;-webkit-transform: scale("+ww/728+","+ww/728+");-webkit-transform-origin: 0 0;-o-transform: scale("+ww/728+","+ww/728+");-o-transform-origin: 0 0;-ms-transform: scale("+ww/728+","+ww/728+");-ms-transform-origin: 0 0;}", 0 ); sheet.insertRule( "#div-gpt-ad-1659428980688-0 div{ height:"+(90*ww/728)+"px;width:"+728+"px;}", 0 ); } else { if ($(window).width() < 340) { var ww = $(window).width(); ww = ww*0.875; var style = document.createElement("style"); document.head.appendChild( style ); var sheet = style.sheet; sheet.insertRule( "#div-gpt-ad-1659428980688-0 iframe {-moz-transform: scale("+ww/320+","+ww/320+");-moz-transform-origin: 0 0;-webkit-transform: scale("+ww/320+","+ww/320+");-webkit-transform-origin: 0 0;-o-transform: scale("+ww/320+","+ww/320+");-o-transform-origin: 0 0;-ms-transform: scale("+ww/320+","+ww/320+");-ms-transform-origin: 0 0;}", 0 ); sheet.insertRule( "#div-gpt-ad-1659428980688-0 div{ height:"+(180*ww/320)+"px;width:"+320+"px;}", 0 ); } } } }); }); } else { document.write('
'); document.write('
'); }
正規表現エンジンを作ろう

正規表現エンジンを作ろう (1)

正規表現と数学


  • X ポスト
  • このエントリーをはてなブックマークに追加

 正規表現は、特に文字列操作が中心となるWeb分野におけるプログラミングにおいて、なくてはならない重要な機能です。本稿では正規表現を解釈するエンジンを実際に実装し、正規表現エンジンがどのように動いているのかを解説します。第1回は、正規表現の数学的な定義や実装方法の概要を紹介します。

  • X ポスト
  • このエントリーをはてなブックマークに追加

はじめに

 こんにちは。hirataraです。

 私が初めて正規表現を使ったのは、PerlによるCGIでの文字列処理でした。それから私はPerlを使い続け、今では正規表現なしのコーディングは考えられないほど、正規表現を当たり前の機能として日常的に使っています。昔は標準では正規表現をサポートしていなかったJavaも、今では正規表現をサポートするようになりました。Javaだけではなく、今日ではほとんどの高級言語にとって、正規表現はなくてはならない機能であると言っても過言ではないほどメジャーな機能となっています。

 本記事では、この正規表現の舞台裏に光を当てます。一見すると作ることが難しそうな正規表現エンジンですが、その根底には数学的な概念があり、その概念さえ知っていれば基礎となる機能の実装はそんなに難しくありません。この連載ではその数学的な概念をPythonを使って表現しながら、実際に動作する正規表現エンジンを作り上げます。

対象読者

 言語はPythonで書いていますが、Pythonの知識がない方でも、考え方は読んで理解して頂けると思います。以下を前提とします。

  • 基礎的なプログラミングができる方
  • 正規表現を使える方

 また、以下のような方に読んで頂けると幸いです。

  • 正規表現をもっと知りたい方
  • 情報科学分野に興味がある方
  • 正規表現エンジンを実装する必要がある方

正規表現とは何か

 ご存知の通り、正規表現は文字列を評価・操作するためにプログラミング言語に用意された機能の一つです。詳しくは『詳説 正規表現』(Jeffrey E.F. Friedl著)や『正規表現の問題集』(山岸賢治著)などを読んで頂けるのが一番ですが、念のため正規表現がどのようなものか、簡単に復習してみましょう。

Perl

 PerlやRubyでは、言語に正規表現を利用するための文法が直接組み込まれています。Perlにおいて、「=~」は Binding Operator と呼ばれ、左辺の文字列を右辺の正規表現で処理させるために用います。スラッシュの間(/~/)に挟まれた部分が正規表現として見なされます。以下の用例では $hoge の中に"[fb]oo"にマッチする部分文字列があるかを調べています。

Perlの正規表現
my $hoge = 'foo';
print "マッチしました\n" if $hoge =~ /[fb]oo/;

 なお、後述する他の言語と違ってPerlでは正規表現のコンパイル結果オブジェクトを意識せずに利用するインタフェースになってはいますが、 qr//表記を使ったり、 /~/o のように最後に o をつけると他の言語と同様にコンパイル結果を使い回すことができます。

Java

 Javaや.NET系等の言語では、正規表現の機能はライブラリとして提供されます。Javaでは、java.util.regexパッケージにPatternとMatcherと言うクラスがあります。Patternがコンパイルされた正規表現を表し、MatcherがPatternに対するマッチング実行時の情報を取り扱います。

 Matcherには、動作の微妙に違う find()、lookingAt()、matches() と言う3つのマッチング判定メソッドがあります。後の説明で使うので、以下のコードでこの3つの違いを確かめておきましょう。

Javaの正規表現
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test{
    static public void main(final String[] args){
        final Pattern p = Pattern.compile("[fb]oo");

        final String hoge1 = "foo";
        final String hoge2 = "foo bar";
        final String hoge3 = "hoge foo bar";

        for(final String str : new String[]{hoge1, hoge2, hoge3}){
           final Matcher m = p.matcher(str);
           System.out.println("【" + str + "のマッチ結果】");
           if(m.find()     ) System.out.println("findでマッチしました");
           if(m.lookingAt()) System.out.println("lookingAtでマッチしました");
           if(m.matches()  ) System.out.println("matchesでマッチしました");
        }

    }
}

/*
実行結果

【fooのマッチ結果】
findでマッチしました
lookingAtでマッチしました
matchesでマッチしました
【foo barのマッチ結果】
findでマッチしました
lookingAtでマッチしました
【hoge foo barのマッチ結果】
findでマッチしました
*/

 実行結果をみて分かる通り、matches()は完全マッチ、lookingAt()は前方一致によるマッチ、find()は部分文字列によるマッチの判定をします。

Python

 最後に、今回の実装言語に当たるPythonの事情を見ておきましょう。Pythonの正規表現のライブラリは reモジュール と言う名前で提供されています。

Pythonの正規表現
# -*- coding: utf-8 -*-
import re

hoge1 = "foo"
hoge2 = "foo bar"
hoge3 = "hoge foo bar"
regexp = re.compile(r"[fb]oo")

for str in (hoge1, hoge2, hoge3):
    print u"【%sのマッチ結果】" % hoge1
    if regexp.match(str) : print u"matchでマッチしました"
    if regexp.search(str): print u"searchでマッチしました"

# 実行結果
# 
# 【fooのマッチ結果】
# matchでマッチしました
# searchでマッチしました
# 【fooのマッチ結果】
# matchでマッチしました
# searchでマッチしました
# 【fooのマッチ結果】
# searchでマッチしました

 Pythonの正規表現オブジェクトにもmatch()とsearch()と言う2種類のマッチングメソッドがありますので、Javaと同様の比較を行ってみました。実行結果を見ると、 searchメソッドがJavaのfind()、matchメソッドがJavaのlookingAt()に一致すると言えそうです。

 reモジュールには、他にもfindallやfinditerのように、文字列全体を連続して走査してマッチ部分を一度に取り出すインタフェースが用意されています。

後方一致マッチはないの?

 JavaでもPythonでも、前方一致によるマッチがあるのに対して後方一致によるマッチのメソッドは用意されていません。これは、前方一致の方が正規表現エンジンの原理から言って実現方法が自然だからです。逆に後方一致(つまり部分一致マッチも)は多少強引な方法……具体的には、総当たりでパターンを当たる方法で実装されます。

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
正規表現と数学

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
正規表現エンジンを作ろう連載記事一覧

もっと読む

この記事の著者

hiratara(ヒラタラ)

1977年に苫小牧市で生まれる。北海道大学理学部数学科卒。小学生の頃、両親に買い与えられたMZ-2500でプログラミングを始めた。学生時代、CGIの自作に没頭し、それ以降WEB開発の魅力に憑かれる。社会人になっても数学好きは変わらず、専門書を買い集めるのが最近の趣味。id:hirataraにてblogを執筆...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/3039 2008/11/15 14:53
" ); }

おすすめ

アクセスランキング

  1. 1
    管理職の24.1%、今後管理職を「続けたくない」と回答。理由は「責任やストレス」が最多に
  2. 2
    NVIDIA、コンパクトな生成AIスーパーコンピューターを発表 NEW
  3. 3
    フロントエンドの定番ライブラリ「React 19」の新機能を紹介──アクションによる非同期処理の進化
  4. 4
    Linuxディストリビューション「Fedora Asahi Remix 41」リリース NEW
  5. 5
    ランサーズ、「2024年必要とされたスキルランキング」を公開。「Lancers」上のデータを集計
  1. 6
    いいエンジニアになるための2つのポイント ──元Google技術者・石原氏が説く「シリコンバレー流ソフトウェア開発術」
  2. 7
    「代替されない強み」を身に着ける覚悟はあるか──Java Champion 寺田佳央氏が経験してきた挫折とは
  3. 8
    IPA、DXの先進事例を素早く効率的に検索できるWebサイト「デジタル事例データベース」を公開
  4. 9
    「CUDA」 ~マンガでプログラミング用語解説
  5. 10
    Next.js 14までの進化を振り返る──App Routerを強化する新機能を解説! NEW

アクセスランキング

  1. 1
    管理職の24.1%、今後管理職を「続けたくない」と回答。理由は「責任やストレス」が最多に
  2. 2
    NVIDIA、コンパクトな生成AIスーパーコンピューターを発表 NEW
  3. 3
    フロントエンドの定番ライブラリ「React 19」の新機能を紹介──アクションによる非同期処理の進化
  4. 4
    Linuxディストリビューション「Fedora Asahi Remix 41」リリース NEW
  5. 5
    ランサーズ、「2024年必要とされたスキルランキング」を公開。「Lancers」上のデータを集計
  6. 6
    いいエンジニアになるための2つのポイント ──元Google技術者・石原氏が説く「シリコンバレー流ソフトウェア開発術」
  7. 7
    「代替されない強み」を身に着ける覚悟はあるか──Java Champion 寺田佳央氏が経験してきた挫折とは
  8. 8
    IPA、DXの先進事例を素早く効率的に検索できるWebサイト「デジタル事例データベース」を公開
  9. 9
    「CUDA」 ~マンガでプログラミング用語解説
  10. 10
    Next.js 14までの進化を振り返る──App Routerを強化する新機能を解説! NEW
  1. 1
    いいエンジニアになるための2つのポイント ──元Google技術者・石原氏が説く「シリコンバレー流ソフトウェア開発術」
  2. 2
    「CUDA」 ~マンガでプログラミング用語解説
  3. 3
    ITエンジニア本大賞2025、投票締切直前! みんなで選んだ歴代の大賞本を振り返って一挙紹介
  4. 4
    デスクトップアプリを開発しよう! 「Rust」と「Tauri 2.0」の基本情報と環境整備の仕方を解説
  5. 5
    今後生成AIとどう向き合うべきなのか? 現場のエンジニアと研究者が最新研究事例から語り合う
  6. 6
    2024年12月に開催される注目のITエンジニア向けカンファレンス5選
  7. 7
    日本在住の英語を話すソフトウェア開発者、年収の中央値は950万円に
  8. 8
    Vue.js3.4~3.5の新機能をまとめて紹介! 新しいAPIやSSRの改善
  9. 9
    VSCodeをドキュメント作成に活用――テキストエディタ、Markdownエディタの設定と拡張機能を解説
  10. 10
    2024年の提示年収が高いプログラミング言語は? paiza調査によるランキングが発表

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

メールバックナンバー

アクセスランキング

  1. 1
    管理職の24.1%、今後管理職を「続けたくない」と回答。理由は「責任やストレス」が最多に
  2. 2
    NVIDIA、コンパクトな生成AIスーパーコンピューターを発表 NEW
  3. 3
    フロントエンドの定番ライブラリ「React 19」の新機能を紹介──アクションによる非同期処理の進化
  4. 4
    Linuxディストリビューション「Fedora Asahi Remix 41」リリース NEW
  5. 5
    ランサーズ、「2024年必要とされたスキルランキング」を公開。「Lancers」上のデータを集計
  1. 6
    いいエンジニアになるための2つのポイント ──元Google技術者・石原氏が説く「シリコンバレー流ソフトウェア開発術」
  2. 7
    「代替されない強み」を身に着ける覚悟はあるか──Java Champion 寺田佳央氏が経験してきた挫折とは
  3. 8
    IPA、DXの先進事例を素早く効率的に検索できるWebサイト「デジタル事例データベース」を公開
  4. 9
    「CUDA」 ~マンガでプログラミング用語解説
  5. 10
    Next.js 14までの進化を振り返る──App Routerを強化する新機能を解説! NEW

アクセスランキング

  1. 1
    管理職の24.1%、今後管理職を「続けたくない」と回答。理由は「責任やストレス」が最多に
  2. 2
    NVIDIA、コンパクトな生成AIスーパーコンピューターを発表 NEW
  3. 3
    フロントエンドの定番ライブラリ「React 19」の新機能を紹介──アクションによる非同期処理の進化
  4. 4
    Linuxディストリビューション「Fedora Asahi Remix 41」リリース NEW
  5. 5
    ランサーズ、「2024年必要とされたスキルランキング」を公開。「Lancers」上のデータを集計
  6. 6
    いいエンジニアになるための2つのポイント ──元Google技術者・石原氏が説く「シリコンバレー流ソフトウェア開発術」
  7. 7
    「代替されない強み」を身に着ける覚悟はあるか──Java Champion 寺田佳央氏が経験してきた挫折とは
  8. 8
    IPA、DXの先進事例を素早く効率的に検索できるWebサイト「デジタル事例データベース」を公開
  9. 9
    「CUDA」 ~マンガでプログラミング用語解説
  10. 10
    Next.js 14までの進化を振り返る──App Routerを強化する新機能を解説! NEW
  1. 1
    いいエンジニアになるための2つのポイント ──元Google技術者・石原氏が説く「シリコンバレー流ソフトウェア開発術」
  2. 2
    「CUDA」 ~マンガでプログラミング用語解説
  3. 3
    ITエンジニア本大賞2025、投票締切直前! みんなで選んだ歴代の大賞本を振り返って一挙紹介
  4. 4
    デスクトップアプリを開発しよう! 「Rust」と「Tauri 2.0」の基本情報と環境整備の仕方を解説
  5. 5
    今後生成AIとどう向き合うべきなのか? 現場のエンジニアと研究者が最新研究事例から語り合う
  6. 6
    2024年12月に開催される注目のITエンジニア向けカンファレンス5選
  7. 7
    日本在住の英語を話すソフトウェア開発者、年収の中央値は950万円に
  8. 8
    Vue.js3.4~3.5の新機能をまとめて紹介! 新しいAPIやSSRの改善
  9. 9
    VSCodeをドキュメント作成に活用――テキストエディタ、Markdownエディタの設定と拡張機能を解説
  10. 10
    2024年の提示年収が高いプログラミング言語は? paiza調査によるランキングが発表