ようへいの日々精進XP

よかろうもん

俺は Ruby について 1bit も理解していなかったので写経する 〜 添付ライブラリ 〜

俺は Ruby について 1bit も理解していなかったので写経する 〜 添付ライブラリ 〜

これは

  • Ruby Gold を受験するにあたって学んでいること等を写経したり, メモったりしています
  • Ruby 技術者認定試験合格教本」を主に参考にしており, 今回は第六章「添付ライブラリ」を写経していきます
  • 実行例では主に pry を利用していますが, 例外が発生した際の実行過程出力(from から始まる行)は省略しています...すいません
[12] pry(main)> Baz2.new.private_method1
NoMethodError: private method `private_method1' called for #<Baz2:0x005606f46e2778>
from (pry):29:in `__pry__'

StringIO

  • 文字列を IO クラスと同じインタフェースで取り扱うクラス
  • IO クラスと同じインタフェースを持つが, 直接の継承関係は無い
  • インスタンス生成時に与えられた文字列や puts 等のメソッドで文字列を StringIO 内のバッファに格納する
  • gets や readline 等のメソッドでバッファから文字列を取り出すことが出来る
require 'stringio'

sio = StringIO.new

open メソッドによる StringIO インスタンスの作成例.

[6] pry(main)> sio = StringIO.open "Hello World" do |io|
[6] pry(main)*   p io.read
[6] pry(main)*   nil
[6] pry(main)* end  
"Hello World"
=> nil
[7] pry(main)> p sio
nil
=> nil

ファイルフォーマット

yaml

yaml とは

  • YAML は, XML よりもさらに簡単な記法でデータ階層構造を表現出来るフォーマット
  • ハッシュ・配列の組み合わせをスペース(タブは使用出来ない)によるインデントで表現したもの
  • YAML の入出力に関するメソッドは全てクラスメソッドなので, インスタンスを生成しないで呼び出すことが出来る

yaml の読み込み

[7] pry(main)> YAML.load("---\n- foo\n- bar\n- baz\n")
=> ["foo", "bar", "baz"]

YAML.load メソッドは, YAML データが記録された文字列, IO インスタンスから Hash クラスのインスタンスを生成する. 複数の YAML が記録されていても, 最初の YAML のみが生成され, 残りは無視される.

$ cat sample.yml 
- foo
- bar
- baz
$ ruby -r yaml -e 'p YAML.load_file("sample.yml")'
["foo", "bar", "baz"]

YAML.load_file メソッドは, 指定した YAML ファイルからオブジェクトを生成する.

yaml の書き込み

YAML.dump メソッドは, 単一のインスタンスを文字列又は IO インスタンスに出力することが出来る.

# 引数 io を省略すると YAML 形式に変換された文字列を返す
[3] pry(main)> YAML.dump(['foo', 'bar', 'baz'])
=> "---\n- foo\n- bar\n- baz\n"
# io を指定すると指定した出力先に YAML 形式のデータを書き込む
[4] pry(main)> YAML.dump(['foo', 'bar', 'baz'], File.open("test.yml", "w+"))
=> #<File:test.yml>
[5] pry(main)> exit
$ cat test.yml 
---
- foo
- bar
- baz
# YAML.dump_stream メソッドは, 複数のオブジェクトを文字列に出力することが出来る
[1] pry(main)> require 'yaml'
=> true
[2] pry(main)> YAML.dump_stream(['foo', 'bar'], ['hoge', 'fuga'])
=> "---\n- foo\n- bar\n---\n- hoge\n- fuga\n"

JSON

JSON とは

  • Ruby 1.9 から標準添付ライブラリに JSON を扱うライブラリが追加された
  • YAML 同様に簡単な記法でデータの階層構造を表現出来るフォーマット
  • ブラウザとサーバー間でデータを交換する形式としてよく利用されている

JSON の読み込み

