04

16

[Ruby] ネットワーク接続されたプリンタのステータスを取得するプログラム

2014.04.16(22:39)

ネットワーク接続されたプリンタのステータスを取得するRubyプログラム。

ライブラリを使わずSNMPパケットを自分で作って送信し、レスポンスを受信する。
IPアドレスを持ち、SNMPプロトコルをサポートし、MIBを持つネットワークプリンタから、
sysName,sysUpTime,hrDeviceStatus,hrPrinterStatusの4つを取得して表示する。


# encoding: Shift_JIS
require 'socket'

# printer_status.rb
# SNMPクライアントプログラム (Ruby2.0.0)
# ライブラリを使わずSNMPパケットを自分で作って送信し、レスポンスを受信する。
# sysName,sysUpTime,hrDeviceStatus,hrPrinterStatusの4つを取得する
# @see http://www.ietf.org/rfc/rfc1759.txt Printer MIB
# @note 使い方 ruby printer_status.rb プリンタのIPアドレス
# @author @saltheads
# @version 1.0.0

# Hex Dump クラス
class HexDump
  # 1行ダンプ
  # @param [String] data ダンプする対象データ
  # @param [Integer] offset 先頭アドレス
  # @param [Integer] step 1行あたりのデータ数
	def self.dumpline(data,offset,step)
	  if (data.length > 0) then
	    line = sprintf("%08x  ",offset)
	    chars = ""
	    data.each_byte{ |c| 
	      line << sprintf("%02x ",c)
	      chars << ((c >= 0x20 && c <= 0x7e) ? c : '.')
	    }
	    line << "   "*(step - data.size + 1)
	    line << chars
	    puts line
	  end
	end
  # データ全体をダンプ
  # @param [String] fulldata ダンプする対象データ
	def self.dump(fulldata)
	  offset = 0
	  step = 16
	  while data = fulldata[offset,step]
	    dumpline(data,offset,step)
	    offset += step
	  end
	end
end

# Snmp Client クラス
class SnmpClient
  # コンストラクタ
  # @param [String] host IPアドレス("1.2.3.4")
  # @param [Integer] port snmp ポート番号 通常161
  # @return [SnmpClient] オブジェクト
  def initialize(host,port=161)
    @host = host
    @port = port
    @debug = true & false
    printf "host #{host}\n" if @debug
  end
  # パケットダンプ
  # @param [String] title タイトル文字列
  # @param [String] data ダンプする対象データ
  def dump(title,data)
    if @debug
      printf "#{title}\n"
      HexDump.dump(data)
    end
  end
  # UDPでrequestを送信し、受信したデータを返す
  # @param [String] request 送信データ
  # @return [String] 受信したデータ
  def sendReceive(request)
    us = UDPSocket.open()
    us.connect(@host,@port)
    us.send(request, 0)
    a=select([us],nil,nil,3)
    if not a     # Timeout
      us.close
      return nil
    end
    response = us.recv(2000,0)
    us.close
    response
  end
  # スペース区切りの文字列から送信データを作る
  # @param [String] packet スペース区切り文字列
  # @return [String] 変換したバイナリデータ
  def packet2data(packet)
    array = packet.split(' ')
    request = ""
    array.each{ |c| request += c.to_i(16).chr }
    request
  end
  # sysName を取得する
  # @return [String] 受信したsysName
  def sysName
    packet  = "30 27 02 01 00 04 06 70 75 62 6c 69 63 a0 1a 02 "
    packet += "02 03 e8 02 01 00 02 01 00 30 0e 30 0c 06 08 2b "
    packet += "06 01 02 01 01 05 00 05 00 "    

    request = packet2data(packet)
    dump('send',request)    
    response = sendReceive(request)
    return if response == nil
    dump('recv',response)    

    len = response[40].bytes.to_a[0]
    len -= 1
    value = response[41..(41+len)]
    hexdumpdata(value) if @debug
    printf("sysName [#{value}]\n") if @debug
    return value
  end

  # sysUpTime を取得する
  # @return [Integer] 受信したsysUpTime
  def sysUpTime
    packet  = "30 27 02 01 00 04 06 70 75 62 6c 69 63 a0 1a 02 "
    packet += "02 03 e9 02 01 00 02 01 00 30 0e 30 0c 06 08 2b "
    packet += "06 01 02 01 01 03 00 05 00 "
    
    request = packet2data(packet)
    dump('send',request)    
    response = sendReceive(request)
    return if response == nil
    dump('recv',response)    

    time = response[41..43]
    hexdumpdata(time) if @debug
    t = 0
    time.each_byte { |c|
      # printf("%x\n",c)
      t = t*256 + c
    }
    printf("sysUpTime %06x = %d\n",t,t) if @debug
    return t
  end

  # hrDeviceStatusを取得する
  # @return [String] 受信したhrDeviceStatusの意味を表す文字列
  def hrDeviceStatus
    packet  = "30 2A 02 01 00 04 06 70 75 62 6c 69 63 a0 1d 02 "
    packet += "02 03 ea 02 01 00 02 01 00 30 11 30 0f 06 0b 2b "
    packet += "06 01 02 01 19 03 02 01 05 01 05 00 "
    
    request = packet2data(packet)
    dump('send',request)    
    response = sendReceive(request)
    return if response == nil
    dump('recv',response)    

    status = response[44]
    hexdumpdata(status) if @debug
    val = status.bytes.to_a[0]
    case val
    when 1 then s = "unknown(1)"
    when 2 then s = "running(2)"
    when 3 then s = "warning(3)"
    when 4 then s = "testing(4)"
    when 5 then s = "down(5)"
    else s = "unknown(#{val})"
    end
    return s
  end

  # hrPrinterStatusを取得する
  # @return [String] 受信したhrPrinterStatusの意味を表す文字列
  def hrPrinterStatus
    packet  = "30 2A 02 01 00 04 06 70 75 62 6c 69 63 a0 1d 02 "
    packet += "02 03 eb 02 01 00 02 01 00 30 11 30 0f 06 0b 2b "
    packet += "06 01 02 01 19 03 05 01 01 01 05 00 "
    
    request = packet2data(packet)
    dump('send',request)    
    response = sendReceive(request)
    return if response == nil
    dump('recv',response)    

    status = response[44]
    hexdumpdata(status) if @debug
    val = status.bytes.to_a[0]
    case val
    when 1 then s = "other(1)"
    when 3 then s = "idle(3)"
    when 4 then s = "printing(4)"
    when 5 then s = "warmup(5)"
    else s = "unknown(#{val})"
    end
    return s
  end
