もりきゅうです。
パターンマッチ(Pattern Matching)を使うと、ファイルから興味のある部
分だけ抜き出すことができます。パターンマッチの話題の中心は正規表現
ですが、正規表現だけではパターンマッチ処理は書けません。繰り返し・
条件分岐・特殊変数など、さまざまな文法要素が合わさってひとつのプロ
グラムになります。これらはどれも欠かすことのできない重要な要素なの
です。
■ パターンマッチ
TSruby を含む行だけ出力するために、条件を書きます。
while line = gets
print line if /TSruby/ =~ line
end
ここで if は条件分岐です。
何か行う if 条件
ここでの条件は
/TSruby/ =~ line
これがパターンマッチです。// に挟まれた TSruby がパターンで、line
がマッチ対象となる文字列です。
パターンは正規表現(Regular Expression)で記述します。
TSruby は単なる文字列ですが、これも正規表現のひとつの形です。T の
次に S がきて r がきて u がきて b がきて y がくるパターンを表現し
ています。
■ 正規表現
正規表現は奥深い世界です。詳しくはドキュメント[*1]を参照していた
だくことにして、よく使う例をいくつか挙げます。
/\bTSruby\b/
\b は単語境界
HITSruby や TSrubyist にはマッチしない
/^TSruby/
^ は行頭
TSruby で始まる行にマッチする
/TSruby$/
$ は行末
TSruby で終わる行にマッチする
/^$/
空行にマッチする
/./
. は一文字にマッチする[*2]
/(TS)?ruby/
? は 0回または 1回
TSruby と ruby にマッチする
/TS(abc|free)/
| は「または(or)」
TSabc と TSfree にマッチする
/[a-zA-Z]+/
[] は文字クラス(文字をまとめる)
文字クラス中の a-z は a から z までの文字
アルファベットにマッチする
/\b[A-Z]\w*/
\w は英数字 [a-zA-Z0-9_] と同じ[*2]
* は 0回以上
Rubyの定数名にマッチする
このように、正規表現はそれ自体が言語です。Rubyの中に正規表現を埋め
込めるわけですね。
*1
http://www.ruby-lang.org/ja/doc.html
*2
. や \w は文字コードを指定すると漢字にもマッチします。文字コード
については、後日「日本語の扱い」としてまとめて説明したいと思います。
■ ほかの書き方
文字列とパターンを逆に書くこともできます。
print if line =~ /TSruby/
隠れた変数 $_ を利用すれば
while gets
print if ~ /TSruby/ # あるいは print if /TSruby/
end
と楽に書くことができます。でも $_ は見えないので説明しにくいです。
■ 条件の書き方
if ... end を使ってもいいですし[*1]
if /TSruby/ =~ line
print line
end
論理演算子 and を使ってもいいです。
/TSruby/ =~ line and print line
*1
Ruby の if ... end は文ではなく式です。だから値を返せます。詳し
くは今後の講座で。
■ 正規表現のオプション
TSruby だけでなく tsRuby にもマッチしたい(大文字小文字を区別しな
い)ときには i オプション(IGNORECASE)を付けます。
/TSruby/i =~ line
m オプション(MULTILINE)を付けると . は改行にもマッチします。
/./m =~ line
さて、プログラム外部からパターンを指定できるようにしてみましょう。
--^ grep0.rb
keyword = ARGV.shift
while line = gets
print line if /#{keyword}/o =~ line
end
--$
$ ruby grep0.rb TSruby rec1.txt rec2.txt
正規表現に変数(だけでなく式全般ですが)を埋め込むとき、内容が変わ
らないのなら、o オプションを使うと一度だけ式展開されるので速度を稼
げます。
しかし、よりRuby的な解決方法があります。
■ 正規表現オブジェクト
--^ grep1.rb
keyword = ARGV.shift
keyword_re = Regexp.compile(keyword)
while line = gets
print line if keyword_re =~ line
end
--$
文字列から正規表現を作り出す Regexp.compile を使ってみました。
i オプションはどうやって指定するのでしょう? ドキュメントで調べて
みましょう。
keyword_re = Regexp.compile(keyword, Regexp::IGNORECASE)
■ ARGV と ARGF
ARGV はプログラムに渡された引数を保持する配列の定数名です。
shift は配列の先頭の要素を取って返します。つまり最初の引数を返しま
す。ここで
keyword = ARGV.shift
のかわりに
keyword = ARGV[0]
と書くとうまくいきません。
grep0.rb:2:in `gets': No such file or directory - "TSruby" (Errno:
:ENOENT)
ARGV[0] は 0番目の引数、つまり最初の引数です。Rubyの配列は 0番から
数えます。返すものは shift の場合と同じだから keyword は問題ないん
です。ではどこがまずいのか? それは gets です。いままで秘密にしてい
ましたが gets の正体は ARGF.gets 。ARGV の要素をファイル名とみなし
て、それらを連結したひとつの仮想ファイルを ARGF は表します。
ARGV[0] は ARGV.shift と違って配列自体は変わりません。だから
TSruby というファイルを探しにいってしまったのです。
----
YOSHIDA Kazuhiro moriq@... http://www.moriq.com/