制作会社で働く人向け「はじめての正規表現」(http://d.hatena.ne.jp/jdg/20080921/1222002244)という面白い記事があって、その中で「見積もり」とか「見積」を統一的に「見積り」に正規表現で変換しようとする話がありました。
こういうのはなかなかむずかしくて、まずサンプルテキストを作ります。
りんごの見積り みかんの見積もり 見積はいくらですか? 見積もった結果を教えて下さい 見積った結果を教えてちょうだい りんごの見積もみかんの見積も見積すぎです りんごの見積りもみかんの見積りも見積りすぎかな 見積もろうとか見積もらないとか見積もれないとか言うな 見積ろうとか見積らないとか見積れないとか言え
これを、下のように変換したい。
りんごの見積り => りんごの見積り みかんの見積もり => みかんの見積り 見積はいくらですか? => 見積りはいくらですか? 見積もった結果を教えて下さい => 見積った結果を教えて下さい 見積った結果を教えてちょうだい => 見積った結果を教えてちょうだい りんごの見積もみかんの見積も見積すぎです => りんごの見積りもみかんの見積りも見積りすぎです りんごの見積りもみかんの見積りも見積りすぎかな => りんごの見積りもみかんの見積りも見積りすぎかな 見積もろうとか見積もらないとか見積もれないとか言うな => 見積ろうとか見積らないとか見積れないとか言うな 見積ろうとか見積らないとか見積れないとか言え => 見積ろうとか見積らないとか見積れないとか言え
作ったRubyプログラムがつぎ。
while line = gets puts line print (" => ") puts line.gsub(/見積/, "見積もり").gsub(/見積もり(?=[っらるれろ])/, "見積も").gsub(/見積もりも(?=[っらるれろ])/,"見積も").gsub(/見積もり(もり|り)/, "見積もり").gsub(/見積も/, "見積") end
一発で行かなくて、一度"見積もり"という表記に変えてから、順次解析して、"見積り"に落しています。走らせるときは漢字コードを必ず指定してください。
ruby -Ks seiki.rb < seiki.txt
というようなかんじ。これはShift-JISの場合。UTF-8ならu、EUCならeですね。
?= 先読み(lookahead)。パターンによる位置指定(幅を持たない) ?! 否定先読み(negative lookahead)。パターンの否定による位置指定(幅を持たない)
とか知らなかったので、大変勉強になりました。
追記:「見積り」「見積」を「見積もり」にする正規表現は上記から最後のgsub(/見積も/, "見積")をとったものになります。
while line = gets puts line print (" => ") puts line.gsub(/見積/, "見積もり").gsub(/見積もり(?=[っらるれろ])/, "見積も").gsub(/見積もりも(?=[っらるれろ])/,"見積も").gsub(/見積もり(もり|り)/, "見積もり") end
感想:
1.できると思っていなかったのでとても満足。
2.よく、正規表現のテキストに「ぜんぶ正規表現でやろうとするな。手の方が速くて確実な場合もあるし、正規表現でやろうとして泥沼にはまる。」と書いてあって、「そんなものかな? とくに表記の揺れの統一はむずかしいよね。」くらいに考えていたけど、こんなものこそ目と手でやると大変。
3.サンプル大切。というかテスト大切。順々に考えたつもりでも、いじくっている内に最初の方からおかしくなっていく。テストの網羅性も大切。
4.頭のいいやりかたを目指すのではなく、遠回りでも確実な方法を考える。今回の場合、「見積もり」と「見積」を「見積り」にするために、まずすべて「見積もり」にしてから考えたのがうまくいった。
5.gsubをつなげて行くのはrubyでなくても思いつく方法ではあるが、rubyは考えやすい。幅を持たない先読み検索は正規表現の話で勉強になった。rubyでは使えないかもしれないと思いつつ試したら使えた。漢字コードの指定は大切。
以上