end

if __FILE__ == $0
  debug = false & true
  host = "10.0.1.99"
  if ARGV.size >= 1
    host = ARGV[0]
  end
  snmp = SnmpClient.new(host)
  name = snmp.sysName
  printf "sysName[#{name}]\n"
  60.times { |i|
    name = snmp.sysName
    time = snmp.sysUpTime
    deviceStatus = snmp.hrDeviceStatus
    printerStatus = snmp.hrPrinterStatus
    printf "sysUpTime[#{time}] deviceStatus[#{deviceStatus}] printerStatus[#{printerStatus}]\n"
    break if debug
    sleep 1
  }
  exit
end
 
 


実行例
プログラムを先に走らせた状態で、プリンタの電源をいれ、紙切れにしたあと、紙をセットし、印刷したようす。
C:>ruby printer_status.rb
sysName[]
sysUpTime[] deviceStatus[] printerStatus[]
sysUpTime[] deviceStatus[] printerStatus[]
sysUpTime[] deviceStatus[] printerStatus[]
sysUpTime[] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[660] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[1396] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[29265] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[29383] deviceStatus[running(2)] printerStatus[other(1)]
sysUpTime[29488] deviceStatus[running(2)] printerStatus[other(1)]
sysUpTime[38746] deviceStatus[down(5)] printerStatus[other(1)]
sysUpTime[38851] deviceStatus[down(5)] printerStatus[other(1)]
sysUpTime[38956] deviceStatus[running(2)] printerStatus[warmup(5)]
sysUpTime[39061] deviceStatus[running(2)] printerStatus[warmup(5)]
sysUpTime[39896] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[40001] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[267976] deviceStatus[running(2)] printerStatus[other(1)]
sysUpTime[268081] deviceStatus[running(2)] printerStatus[other(1)]
sysUpTime[268186] deviceStatus[running(2)] printerStatus[warmup(5)]
sysUpTime[268291] deviceStatus[running(2)] printerStatus[warmup(5)]
sysUpTime[270931] deviceStatus[running(2)] printerStatus[printing(4)]
sysUpTime[271036] deviceStatus[running(2)] printerStatus[printing(4)]
sysUpTime[271141] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[271246] deviceStatus[running(2)] printerStatus[idle(3)]
 
 
私の手持ちのプリンターでは、
印刷できないときに、deviceStatusがdown(5)となり、
印刷中か印刷すべきものが残っているときに、printerStatusがprinting(4)となるらしい。

