千葉大学普遍教育情報処理科目:自習用テキスト「プログラミング入門」

生物生産科学科 担当 國分 尚


<-- 第4章目次第6章 -->


5.ファイル処理と正規表現

  1. Genbankの出力を加工する
  2. ファイルからデータを読み込む
  3. ファイルにデータを書き込む
  4. 正規表現

コンピュータが電子計算機と呼ばれた頃は数値計算をさせるのが最も一般的な使い方であった。しかし、今日では文章を扱ったり、画像の編集をしたり、音楽を聴いたりすることの方が圧倒的に多く、「情報処理機」と呼ぶほうが的確であろう。

5−1.Genbankの出力を加工する

ここでは文字情報のファイルを扱う実用例としてGenBank (http://www.ncbi.nlm.nih.gov/)という遺伝子情報データベースの出力を使いやすい形に書き換えるプログラムを作ってみる。

GenBankからの出力は次のような形をしている。

genbank.txt(抜粋)

さて、この形式のファイルが与えられたが、あなたが必要なのはそのうちのわずかな情報で、しかも塩基配列はすべて1行につながっていて数字や空白がない方が都合がいいとしたらどうするだろう。

エディタやワープロで編集することもできるが、せいぜい数十レコードまでであろう。それ以上は人間がすべき仕事ではない。このような単純作業こそコンピュータが最も得意とする分野なのだから。

そこで、このようなプログラムを作ってみる。必要な情報は塩基配列の定義[DEFINITION]、レコード番号[ACCESSION]、核酸が取られた生物の名前[SOURCE]、配列そのもの[ORIGIN]の数字と改行、空白を除いたもの、とする。


プログラム9.genbank.rb


# GenBank output formatting program
#  H. Kokubun
#  ver 0.1 30 Apr 2004 The first rendation
#  ver 0.2   1 May 2004 Selected some fields to display.

while line = ARGF.gets  #コマンドラインで指定したファイルから1行読み込み、
  #まだファイルの終わりでない間繰り返す
  if /LOCUS/ =~ line
    #読んだ行の中にLOCUSという文字列があれば、レコードの始まりと見なす
    print "\n----------\n",line  #レコードの始まりなので区切り線を書く
  elsif /DEFINITION|ACCESSION|SOURCE/ =~ line
    #これらの文字列を含む行が必要なので、出力する
    print line
  elsif /ORIGIN/ =~ line
    #塩基配列の場合の処理
    print line
    line = ARGF.gets
    seq = ""
    begin
      line.scan /[A-Za-z]+/ do | aChunk |
        seq << aChunk
      end
      line = ARGF.gets
    end until /\/\// =~ line
    print seq, "\n"
  end
end

5−2.ファイルからデータを読み込む

この章の初めへ
while line = ARGF.gets
  あれこれ
end

この形がRubyでファイルからデータを読み込む基本となる。ARGFとは特殊なファイルで、


keyaki% ruby test.rb a.txt b.txt c.txt

という形でコマンドが与えられたとき、a.txt、b.txt、c.txtの3つのファイルから1行づつデータを読み出す。最後のファイルの最後に達するとnilという特別な値が返り、whileの条件を満たさなくなるのでループから脱出する。

例えば、UNIXのcatコマンドをRubyで書くと


プログラム10.cat.rb


while line = ARGF.gets
  print line
end

と、たった3行で済んでしまう。

5−3.ファイルにデータを書き込む

この章の初めへ

Rubyでファイルにデータを書き込む方法も当然あるが(IOクラスのputsメソッドを使う)、今回はOSのリダイレクションを使うことにする。コマンドラインで


keyaki% ruby fact.rb 100 > output.txt

とすればprint文の出力は画面ではなくoutput.txtというファイルに保存される。リダイレクションについては「キャンパス情報リテラシー」p. 90を参照のこと。今回の例題ではこれで十分なのでRubyのファイルの扱いに関しては参考書で勉強してほしい。

5−4.正規表現

この章の初めへ

genbank.rbのプログラム中には /あれこれ/ =~ 変数 という部分が多くあるが、/あれこれ/が正規表現と呼ばれるもので、文字列中にあるパターンが含まれる(マッチする)かどうかを見つける手段である。文字列の検索や加工には欠かすことができない。正規表現はそれ自体がミニプログラミング言語であると言われるほど複雑なことができ、その説明だけで1冊の本が書けるほどである。ここではプログラム9.の例について最低限のものにしぼって説明する。

ア.特殊文字以外はその文字列と一致するものとマッチする

  /ORIGIN/ =~ line

この表現はlineの中に“ORIGIN"という文字列があればマッチし、式の値が真になる。マッチした部分は$~という変数に格納されている。

イ.|で区切ると区切りの両側の表現のどちらかにマッチする

  /REFERENCE|FEATURES|COMMENT/ =~ line

この表現はlineの中に“REFERENCE"、“FEATURES"、“COMMENT"という文字列のいずれかがあればマッチする。複数ある時は最も左側のものにマッチする。

ウ.[ ]の中のどれか1文字にマッチする

  /[A-Za-z]/ =~ line

この表現はlineの中の最初の英文字にマッチし、その文字を返す。A-ZはABCDEF...XYZを省略した表記法である。

エ.+は直前の表現の1回以上の繰り返し、*は0回以上の繰り返し、?は0回または1回、{m,n}はm回以上n回以下に相当する

オ.\sは空白文字(スペース、タブ、改行)、\nは改行、\tはタブ、^は行頭、$は行末、.は任意の1文字にマッチする

  /^REFERENCE\s+1\s/ =~ line

この表現はlineの先頭に“REFERENCE"があり、最低1文字のスペースのあとに“1"があり、その直後にスペースがあるような文字列にマッチする。

REFERENCE       1 abcde  マッチする
a REFERENCE    1  12345  マッチしない(REFERENCEが先頭にない)
REFERENCE    12          マッチしない(1の直後がスペースでない)

カ.正規表現の中で特別な意味がある文字(メタ文字)を本来の文字として使う時は前に\を付ける

  /\/\// =~ line

この表現は//にマッチする。

キ.( )で表現をグループ化できる

  /abc+/ はabc, abcc, abcccなどにマッチするのに対し
  /(abc)+/ はabc, abcabc, abcabcabcなどにマッチする。

練習10.genbank.rbを書き換えて塩基配列の中に特定な配列、例えばtcga(制限酵素TaqIの認識配列)があったらその配列の前後20塩基までを出力するようにせよ。次の正規表現を使うと簡単にできる。


if /.{0,20}tcga.{0,20}/ =~ seq
  print "Taq I recognition site: ",$~,"\n"
end

.はどんな1文字にもマッチする。{0,20}は直前の表現を0から20回繰り返したものにマッチする(この書き方はgrepやawkでは使えない)。Rubyの正規表現は最長一致を原則としているのでtcgaの前に20文字以上あれば20文字がマッチする。後ろも同様である。$~に直前にマッチした文字列が格納されている。

練習11.genbankの出力はこのままではExcelなどの表計算ソフトに取り込むことはできない。そのためには一般に1レコードを1行とし、各データをタブという特殊文字( \t で表す)で区切って書き出すのが便利である。これをタブ区切りテキストと言う。

そこで、上のgenbank.txtを読んで各レコードを
LOCUS \t ACCESSION \t 長さ \t SOURCE \n
のような形式で出力するプログラムを作成する。
例えば

LOCUS       PHMYBPH22       1057 bp    mRNA    linear   PLN 
12-SEP-1993
DEFINITION  P.Hybrida myb.Ph2 gene encoding protein 2.
ACCESSION   Z13997
SOURCE      Petunia x hybrida

というレコードなら
PHMYBPH22\tZ13997\t1057\tPetunia x hybrida\n
のようにする。

LOCUS名と長さはLOCUSの行の2番目と3番目の単語を取り出すようにすればよいだろう。1行のデータを単語に分けるには文字列のsplitメソッドを使う。


words = line.split()

とするとwordsという配列にlineの内容を空白で分割した文字列が入る。上のLOCUSの行であればwords[0]はLOCUS, words[2]は1057となる。


目次

  1. アルゴリズムとプログラム
  2. プログラミング言語
  3. Rubyプログラミング入門
    1. 環境を整える
    2. エディタの使い方
    3. Hello World!
    4. 台形の面積を求める
    5. Rubyプログラムの実行
  4. Rubyプログラムの構造
    1. 文、数値、文字列、変数、代入
    2. データの型、あるいはオブジェクトの種類
    3. 配列とハッシュ
    4. 制御構造
    5. アルゴリズムからプログラムへ
    6. オブジェクトとメソッド
  5. ファイル処理と正規表現
    1. Genbankの出力を加工する
    2. ファイルからデータを読み込む
    3. ファイルにデータを書き込む
    4. 正規表現
  6. その他
  7. 参考文献
  8. 用語集
  9. 練習問題の解答例
  10. 索引

情報処理のトップへ戻る


2004年5月14日作成、2006年4月18日更新

國分 尚
hkokubun@faculty.chiba-u.jp