(8)06/08の授業内容

  1. 正規表現とは
  2. 電話帳で「ハーブプラス」というお店の電話番号を探すことを考えます.電話帳ではお店の名前が50音順に並んでいますから,「ハ」の所を見れば書いてあることがわかります.では「髪ing」という美容室の電話番号はどのようにして調べればよいでしょうか? この場合「カミイング」と読むかもしれませんし,「ハツイング」や「ヘアイング」かもしれません(答えは「カミング」のようです).こんな場合に先頭の文字は読み方はわからないけれど,「ing」がついているお店というような方法で調べることができれば便利です.

    次に名簿から特定の人物を探す場合を考えてみましょう.英語の授業で作成した名簿で,名前はローマ字で書かれており,かつNaoya KANDAというように名前が先に書かれているとします.このような名簿から藤島さんの住所を探すことを考えてみましょう.藤島さんの名前はわからないので名字を手がかりに探さなければならないのですが,fujisima,fujishima,hujisima,hujishima,fuzishima,...のように沢山の表記方法がありすぐには見つけられそうにありません.「shima」もしくは「sima」という文字が使われている名字の人というような探し方ができれば便利です

    電話で名乗られたときも同じようなことが考えられます.「ビデオの予約をしていたと思うけれど,返却されていますか」というような問い合わせがあったとします.携帯電話から電話しているようであり,電波状況が悪く何度名乗ってもらっても「...トウです」としか聞こえません.どうも「イトウ」か「ゴトウ」のようなのですが,どちらだかわかりません.こんな時,「(イまたはゴ)トウ」という名字の人という探し方ができれば便利です.「サトウ」と「サイトウ」の聞き分けが難しい場合には「サ(イ)トウ」という名字の人という探し方が必要になるかもしれません

    何かを検索するときに,そのデータに含まれる文字列の一部やパターンを利用することがあります.このパターンを表現するために使われるのが正規表現です

  3. 正規表現の例
  4. 「FUJISHIMA」と「FUJISIMA」どちらかを探したい場合,正規表現では次のように検索パターンを書きます

    FUJISH?IMA

    ?は直前のHはあってもなくてもよいという指定です.これで「FUJISHIMA」と「FUJISIMA」のどちらでもマッチするようになります.?以外にも[]を使って表現することもあります

    スガ[ハワ]ラ

    こうすると,「スガハラ」でも「スガワラ」でもマッチします.[]の中に何文字が入れると,これらのうちのいずれかが含まれればよいという指定になります

    Rubyでは正規表現のパターンであることを指定するために//でくくって指定しますので,正確には下記のとおりになります

    /FUJISH?IMA/

    /スガ[ハワ]ラ/

  5. 正規表現の練習
  6. 指定したファイルから,該当する文字列を含む行を検索するというプログラムを使って練習してみよう

    まず下記の架空の名簿をmeibo.txtという名前をつけて保存しよう(emacsに貼り付けた上で保存する).

    丘本秋子  オカモト アキコ  角館  OKAMOTO Akiko
    高梁缶タカハシ カン浦上TAKAHASHI Kan
    仁科牧子ニシナ マキコ所沢NISHINA Makiko
    藤嶌楓フジシマ カエデ葛飾区HUJISHIMA Kaede
    伊井利郎イイ トシロウ横手II Toshiro
    木之元週一キノモト シュウイチ福岡KINOMOTO Shuichi
    多村数馬 タムラ カズマポートアイランドTAMURA Kazuma
    砂糖真琴サトウ マコトさいたま市SATO Makoto
    藤嶋丸子フジシマ マルコ黒井須FUJISIMA Maruko
    嶋田多香子シマダ タカコ網走SHIMADA Takako
    齋藤由サイトウ ユウ下関SAITO Yu
    高橋麹タカハシ コウジ平和島Takahasi Koji
    渡部すずワタベ スズロンドンWATABE Suzu
    渡辺勇次ワタナベ ユウジアレフガルドWATANABE Yuji
    藤島真希 フジシマ マキ歩歩露FUJISHIMA Maki

    次に,以下のプログラムをkensaku.rbという名前をつけて保存しよう

    #!/usr/koeki/bin/ruby
    
    while line=gets
      if /sai?to/i =~ line
        print line
      end
    end

    保存をしたら,実行してみます.実行する際には検索したいファイル名を指定する必要があります

    irsv{naoya}%chmod +x kensaku.rb
    irsv{naoya}%./kensaku.rb meibo.txt
    砂糖真琴 サトウ マコト さいたま市 SATO Makoto 
    齋藤由  サイトウ ユウ 下関    SAITO Yu 

    続いてプログラムの内容について解説します

    1. while line=gets
    2. getsはキーボードから入力された値を文字列として取得するメソッドと説明していました.しかし実際には入力を行うのはキーボードとは限りません.特に指定をしなければキーボードからの入力が求められますが,プログラム実行時にファイルを指定すると,そのファイルの中味を読み込みます

      今回の場合,プログラムを実行する際に./kensaku.rb meibo.txtとしていますので,getsメソッドはキーボードからの入力を待たずに,meibo.txtファイルを1行ずつ読み込みます

      meibo.txtは15行ありますので,while line=getsは15回繰り返されます.これ以上読むデータがなくなるとwhileの繰り返しは終了します

    3. if /sai?to/i = ~line
    4. このifで読み込んだ行が検索パターン(正規表現)にマッチしているか判定を行います.正規表現にマッチしているかどうかは

      正規表現 =~ 文字列

      と表現します./sai?to/は正規表現の指定で「saito」もしくは「sato」をあらわします./の後ろにあるiは「大文字と小文字を区別しない」という意味を付加するもので,必要に応じてつけたり,つけなかったりします.iをつけない場合は「sato」はマッチしますが「SATO」はマッチしなくなります

      なお,正規表現の後ろの=~は「正規表現がマッチする場合は」という比較演算子です.「正規表現がマッチしない場合には」を意味する演算子は!~になります

    5. print line
    6. lineという変数には,ファイルから読み込んだ行が入っています.上記の条件に合致する場合に,その行が出力されます

    7. end
    8. ifに対応するendです

    9. end
    10. whileに対応するendです

    練習

    kensaku.rbプログラムの/sai?to/の部分を様々に変更して結果がどうなるか試してみよう

    /sai?to/の中を日本語にするとうまく実行できないことが確認できるだろうか

    日本語にマッチさせるためには/の後ろにiの代わりにeまたはsをつける必要があります

    i:アルファベットの大文字と小文字を区別しない

    e:日本語をEUC文字列だと仮定して照合する

    s:日本語をShift-JIS文字列だと仮定して照合する


    一通り試してみたら,下記の問題について考えてみよう

    • カタカナで「ワタナベ」さんと「ワタベ」さんにマッチする正規表現はなにか
    • アルファベットで3人のフジシマさんにマッチする正規表現はなにか

  7. 正規表現の指定方法
    1. 通常の文字によるマッチング
    2. 正規表現によるパターンが英数字のみで書かれている場合,単純に文字列の中にその文字が含まれているかどうかでマッチする,しないを判断します.以下にサンプルを示します.ここで黄色で網掛けされた文字列は正規表現にマッチした文字列,網掛けされていない文字列はマッチしない文字列を表します(以下同じ)

      /ABC/#=> ABCABCDEF123ABC, A1B2C3, AB, abc

    3. 行頭と行末のマッチング
    4. /ABC/というパターンはABCを含む文字列であればどんな文字列であってもマッチします.しかし場合によってはABCから始まるもののみを検索したり,最後がABCとなるものを検索することもありえます.「ABCDEF」はマッチするけれど,「123ABC」はマッチしないようにする,もしくは「123ABC」はマッチするけれど,「ABCDEF」はマッチしないようにするためにはどうしたらよいでしょうか

      このためには「^」や「$」というような特殊な文字を使用します.これらの特殊な文字はメタ文字と呼び,「^」は行頭マッチング,「$」は行末マッチングを表します.他にもメタ文字は多数ありますが,これらについては後述します.

      /^ABC$/#=> ABC, ABCDEF, 123ABC

      /^ABC/#=> ABCABCDEF, 123ABC

      /ABC$/#=> ABC, ABCDEF, 123ABC

    5. マッチさせたい文字を範囲で指定する
    6. イトウもしくはゴトウのように,「イかゴ」のどちらかを含むというような条件を指定したい場合があります.このようにいくつかの文字のうち,その1つを指定したい場合には,マッチさせたい文字の集合を[]で囲みます

      [AB]:AまたはB

      [ABC]:AまたはBまたはC

      [CBA]:上と同じ(順番は関係なし)

      [012ABC]:0,1,2,A,B,Cいずれかの文字

      このような書き方だと,「AからZの大文字のアルファベット」というような数が多い条件になると記載が大変です.このような場合には[]の中を○-○というようにハイフンを使って範囲で示すことができます

      [A-Z]:アルファベットの大文字全部

      [a-z]:アルファベットの小文字全部

      [0-9]:数字全部

      [A-Za-z]:アルファベットの大文字,小文字全部

      [A-Za-z_]:アルファベットの大文字,小文字全部とアンダーバー(_)

      [A-Za-z_-]:アルファベットの大文字,小文字全部とアンダーバー(_)とハイフン(-)

      ハイフンは範囲を示すために使用します.ハイフン自体を検索対象の文字列としたい場合には,最初か最後に書かなければなりません

      なお行頭マッチングで使用した^は[]内で使用するとそこで指定されたもの以外の文字という意味になります

      [^ABC]:A,B,C以外の文字

      [^a-z]:アルファベットの小文字以外の文字

      /[ABC]/#=> BBCD, 123

      /a[ABC]c/#=> aBc1aBcDe, abc

      /[^ABC]/#=> 1, A

      /a[^A-C]c/#=> aBcabc

      /[ABC][AB]/#=> ABCAAACCCCAxCBx, CC, CxAx, C

      /[0-9][A-Z]/#=> 0A000AAA

      /[^A-Z][A-Z]/#=> 1A2B3C

      /[^0-9][^A-Z]/#=> 1A2B3C

    7. 任意の文字とのマッチング
    8. .(ピリオド):任意の1文字にマッチする.ピリオド1つなら1文字の文字列,3つなら3文字の文字列をあらわす

      /A.C/#=> ABC012A3C456AA, AC, ABBC, abc

      /aaa.../#=> 00aaabcde, aaabb

    9. バックスラッシュを使ったパターン
    10. \s:空白文字をあらわす.空白,タブ,改行文字とマッチする

      /ABC\sDEF/#=> ABC DEFABC\tDEF, ABCDEF

      \S:空白以外をあらわす

      /ABC\SDEF/#=> ABC3DEFABC-DEF, ABCDEF

      \d:0から9までの数字とマッチする

      /\d\d\d-\d\d\d\d/#=> 012-345601234-012345, ABC-DEFG, 012-21

      \w:英数字にマッチする

      /\w\w\w/#=> ABCabc012, AB C, AB\nC

      \A:文字列の先頭にマッチする

      /\AABC/#=> ABCABCDEF, 012ABC, 012\nABC

      \Z:文字列の末尾にマッチする

      /ABC\Z/#=> ABC012ABC, ABCDEF, 012\nABC, ABC\nDEF

    11. 繰り返し
    12. *:0回以上の繰り返しにマッチする

      /A*/#=> AAAAAA1BBB

      /A*C/#=> AAACBC, AAAB

      /AAA*C/#=> 012\nAAC, AC

      /A.*C/#=> AB012CAB CDACDE

      +:1回以上の繰り返しにマッチする

      /A+/#=> AAAAAA, 1, BBB

      /A+C/#=> AAAC, BC, AAAB

      /AAA+C/#=> AAC, AC

      /A.+C/#=> AB012CAB CD, ACDE

      ?:0回または1回の繰り返しにマッチする

      /^A?$/#=> A, AAAAA

      /^A?C/#=> AC, AAAC, BC, C

      /AAA?C/#=> AAACAAC, AC

      /A.?C/#=> ACDEABCDE, AB012C, AB CD

    13. ()と繰り返し
    14. ():*+?と組み合わせて使用することで,1文字単位での繰り返しではなく,複数の文字列の繰り返しにマッチする

      /^(ABC)$/#=> ABCABCABC, ABCABCAB

      /^(ABC)+$/#=> ABCABCABC, ABCABCAB

      /^(ABC)?$/#=> ABC, ABCABC, ABCABCAB

    15. 選択
    16. :いくつかの候補の中からどれか1つに当てはまるものにマッチする

      /^(ABC|DEF)$/#=> ABCDEF, AB, ABCDEF

      /^(AB|CD)+$/#=> ABCD, ABCABC, ABCABCAB

      課題

      様々な正規表現の表現方法について説明したが,これらを実際に使って試してみよう.具体的には下記の手順に従って実施すること

      1. 自分の興味があるもの(例:好きな野球選手とチーム,自動車の車種とメーカー,ジュースとメーカー,雑誌と出版社,小説と作家,競走馬と調教師,聖闘士と必殺技)について,1行に1データを書いて記述し,data.txtというファイル名で保存する.少なくとも10行以上作成すること
      2. 個人情報保護の保護の問題があるため,サークルの名簿等,個人情報が含まれているものをそのまま利用することは避けること
      3. kensaku.rbの正規表現の部分を様々に変更し,データファイルの中からマッチするデータを取り出し,レポートする
      4. なお,毎回正規表現を変更するためにプログラムを書き換えるのが面倒だと感じる人は,キーボードから正規表現を入力できるように変更しても良い.ただしこれを独学で行うのはかなり難しいので,興味のある人のみとする(まだ教えていないことを自分なりに勉強しないと作成できない)

      作成したdata.txtやプログラム,実行結果をメールでnaoya@e.koeki-u.ac.jp宛に送る.

      課題の提出期限は6月11日(土)17:00までです


      メール送信時の注意

      • Subjectは「学籍番号-kadai6」とすること
      • 本文の1行目ではまず名乗ること
      • 2行目以降は下記の構成で記載すること
        1. 作成したdata.txtとその説明(何のデータなのか)
        2. 作成したプログラム(作成した人のみ)
        3. プログラムの各部の説明(作成した人のみ)
        4. 使用した正規表現と実行結果
        5. 感想

        今回はプログラムの作成は求めておらず,様々な正規表現について実施した結果をまとめるというものである.それゆえ,評価を行う上では使用した正規表現の種類や,複雑さを考慮する(最低でも10種類以上は試してみること)

        課題を行う上では,/^x...?$/という正規表現を使用したら○○と□□がマッチしました,というように適当に行うのではなく,例えば,フジシマにマッチし,シマダにマッチしないようにするために/^x...?$/という正規表現を用いた./^x...?$/は〜〜という意味であり○○の理由でフジシマにはマッチするが,シマダにはマッチしないためである.実行結果は下記のとおりであり,正しくフジシマのみにマッチした,というように目的意識を持って行うこと

        そのためには,正規表現を使って様々なパターンマッチングを行うことができるようなdata.txtを作成する必要がある

        したがって,Webページに記載されているまま,例えば/ABC/,/^ABC/を試してみて,1つも該当するものがありませんでしたというのは評価しない.データを見ながらマッチするものがあるような正規表現をいかに数多く考えるか,いかに複雑な方法で指定してマッチするものを見つけることができるかを見る

        つまり,今回の課題はいかに巧くdata.txtを作成できるかが重要となる.今回は相談しなければできない課題ではないため,data.txtは各自で作成すること.重複した内容のレポートが提出された場合には,どちらがどちらを写したのか特定できないため,双方とも減点する

      Tips

      emacsについて

      • emacsで新しいファイルを作成する場合は,C-x C-fを押し,ミニバッファにkensaku/kadai2.rbのようにファイル名を入力する
      • ファイルの保存はC-x C-s
      • 日本語入力のオンオフの切り替えはC-o

      Mewについて

      • emacsを起動する
      • Escとxを押し,mewと入力しReturnEscとxを押すことを,一般にM-xと表記します)%<--これでMewが起動します
      • Mewを起動するとパスワードがたずねられるので入力しReturn
      • 新着メールの確認およびパスワードを間違えた場合はiと入力しReturn
      • メールを読む場合は,カーソルキー(矢印キー)で読みたいメールを選びReturn
      • メールの新規作成はwと入力
      • e-mailの本文にテキストファイルを読み込むには,新規送信メール画面の本文を記入するエリアにカーソルを移動し,Ctrl+x,iとすると,ミニバッファにInsert file: ~/と表示されるので,読み込みたいファイル名を入力する。
      • プログラムの実行結果の貼り付けは,kterm上の出力結果部分をマウスで選択し,Mewの本文の貼り付け位置にカーソルを移動し,マウスの真ん中ボタンをクリックする
      • メールの送信はC-c C-cと入力するか,もしくはメニューのsendアイコンをクリックする
      • Mewを終了するにはqと入力
      • emacsを終了する