rubyco(るびこ)の日記 RSSフィード

2007-06-13

Mailクラスでメールを解析する

Mailクラスを使うとメールを解析することができます。以下では、mbox形式のファイルストリームからメールを読み出し、差出人に example.com が含まれているものを集計しています。

require 'mailread'

MBOX = 'math_girls.mbox'
from = Hash.new(0)
File.open(MBOX) do |mbox|
  while not mbox.eof?
    message = Mail.new(mbox)
    from[message.header['From']] += 1
  end
end

from.keys.grep(/example\.com/).sort.each do |name|
  puts "#{name} => #{from[name]}"
end

実行例です。

<xxxxxxxx@example.com> => 202
<yyyyyyy@example.com> => 35
<zzzz@example.com> => 2

文字列の分割(scanとsplit)

String#scanを使うと、パターンを指定して必要な文字列を抜き出すことができます。

String#splitを使うと、区切り文字(デリミタ)を指定して必要な文字列を抜き出すことができます。

以下に例を示します。

p '123,45;6789/012'.scan(/\d+/)     #=> ["123", "45", "6789", "012"]
p '123,ab,WXYZ,(3)'.split(/,/)      #=> ["123", "ab", "WXYZ", "(3)"]

Benchmarkモジュールベンチマークテスト

複数の実装方法のどちらが速いかな?とRubyで調べたいときには、Benchmarkモジュールが便利です。

1〜nまでの和を計算する二つの方法を比較してみましょう。以下のプログラムでは、足し算を本当に繰り返して求める方法(method1)と、公式を使って計算で求める方法(method2)の二者の速度を比較しています。

require 'benchmark'

def method1(n)
  sum = 0
  1.upto(n) do |k|
    sum += k
  end
  sum
end

def method2(n)
  (1 + n) * n / 2
end

n = 100_000
Benchmark.bm(7) do |x|
  x.report('case A') { method1(n) }
  x.report('case B') { method2(n) }
end

実行例です。

             user     system      total        real
case A   0.156000   0.000000   0.156000 (  0.157000)
case B   0.000000   0.000000   0.000000 (  0.000000)
  • Benchmarkのクラスメソッドbmに与えている引数7は、繰り返し回数ではなく、見出し行の先頭オフセット量です。
  • bmの呼び出しに対応づけられたブロックの引数xには、Benchmark::Reportオブジェクトが渡されてきますが、あまり気にする必要はありません。

シングルクォートの文字列リテラルで置換されるもの

Rubyでは、シングルクォートでくくられた文字列リテラル '...' の中で使える \ のエスケープは、\\ と \' だけです。そのことを確かめようと思いました。

プログラムで \すべての文字 という形の文字列を作り、evalして長さがいくら減るかを調べます。

s = '\''
(0..255).each do |n|
  s += '\\'
  s += n.chr
end
s += '\''
p eval(s).length        #=> 510

512から2減って510になっています。 \\と\'の二つ分。

もっと短く。

p eval('\'' + (0..255).collect {|code| '\\' + code.chr }.join + '\'').length
#=> 510
Connection: close