参考リンク
  1. Printer MIB
    http://www.ietf.org/rfc/rfc1759.txt

02

13

[Ruby] ジェムストリング問題を解きました

2014.02.13(21:59)

結城浩 @hyuki さんのツイートで知った
ジェムストリング問題
http://www.hyuki.com/codeiq/#c14
を解いてCodeIQへ投稿しました。
考えた過程と、解答を生成するプログラムを残しておきます。

ジェムストリング問題は、宝石の並んでいるパターンから並ぶ規則を見つけ、特定のパターンが出現する位置を求める問題。
順列組合せの問題ですが総当たりすると計算量が多いため、計算量を抑えながら求める手段を見つけなければなりません。
今回もたくさんの気づきを与えてくれる、すばらしい出題でした。結城浩さん、ありがとう。

実行結果から
princess("abbbbcddddeefggg","eagcdfbe")
a 558834981
b 2198286197
c 558834981
d 2198286197
eab 21310301
eac 5452981
ead 21310301
eae 5452981
eaf 5452981
eagb 4914235
eagcb 416928
eagcdb 148912
eagcdd 113546
eagcde 38847
eagcdfbb 5023
eagcdfbd 5023
answer = 5578864439
総当たりはやらずにaで始まるものの個数を数え、bで始まるものの数を数え、全部を合計して求める作戦です。
プログラムの前半は、指定された文字列で始まる個数を数えることをやっていて
プログラムの後半は、何で始まる文字列を見つければ答えにたどりつくかを求めています。
プログラムは以下のとおり。ユニットテストもやっています。
# encoding: Shift_JIS
# 王女様の宝石パターンを見つけよう!
# @version 1.0 - 2014/02/08
# @author @saltheads
# @see https://codeiq.jp/ace/yuki_hiroshi/q684
# @note 

require 'test/unit'

#---------------------------------------------------------------
# 宝石の種類ごとの配列から全組み合わせの数を求める
# @param [Array] 各宝石の数の配列 [aの数,bの数,..]
#                処理の都合上、各要素の数は9個まで
# @param [Hash] すでに求めた結果
# @return [Int] 全部で何通りあるか。
def recursive_days(a1, h = Hash.new)
  a1.delete(0)                    # 0は削除
  return 0     if a1.size < 1     # 配列のサイズが0なら0
  return a1[0] if a1.size == 1    # 配列の要素が1つならその値そのもの
  key = a1.map{|n| n.to_s}.join   # 残った配列の要素を連結したkeyが
  val = h[key]                    # ハッシュに残っていれば、それを返す
  if val == nil
	  s = 0
	  plus = 0
	  a1.each_with_index{ |n,idx|
	    a2 = a1.dup
	    a2[idx] -= 1
	    s += recursive_days(a2, h)  # 再帰
	    plus += 1
	  }
	  val = (s + plus)
	  h[key] = val
  end
  val
end

