はじめに
今回は、
正規表現とは
さて、
基本的な演算
正規表現では、
- 量化:
- 語を繰り返す。一般に用いられる演算子*から、
スター演算とも言う。結合則は強い。 - 連接:
- 語と語をつなぐ。
- 選言:
- ある語か別の語かのどちらか。結合則は弱い。
演算子
Rubyでは、
具体的には後述しますが、
基本的な正規表現
簡単な物から見ていきましょう。まず、
しかし、
数値をカンマで区切る
大きな数値を表記する際は、
str = "1000000"
ary = []
n = 0; new = []
str.split(//).reverse.each do |x|
new.push(x)
new.push(',') if (n += 1) % 3 == 0
end
p new.reverse.join #=> "1,000,000"
しかし、
str = "1000000"
1 while str.gsub!(/([0-9])((?:[0-9]{3})+)¥z/, '¥1,¥2')
p str #=> "1,000,000"
先読み
さて、
p "1000000".gsub(/([0-9])(?=(?:[0-9]{3})+¥z)/, '¥1,')
#=> "1,000,000"
戻り読み
ここまでできてしまうと、
# Ruby 1.9やJRuby、IronRuby等限定
p "1000000".gsub(/(?<=[0-9])(?=(?:[0-9]{3})+¥z)/, ',')
#=> "1,000,000"
Quoted string
もう1つ、
/¥A"[^"]*"¥z/ =~ '"hoge"' #=> 0
/¥A"[^"]*"¥z/ =~ '"ho¥"ge"' #=> nil
/¥A"(?:¥¥.|[^"¥¥])*"¥z/ =~ '"ho¥"ge"' #=> 0
指数関数的発散
ここで前述の正規表現をstrfriendで視覚化してみましょう。¥Aと¥zに対応していないのはご愛敬として、
かといって、
# 注意: 時間のかかる処理です
/¥A"(?:¥¥.|[^"¥¥]+)*"¥z/ =~ '"aaaaaaaaaaaaaaaaaaaaaaaa'
ループ展開
この問題の対処法については詳説正規表現の6.
- AとBの先頭文字が異なる
- Bが空文字にマッチしない
- B内部ではバックトラックしない
(Bがマッチする文字列の部分に、 Bはマッチしない)
A、
r = /¥A"[^"¥¥]*(¥¥.[^"¥¥]*)*"¥z/
r =~ '"aaaaaaaaaaaaaaaaaaaaaaaa'
正規表現の限界
これまでに述べたとおり、
入れ子は正規表現の属する正規文法では表現できず、
部分式呼出し
さて、
具体的には、
r = /¥A(?<paren>¥(¥g<paren>*¥))*¥z/
r =~ "()" #=> 0
r =~ "(()())()" #=> 0
r =~ ")" #=> nil
JSON
最近Web方面で引っ張りだこの、
r = Regexp.compile(<<'__REGEXP__'strip, Regexp::EXTENDED)
(?<json> ¥g<object> | ¥g<array> ){0}
(?<begin-array> ¥g<ws> ¥[ ¥g<ws> ){0}
(?<begin-object> ¥g<ws> ¥{ ¥g<ws> ){0}
(?<end-array> ¥g<ws> ¥] ¥g<ws> ){0}
(?<end-object> ¥g<ws> ¥} ¥g<ws> ){0}
(?<name-separator> ¥g<ws> : ¥g<ws> ){0}
(?<value-separator> ¥g<ws> , ¥g<ws> ){0}
(?<ws> [¥x20¥t¥n¥r]* ){0}
(?<value> false | null | true | ¥g<object> | ¥g<array> | ¥g<number> | ¥g<string> ){0}
(?<object> ¥g<begin-object> (?: ¥g<member> (?: ¥g<value-separator> ¥g<member> )* )? ¥g<end-object> ){0}
(?<member> ¥g<string> ¥g<name-separator> ¥g<value> ){0}
(?<array> ¥g<begin-array> (?: ¥g<value> (?: ¥g<value-separator> ¥g<value> )* )? ¥g<end-array> ){0}
(?<number> ¥-? ¥g<int> ¥g<frac>? ¥g<exp>? ){0}
(?<exp> [eE] [-+] [0-9]+ ){0}
(?<frac> ¥. [0-9]+ ){0}
(?<int> 0 | [1-9] [0-9]* ){0}
(?<string> " ¥g<char>* " ){0}
(?<char> [^¥x00-¥x1F"¥¥] | ¥¥ (?: ["¥¥¥/¥b¥f¥n¥r¥t] | u [0-9a-fA-F]{4} ) ){0}
¥A¥g<json>¥z
__REGEXP__
r =~ '{"foo":1,"bar":null,"baz":true}' #=> 0
後方参照
テキスト処理用の正規表現エンジンでは、
後方参照を用いると、
r = /¥A(?:<([^>]+)>[^<]*<¥/¥1>)*¥z/
ネストレベル付き後方参照
鬼車にはネストレベル付き後方参照という機能があります。これは部分式呼び出しによる入れ子構造へのマッチを行っている際に用います。このような場合に後方参照を用いようとすると、
# http://www.geocities.jp/kosako3/oniguruma/doc/RE.ja.txt より
p /¥A(?<a>|.|(?:(?<b>.)¥g<a>¥k<b+0>))¥z/.match("reer")
XML
XML整形式もJSONと同じく文脈自由文法ですが、
r = Regexp.compile(<<'__REGEXP__'strip, Regexp::EXTENDED)
(?<element> ¥g<stag> ¥g<content>* ¥g<etag> ){0}
(?<stag> < ¥g<name> ¥s* > ){0}
(?<name> [a-zA-Z_:]+ ){0}
(?<content> [^<&]+ (¥g<element> | [^<&]+)* ){0}
(?<etag> </ ¥k<name+1> >){0}
¥A¥g<element>¥z
__REGEXP__
まとめ
今日のテキスト処理には欠かせない正規表現も、
もっとも、