Forgexは、すべてFortranで書かれた正規表現エンジンです。
このプロジェクトはFortranパッケージマネージャーで管理され、 正規表現の基本的な処理を提供し、MITライセンス のもとで利用可能なフリーソフトウェアです。 エンジンの核となるアルゴリズムには決定性有限オートマトン(Deterministic Finite Automaton, DFA)を使用しています。 この選択は実行時パフォーマンスを重視したものです。
Forgexが処理を受け付ける正規表現の記法は以下の通りです。
|
選言(alternation)のバーティカルバー*
ゼロ回以上にマッチするアスタリスク+
一回以上にマッチするプラス記号?
ゼロ回または一回にマッチするクエスチョンマーク\
メタキャラクターのエスケープ.
任意の一文字にマッチするピリオド[a-z]
)[^a-z]
)[α-ωぁ-ん]
)否定クラスは制御文字にはマッチしないことに注意してください。
{num}
,{,max}
,{min,}
,{min, max}
,
ここで num
とmax
は0(ゼロ)以外の自然数を指定します。^
, 行頭にマッチ$
, 行末にマッチ\t
, タブ文字\n
, 改行文字 (LFまたはCRLF)\r
, 復帰文字 (CR)\s
, 空白文字 (半角スペース, タブ文字, CR, LF, FF, 全角スペース U+3000)\S
, 非空白文字\w
, ラテン文字アルファベット、半角数字及びアンダースコア([a-zA-Z0-9_]
)\W
, \w
の否定クラス([^a-zA-Z0-9_]
)\d
, 半角数字 ([0-9]
)\D
, 非半角数字 ([^0-9]
)ドキュメントは英語と日本語で次のリンクから利用可能です。 https://shinobuamasaki.github.io/forgex.
動作確認は以下のコンパイラーで行っています。
gfortran
) v13.2.1ifx
) 2024.0.0 20231017以下では、ビルドとAPIの使い方について解説しますが、Fortranパッケージマネージャー(fpm
)を利用することを前提とします。
まず初めに、あなたのプロジェクトのfpm.toml
に以下の記述を追加します。
[dependencies]
forgex = {git = "https://github.com/shinobuamasaki/forgex", tag="v2.0"}
そのプロジェクトのプログラムのヘッダーにuse forgex
と記述すると、.in.
と.match.
の演算子、
regex
サブルーチンとregex_f
関数が導入され、use
文の有効なスコープでこれらの4つを使用することができます。
program main
use :: forgex
implicit none
.in.
演算子は、文字列型を引数にとり、第一引数のパターンが、第二引数の文字列に含まれる場合に真を返します。
block
character(:), allocatable :: pattern, str
pattern = 'foo(bar|baz)'
str = "foobarbaz"
print *, pattern .in. str ! T
str = "foofoo"
print *, pattern .in. str ! F
end block
.match.
演算子は、同様に指定されたパターンが、厳密に文字列と一致する場合に真を返します。
block
character(:), allocatable :: pattern, str
pattern = '\d{3}-\d{4}'
str = '100-0001'
print *, pattern .match. str ! T
str = '1234567'
print *, pattern .match. str ! F
end block
regex
関数は、入力文字列の中でパターンに一致した部分文字列を返します。
block
character(:), allocatable :: pattern, str, res
integer :: length
pattern = 'foo(bar|baz)'
str = 'foobarbaz'
call regex(pattern, str, res)
print *, res ! foobar
! call regex(pattern, str, res, length)
! the value 6 stored in optional `length` variable.
end block
オプショナル引数のfrom
/to
を使用すると、与えた文字列から添字を指定して部分文字列を切り出すことができます。
block
character(:), allocatable :: pattern, str, res
integer :: from, to
pattern = '[d-f]{3}'
str = 'abcdefghi'
call regex(pattern, str, res, from=from, to=to)
print *, res ! def
! The `from` and `to` variables store the indices of the start and end points
! of the matched part of the string `str`, respectively.
! Cut out before the matched part.
print *, str(1:from-1) ! abc
! Cut out the matched part that equivalent to the result of the `regex` function.
print *, str(from:to) ! def
! Cut out after the matched part.
print *, str(to+1:len(str)) ! ghi
end block
regex
関数の宣言部(インタフェース)は次の通りです。
interface regex
module procedure :: subroutine__regex
end interface
pure subroutine subroutine__regex(pattern, text, res, length, from, to)
implicit none
character(*), intent(in) :: pattern, text
character(:), allocatable, intent(inout) :: res
integer, optional, intent(inout) :: length, from, to
マッチした文字列を関数の戻り値として得たい場合には、regex_f
関数を使用してください。
interface regex_f
module procedure :: function__regex
end interface regex_f
pure function function__regex(pattern, text) result(res)
implicit none
character(*), intent(in) :: pattern, text
character(:), allocatable :: res
UTF-8の文字列についても、ASCII文字と同様に正規表現のパターンで一致させることができます。 以下の例は、漢文の一節に対してマッチングを試みています。
block
character(:), allocatable :: pattern, str
integer :: length
pattern = "夢.{1,7}胡蝶"
str = "昔者莊周夢爲胡蝶 栩栩然胡蝶也"
print *, pattern .in. str ! T
call regex(pattern, str, res, length)
print *, res ! 夢爲胡蝶 栩栩然胡蝶
print *, length ! 30 (is 3-byte * 10 characters)
end block
この例ではlength
変数にバイト長が格納され、この場合は10個の3バイト文字に一致したので、その長さは30となります。
バージョン3.2以降では、Forgexエンジンを使用したコマンドラインツールforgex-cli
が提供されてり、Forgexエンジン自体のデバッグ、正規表現マッチングのテストやベンチマークのために使用することができます。
以下のようにコマンドを実行することで、標準出力に結果を得ることができます。
使い方の詳細についてはドキュメンテーションを参照してください。
コマンド:
forgex-cli find match lazy-dfa '([a-z]*g+)n?' .match. 'assign'
fpm run
経由で実行する場合:
fpm run forgex-cli --profile release -- find match lazy-dfa '([a-z]*g+)n?' .match. 'assign'
出力:
pattern: ([a-z]*g+)n?
text: 'assign'
parse time: 46.5us
compile nfa time: 74.9us
dfa initialize time: 78.4us
search time: 661.7us
matching result: T
memory (estimated): 10380
========== Thompson NFA ===========
state 1: (?, 5)
state 2: <Accepted>
state 3: (n, 2)(?, 2)
state 4: (g, 7)
state 5: (["a"-"f"], 6)(g, 6)(["h"-"m"], 6)(n, 6)(["o"-"z"], 6)(?, 4)
state 6: (?, 5)
state 7: (?, 8)
state 8: (g, 9)(?, 3)
state 9: (?, 8)
=============== DFA ===============
1 : ["a"-"f"]=>2
2 : ["o"-"z"]=>2 ["h"-"m"]=>2 g=>3
3A: n=>4
4A:
state 1 = ( 1 4 5 )
state 2 = ( 4 5 6 )
state 3A = ( 2 3 4 5 6 7 8 )
state 4A = ( 2 4 5 6 )
===================================
gfortran
でコンパイルされたプログラムでは、OpenMPの並列ブロックの中で割り付け可能文字列型変数を使用すると、セグメンテーション違反などでプログラムが停止する可能性があります。forgex-cli
をWindows上のPowerShellで利用する場合、Unicode文字を正しく入出力するには、システムのロケールをUTF-8に変更する必要があります。\p{...}
の追加pure elemental
属性を追加本プロジェクトに含まれるすべてのコードは、3スペースのインデントで記述されます。
冪集合構成法のアルゴリズムと構文解析については、Russ Cox氏の論文と近藤嘉雪氏の本を参考にしました。
優先度付きキューの実装は、ue1221さんのコードに基づいています。
文字列に対して.in.
演算子を適用するというアイデアは、soybeanさんのものにインスパイアされました。
forgex-cli
のコマンドラインインターフェイスの設計については、Rust言語のregex-cli
を参考にしました。
このプロジェクトはMITライセンスで提供されるフリーソフトウェアです (cf. LICENSE)。