文字列から JSON を読み込む場合, JSON.parse 又は JSON.load メソッドを利用する.

[4] pry(main)> JSON.load('["foo", "bar", "baz"]')
=> ["foo", "bar", "baz"]
# Proc には解釈されたオブジェクトから順番に渡されるので, 以下のように出力される
[6] pry(main)> JSON.load('["foo", "bar", "baz"]', lambda{|x| p x})
"foo"
"bar"
"baz"
["foo", "bar", "baz"]
=> ["foo", "bar", "baz"]
# JSON.parse は load と異なり, 引数には文字列しか渡すことが出来ない
[7] pry(main)> JSON.parse('["foo", "bar", "baz"]')
=> ["foo", "bar", "baz"]

YAML と異なり, ファイルから直接読み込むメソッドは用意されていないが, load メソッドの引数に io オブジェクトを指定することで, io オブジェクト経由でデータを読み込むことが出来る.

$ cat sample.json 
["foo", "bar", "baz"]
$ ruby -r 'json' -e 'p JSON.load(File.open("sample.json"))'
["foo", "bar", "baz"]

JSON の書き込み

JSON の書き込みは JSON.dump を利用する.

[1] pry(main)> require 'json'
=> true
[2] pry(main)> JSON.dump(['foo', 'bar', 'baz'])
=> "[\"foo\",\"bar\",\"baz\"]"

ファイルに書き出す場合には, 第二引数に IO オブジェクトを指定する. 第三引数にはオブジェクトを参照する深さを指定する. limit 以上深くリンクしたオブジェクトをダンプしようとすると AregumentError が発生する.

[1] pry(main)> require 'json'
=> true
[2] pry(main)> f = File.open('dump.json', "w")
=> #<File:dump.json>
[3] pry(main)> JSON.dump(['foo', 'bar', 'baz'], f)
=> #<File:dump.json>

CSV

CSV とは

  • 無く子も黙る...

読み書き可能なメソッド

CSV を mode で指定した形式でオープンし, ブロックで処理する.

[1] pry(main)> require 'csv'
=> true
[2] pry(main)> CSV.open("sample.csv") do |csv|
[2] pry(main)*   csv.each do |row|
[2] pry(main)*     p row
[2] pry(main)*   end  
[2] pry(main)* end  
["abc", "def", "ghi", "jkl"]
["123", "456", "789", "012"]
["ABCDEFG", nil, "HIJKLM", nil, nil]
=> nil

以下, 書き込み.

[3] pry(main)> CSV.open("sample2.csv", "w") do |row|
[3] pry(main)*   row << ["abc", "def", "ghi"]
[3] pry(main)*   row << ["jkl", "mno", "pqr"]
[3] pry(main)* end  
=> <#CSV io_type:File io_path:"sample2.csv" encoding:UTF-8 lineno:2 col_sep:"," row_sep:"\n" quote_char:"\"">
[4] pry(main)> exit
$ cat sample2.csv 
abc,def,ghi
jkl,mno,pqr

読み込みのみ

以下のメソッドを利用する.

  • foreach
  • read
  • realines
[1] pry(main)> require 'csv'
=> true
[2] pry(main)> CSV.read("sample.csv")
=> [["abc", "def", "ghi", "jkl"], ["123", "456", "789", "012"], ["ABCDEFG", nil, "HIJKLM", nil, nil]]
[3] pry(main)> CSV.readlines("sample.csv")
=> [["abc", "def", "ghi", "jkl"], ["123", "456", "789", "012"], ["ABCDEFG", nil, "HIJKLM", nil, nil]]
[5] pry(main)> table = CSV.read("sample.csv", headers: true)
=> #<CSV::Table mode:col_or_row row_count:3>
[6] pry(main)> table.to_a
=> [["abc", "def", "ghi", "jkl"], ["123", "456", "789", "012"], ["ABCDEFG", nil, "HIJKLM", nil, nil]]

以上

写経でした.