Ruby Freaks Lounge

第6回 Ruby M17N 事始め:正規表現編

この記事を読むのに必要な時間:およそ 4.5 分

はじめに

今回は,今話題の正規表現について,その歴史と限界,そして限界を越えた先までを解説します。なお,るりまるびまの記事もあわせて読んでみてください。

正規表現とは

さて,正規表現はもともと1940年代に神経生理学者によって生まれ※1),数学者※2や言語学者,情報工学者たちによって理論的背景が研究されてきました。これを最初にコンピュータで用いたのがKen Thompsonによるqed※3で,それ以降正規表現はテキスト処理に欠かせないツールとして愛されてきました※4)。

※1)
A logical calculus of the ideas immanent in nervous activity
※2)
Representation of Events in Nerve Nets and Finite Automata
※3)
qedの正規表現にマッチした行を表示するコマンド,g/regular expression/p = Global Regular Expression Printがgrepコマンドの由来
※4)
詳説 正規表現 第三版 P.83
http://msdn.microsoft.com/ja-jp/library/cc427944.aspx

基本的な演算

正規表現では,量化・連結・選言という3つの演算を用いて,目当ての文字列群だけを識別する規則を記述します※5)。

量化:
語を繰り返す。一般に用いられる演算子*から,スター演算とも言う。結合則は強い。
連接:
語と語をつなぐ。
選言:
ある語か別の語かのどちらか。結合則は弱い。
※5)
正規表現の正確な定義は形式言語理論における正規表現など

演算子

Rubyでは,さまざまな演算子を用いて規則を記述します。GNU regexベースの1.8.7鬼車ベースの1.9.1で機能が異なるので対応するマニュアルを参照してください。

具体的には後述しますが,1.9系では戻り読み「(?<= ... )」や否定戻り読み「(?<! ... )」等の機能が追加されており,より複雑な規則を記述できます。また,Ruby M17Nへの対応も行われています。

基本的な正規表現

簡単な物から見ていきましょう。まず,自然数を表す文字列にマッチする正規表現はどうなるでしょうか。/[0-9]+/だと001等も入ってしまいますね。つまり,最初の文字は[1-9]で,2文字目以降は[0-9]になります。0を忘れずに加えると,/[1-9][0-9]*|0/となります。

しかし,これでもまだ不十分です。なぜかというと,"001"の最初の1文字だけにマッチしてしまうのです。今までの物をグループ化した上で,文字列先頭にマッチする¥A,文字列末尾にマッチする¥zを加えた,/¥A([1-9][0-9]*|0)¥z/が正解です。

数値をカンマで区切る

大きな数値を表記する際は,しばしば3桁おきにカンマで区切ることがあります。原始的に書くならば以下のような感じでしょうか。

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"

しかし,これだと少し大がかりすぎます。正規表現を使ってみると,例えば以下のようになります。数字の後に3の倍数個数字が末尾まで続いている場合,間にカンマを入れるという仕組みです。(?: ... )は「キャプチャしない括弧」と言ってマッチした内容を後から参照できるようにしません。

str = "1000000"
1 while str.gsub!(/([0-9])((?:[0-9]{3})+)¥z/, '¥1,¥2')
p str #=> "1,000,000"

先読み

さて,本来正規表現には量化・連接・選言の3つの演算しかないのですが,ツールとしての利便性を追求した結果,最近の実装ではその枠を超えた機能が追加されています。その1つが先読み(?= ... )と否定先読み(?! ... )で,これはEcmaScript 3rd Editionの正規表現にも含まれているのでなかなか使える機能です。これを用いると今までのは,1行で書けるようになってしまいます。

p "1000000".gsub(/([0-9])(?=(?:[0-9]{3})+¥z)/, '¥1,')
#=> "1,000,000"

著者プロフィール

成瀬ゆい(なるせゆい;田中隆裕)

nkf メンテナ,Ruby コミッタ。2004年にnkfライブラリをUTF-8対応版にするために開発に参加。以後文字列周りを中心にいじる。また、去年から就職したソフトバンク・テクノロジーでRailsや携帯電話と戯れる。まさか仕事でRubyをいじることになるとは。

コメント

コメントの記入