# 宝石名から宝石の種類ごとの数をカウントした配列を作る
# 'abb'から[1,2]
# 'aaabbc'から[3,2,1]
def char_count(gems)
	count_array = Array.new
	gems_array = gems.split(//).sort
	gems_array.uniq.each { |c|
	  count_array << gems_array.select{|elem| elem == c}.size
	}
	count_array
end

class GemTest1 < Test::Unit::TestCase
  def test_char_count
    assert_equal(char_count(''),[])
    assert_equal(char_count('abb'),[1,2])
    assert_equal(char_count('aaabcc'),[3,1,2])
  end
end

# 宝石名から全組み合わせの数を求める
# @param [String] 全宝石名 'abc'
# @return [Int] 全部で何通りあるか。
def day_count(gems)
  count_array = char_count(gems)
  recursive_days(count_array)
end

class GemTest2 < Test::Unit::TestCase
  def test_day_count
    assert_equal(day_count(''),0)
    assert_equal(day_count('abb'),8)
    assert_equal(day_count('aaabcc'),188)
    assert_equal(day_count('abbbcddddeefggg'),2198286197)
  end
end

#---------------------------------------------------------------
# 宝石名の引き算をする
# @param [String] 'aaabcc' から
# @param [String] 'ac' を引くと
# @return [String] 'aabc' が返る
def gems_subtract(gems_from,gems_to)
	gems_from_array = gems_from.split(//).sort
	gems_to_array = gems_to.split(//).sort
  gems_to_array.each { |c|
  	index = gems_from_array.find_index(c)
  	gems_from_array.delete_at(index) if index != nil
  }
  gems_from_array.join
end

class GemTest3 < Test::Unit::TestCase
  def test_gems_subtract
    assert_equal(gems_subtract('abc', 'a'),"bc")
    assert_equal(gems_subtract('aaabcc', 'ac'),"aabc")
  end
end

# 宝石名全体からn個選んだ部分宝石名を作ったもののうち、比較対象より小さいものを求める
# @param [String] 全宝石名 'aaabcc'
# @param [String] 比較対象 'acbc'
# @return [Array] 部分宝石名を入れた配列   ["aa", "ab", "aca", "acba"]
def milestones(gems,target)
  array = Array.new
  key = ''
  target.split(//).each { |c|
	  smaller = gems.split(//).sort.uniq.select{|elem| elem < c}
	  array << smaller.map { |elem| key + elem}
	  key += c
	  gems = gems_subtract(gems, c)
	  printf "#{gems} #{key} #{c} #{smaller} #{array}\n" if false
  }
  array.flatten
end

class GemTest4 < Test::Unit::TestCase
  def test_milestones
    assert_equal(milestones('aaabcc','acbc'),["aa", "ab", "aca", "acba"])
    assert_equal(milestones('aaabcc','bcac'),["a", "ba", "bcaa"])
    assert_equal(milestones('aaabcc','c'),["a", "b"])
    assert_equal(milestones('aaabcc','cc'),["a", "b", "ca", "cb"])
    assert_equal(milestones('aaabcc','ccaa'),["a", "b", "ca", "cb"])
  end
end

# 王女様のパターンが何日目に見つかるか求める
# @param [String] 全宝石名 'aaabcc'
# @param [String] 見つけたいパターン 'acbc'
# @return [Int] 79
def princess(gems, target)
  print "--------------------------\n"
  print "princess(\"#{gems}\",\"#{target}\")\n"
	sum = target.size
	milestones(gems, target).each { |elem|
	  subs = gems_subtract(gems, elem)
	  count = day_count(subs)
	  print "#{elem} #{count}\n" if true
	  sum += (1 + count)
	}
  print "answer = #{sum}\n" if true
  sum
end

class GemTest6 < Test::Unit::TestCase
  def test_queen
    assert_equal(1,1)
    assert_equal(princess('aaabcc', 'a'), 1)
    assert_equal(princess('aaabcc', 'acab'), 63)
    assert_equal(princess('aaabcc', 'acbc'), 79)
    assert_equal(princess('aaabcc', 'b'), 91)
    assert_equal(princess('aaabcc', 'baa'), 93)
    assert_equal(princess('aaabcc', 'cc'), 175)
    assert_equal(princess('aaabcc', 'ccaba'), 183)
    assert_equal(princess('aaabcc', 'ccbaaa'), 188)
    assert_equal(princess('abbbbcddddeefggg', 'eagcdfbe'), 5578864439)
  end
end
おそらく、もっときれいなアルゴリズムがあるのだと思う。@hyuki さんの解説を待ちたい。

12

30

[Ruby] リクルートプログラミンコンテスト ババ抜き 問題

2013.12.30(21:42)

こんな問題わかんないよ…。「賞金が素数」という斬新なプログラミングコンテストをリクルートが開催 : ギズモード・ジャパン
http://www.gizmodo.jp/2013/12/recruit_programming_contest.html
2013年12月8日(日)にリクルートホールディングスが開催した「Recruit Programming Contest」
の模擬練習会で出された問題
A - ババ抜き

を解いてみました。でも、与えられたテストケースだけ正しく解ければいいのかなあ? たぶん違いますよね?
コンテストでは、みんなが解いたプログラムが正しいかどうか、どうやって判定していたのだろう?

# encoding: Shift_JIS

# こんな問題わかんないよ…。「賞金が素数」という斬新なプログラミングコンテストをリクルートが開催 : 
# ギズモード・ジャパン
# http://www.gizmodo.jp/2013/12/recruit_programming_contest.html
# 2013年12月8日(日)にリクルートホールディングスが開催した
#「Recruit Programming Contest」の模擬練習会で出された問題
# A - ババ抜き

# ヒアドキュメント
 question=<<QUESTION
3
3
16372
2746J18
348
4
1234
1234
1234
1234J
5
13645
643125
147
5137J
3245
QUESTION
 answer=<<ANSWER
15
29
-1
ANSWER

def game(player_array)
 debug = false
 history = Array.new
 turn = 1
 me = 0

 100.times {
  # ババ抜きが終わらない判定用
  a = Array.new
  player_array.each { |p|
   a << p.dup
  }
  history << a

  # 1ターン処理
  printf "#{turn} #{player_array}\n"
  me = 0 if me >= player_array.size
  you = me + 1
  you = 0 if you >= player_array.size
  take = player_array[you].shift
  if player_array[me].include?(take)
   player_array[me].delete(take)
  else
   player_array[me] << take
  end
  player_array.delete([])
  me = me + 1
  return turn if player_array.size < 2
  turn = turn + 1
  
  # ババ抜きが終わらない判定
  # printf "# history #{history}\n"
  history.each_with_index { |one,idx|
   if one == player_array
    printf "# found same pattern #{one}\n"
    return (-1)
   end
  } if true
 }
 exit
end

result_array = Array.new
qa = question.lines.map!{|line|line.chop}
count = qa.shift.to_i
count.times {
 player_array = Array.new
 num = qa.shift.to_i
 num.times {
  cards_array = qa.shift.split(//)
  player_array << cards_array
 }
 result = game(player_array)
 printf "result = #{result} ------------------------------ \n"
 result_array << result
}

aa = answer.lines.map!{|line|line.chop.to_i}
p aa
p result_array
p result_array == aa
 
 
久しぶりにRubyでプログラム書いたら、ババ抜きが終わらない判定に使った、配列の深いコピー問題を忘れていて時間がかかりました。

09

24

[Ruby] ナムドット問題を解きました

2013.09.24(21:00)

結城浩 @hyuki さんのツイートで知った
ナムドット問題
http://www.hyuki.com/codeiq/#c13
を解いてCodeIQへ投稿しました。
考えた過程と、解答を生成するプログラムを残しておきます。

ナムドット問題は、与えられた(52-20)行の文字列からその生成規則を見つける問題。
不規則に見えるものから規則を見つけるのもプログラマのお仕事。

出題の先頭部分は以下のとおり。
12345
1234.5
1235.4
123.45
123.4.5
1245.3
124.35
124.3.5
...
最初に考えたこと
・52行のパターンのうち重なっているものはないので何かの繰り返しではなさそう。
・曜日とか月とかも関係なさそう。何かの規則で52行が生成されているらしい。
・簡単でもない代わりに、ランダムとか意地悪もないはず。

次に考えたこと
・単純な規則ではなく、複数の規則を組み合わせた結果なんだろう。
・なので、見つかった規則から順番に取り除いてゆけばやがて単一の規則に
たどりつくのではないだろうか。
・全部の行の先頭に1があるのでこれは取り除いてOK
・5は左から右へ移動してゆくような気がする。
・5が左から右へ移動するときにドット(.)が関係しているような。
・ドット(.)が移動するアルゴリズムと、ドット(.)の周辺を入れ替える
ようなアルゴリズムが組み合わさっているのでないか。★

ここで2日経過。

・★ではどうにも解答に行き着かず、その延長線上で考えるのをやめる。
・改めて、
・1行目と2行目を見比べると、"5"が".5"に変わっている。
・それと同じことが、4~5行目、7~8行目、10~11行目、以後ずっと
続いているようだ。そこで、2行目まで、5行目まで、8行目まで、11行目まで
で区切ってみる。
・それぞれのブロックで"5"や".5"を取り除くと、
3~5行目までは全部"123.4",
6~8行目までは全部"124.3",
9~11行目までは全部"12.34"
である。これを各ブロックの基本文字列と呼ぶことにする。

あらためて、3~5行目を見ると、"123.4"に対して、
ドットの左に"5"を、一番右に"5"を、一番右に".5"を追加した形になっている。
7~8行目、10~11行目、、、、48~52行目までそうなっている。
1~2行目はドットがない "1234"に対して、一番右に"5"を、
一番右に".5"を追加した形になっている。規則的だ。

これでブロックの基本文字列が与えられれば
1ブロック分は生成できるようになった。

あとはブロックの基本文字列をどうやって作るかだ。

もとの出題の
"12345","1234.5","1235.4","123.45","123.4.5",...
と、ブロックの基本文字列
"1234","123.4","124.3","12.34","12.3.4",...
を見比べると、そっくり。
出題の文字列の先頭の"1"を削除して、残った数字を全部1ずつ引くと、
ブロックの基本文字列ができる。

つまり、出題の文字列から、ブロックの基本文字列を作り出すことができる。解けた!

以下のRubyプログラムはそのとおりを表現したものになっています。

# encoding: Shift_JIS
# print("numdot mondai\n")

def generate(array,word)
  first = word.split(//)
  first.shift     # 先頭を取り除く
  second = Array.new
  first.each { |ch|
    if ch == '.' 
      second << ch
    else
      second << ((ch.to_i) - 1).to_s  # 各要素を1減らす
    end
  }
  len = second.length
  second.each_with_index { |ch,idx|
    if ch == '.'  # ドットごとにドットの手前に5をいれた数を作る
       array << (second[0..idx-1].join + "5" + second[idx..len].join)
    end
  }
  array << (second.join + "5")  # "5"を後置
  array << (second.join + ".5") # ".5"を後置
end

array = Array.new
word = "12345"
array = generate(array,word)    # 最初の種で配列を作る
1.upto(15) { |idx|
  array = generate(array,array[idx]) # 2回目以降は配列のn番目を使って生成する
}
array.each_with_index { |word,idx|
  printf "#{word}\n"
  break if idx == 51
}
exit
このプログラムで生成した答え
12345
1234.5
1235.4
123.45
123.4.5
1245.3
124.35
124.3.5
125.34
12.345
12.34.5
125.3.4
12.35.4
12.3.45
12.3.4.5
1345.2
134.25
134.2.5
135.24
13.245
13.24.5
135.2.4
13.25.4
13.2.45
13.2.4.5
145.23
14.235
14.23.5
15.234
1.2345
1.234.5
15.23.4
1.235.4
1.23.45
1.23.4.5
145.2.3
14.25.3
14.2.35
14.2.3.5
15.24.3
1.245.3
1.24.35
1.24.3.5
15.2.34
1.25.34
1.2.345
1.2.34.5
15.2.3.4
1.25.3.4
1.2.35.4
1.2.3.45
1.2.3.4.5
おそらく、もっときれいなアルゴリズムがあるのだと思う。@hyuki さんの解説を待ちたい。

08

25

Ruby練習問題 その1 解答例

2013.08.25(20:29)

Ruby練習問題 その1 解答例 Ruby練習問題 その1 解答例
「初心者向け解答」例をこしらえました。短くトリッキーな解答ではなく、長くても理解しやすく応用がききやすいものにしました。
# encoding: Shift_JIS
=begin
 Ruby練習問題 その1 (正規表現,配列,ファイル)
=end

# ヒント
printf "整数の配列\n"
array = [100,20,3]
p array
p array.sort

p "50"
p "50".to_i * 2

printf "文字列の配列\n"
array = Array.new
array << "100"
array << "20"
array << "3"
p array
p array.sort
p array.sort{|a,b| a.to_i <=> b.to_i}

# ファイルを書く
file_name = 'data.txt'
File.open(file_name,"w") { |f|
  f.puts "square,40,5"      # 幅,高さ
  f.puts "square,30,20"
  f.puts "circle,100"       # 半径
  f.puts "triangle,20,10"   # 底辺,高さ
  f.puts "circle,20"
  f.puts "circle,3"
  f.puts "circle,3"         # 半径3は2つある
  f.puts "square,8,15"
}

printf "ファイルを読んでそのまま出力する\n"
File.open(file_name,"r") { |f|
  while line = f.gets
    puts line
  end
}

printf "ファイルを読んで、circleを含む行だけを出力する\n"
File.open(file_name,"r") { |f|
  while line = f.gets
    if /circle/ =~ line
      puts line
    end
  end
}

printf "ファイルを読んで、circleを含む行を、circle,半径,面積 と出力する\n"
# rubyでの円周率はググる。
File.open(file_name,"r") { |f|
  while line = f.gets
    if /circle,(\d+)/ =~ line
      hankei = $1.to_i
      menseki = hankei * hankei * Math::PI
      printf "circle,#{hankei},#{menseki}\n"
    end
  end
}

printf "ファイルを読んで、circleを含む行を、半径の小さい順に、circle,半径 と出力する\n"
zukei = 'circle'
array = Array.new
File.open(file_name,"r") { |f|
  while line = f.gets
    if /#{zukei},(\d+)/ =~ line
      hankei = $1.to_i
      array << hankei
    end
  end
}
array.sort.each { |hankei|
    printf "#{zukei},#{hankei}\n"
}

printf "ファイルを読んで、circleを含む行を、半径の小さい順に、circle,半径 と出力する[別解]\n"
zukei = 'circle'
array = Array.new
File.open(file_name,"r") { |f|
  while line = f.gets
    if /#{zukei},(\d+)/ =~ line
      array << line.chomp
    end
  end
}
array.sort{ |line1,line2|
  hankei1 = line1.scan(/#{zukei},(\d+)/)[0][0].to_i
  hankei2 = line2.scan(/#{zukei},(\d+)/)[0][0].to_i
  hankei1 <=> hankei2
}.each { |line|
  puts "#{line}"
}

printf "ファイルを読んで、squareを含む行を、square,高さ,幅 と出力する (幅と高さを逆に出力)\n"
File.open(file_name,"r") { |f|
  while line = f.gets
    if /(\w+),(\d+),(\d+)/ =~ line
      zukei, haba, takasa = $1,$2,$3
      if zukei == 'square'
        printf "#{zukei},#{takasa},#{haba}\n"
      end
    end
  end
}

printf "ファイルを読んで、全部の行について、行全体,面積 と出力する\n"
File.open(file_name,"r") { |f|
  while line = f.gets
    line.chomp!
    array = line.split(/,/)
    zukei = array.shift
    array.map!{ |s| s.to_i }
    
    case zukei
    when 'square'
      haba,takasa = array[0..1]
      menseki = haba * takasa
    when 'circle'
      hankei = array[0]
      menseki = hankei * hankei * Math::PI
    when 'triangle'
      teihen,takasa = array[0..1]
      menseki = teihen * takasa / 2
    else
      raise "undefined #{zukei}\n"
    end
    printf "%-14s,%3.2f\n",line,menseki
  end
}
 
実行結果 (Ruby2.0.0)
整数の配列
[100, 20, 3]
[3, 20, 100]
"50"
100
文字列の配列
["100", "20", "3"]
["100", "20", "3"]
["3", "20", "100"]
ファイルを読んでそのまま出力する
square,40,5
square,30,20
circle,100
triangle,20,10
circle,20
circle,3
circle,3
square,8,15
ファイルを読んで、circleを含む行だけを出力する
circle,100
circle,20
circle,3
circle,3
ファイルを読んで、circleを含む行を、circle,半径,面積 と出力する
circle,100,31415.926535897932
circle,20,1256.6370614359173
circle,3,28.274333882308138
circle,3,28.274333882308138
ファイルを読んで、circleを含む行を、半径の小さい順に、circle,半径 と出力する
circle,3
circle,3
circle,20
circle,100
ファイルを読んで、circleを含む行を、半径の小さい順に、circle,半径 と出力する[別解]
circle,3
circle,3
circle,20
circle,100
ファイルを読んで、squareを含む行を、square,高さ,幅 と出力する (幅と高さを逆に出力)
square,5,40
square,20,30
square,15,8
ファイルを読んで、全部の行について、行全体,面積 と出力する
square,40,5   ,200.00
square,30,20  ,600.00
circle,100    ,31415.93
triangle,20,10,100.00
circle,20     ,1256.64
circle,3      ,28.27
circle,3      ,28.27
square,8,15   ,120.00
 
プロフィール

島敏博

Shima Toshihiro 島敏博
信州アルプスハイランド在住。HaskellとElixirが好き。組み込みソフトウェアアーキテクト、C++プログラマ、山歩き、美術館巡り、和食食べ歩き、日本赤十字社救急法指導員、インデックス投資、クラシック音楽、SESSAME会員、状態マシン設計、モデル駆動開発、ソフトウェアプロダクトライン、Rubyist、実践ビジネス英語

■ ツイッター
http://twitter.com/saltheads
■ Facebook
http://www.facebook.com/saltheads
■ Qiita
http://qiita.com/saltheads

印刷する場合は、ブラウザの印刷メニューではなく、このページの上から3cmくらいの青いところにある、「印刷」を押してみてください。少しうまく印刷できます。まだ完全ではないのですが、これで勘弁してください。


カテゴリ
最新記事
月別アーカイブ
最新コメント
検索フォーム
リンク
sessame
RSSリンクの表示