第1章 はじめてのRuby

List 1.1 : helloruby.rb

print("Hello, Ruby.\n")

List 1.2 : put_and_p.rb

puts "Hello,\n\tRuby."
p "Hello,\n\tRuby."

List 1.3 : kiritsubo.rb

print "いづれの御時にか女御更衣あまたさぶらいたまいけるなかに\n"
print "いとやむごとなき際にはあらぬがすぐれて時めきたまふありけり\n"

List 1.4 : area_volume.rb

x = 10
y = 20
z = 30
area = (x*y + y*z + z*x) * 2
volume = x * y * z
print "表面積=", area, "\n"
print "体積=", volume, "\n"

List 1.5 : comment_sample.rb

=begin
「たのしいRuby 第6版」サンプル
コメントの使い方の例
2006/06/16 作成
2006/07/01 一部コメントを追加
2019/01/01 第6版用に更新
=end
x = 10  # 横
y = 20  # 縦
z = 30  # 高さ
# 表面積と体積を計算する
area = (x*y + y*z + z*x) * 2
volume = x * y * z
# 出力する
print "表面積=", area, "\n"
print "体積=", volume, "\n"

List 1.6 : bigger_smaller.rb

a = 20
if a >= 10 then
  print "greater\n"
end
if a <= 9 then
  print "smaller\n"
end

List 1.7 : bigger_smaller_else.rb

a = 20
if a >= 10
  print "greater\n"
else
  print "smaller\n"
end

第2章 便利なオブジェクト

ファイル名のあるソースコードはありません。

第3章 コマンドを作ろう

List 3.1 : puts_argv.rb

puts "最初の引数: #{ARGV[0]}"
puts "2番目の引数: #{ARGV[1]}"
puts "3番目の引数: #{ARGV[2]}"
puts "4番目の引数: #{ARGV[3]}"
puts "5番目の引数: #{ARGV[4]}"

List 3.2 : happy_birth.rb

name = ARGV[0]
puts "Happy Birthday, #{name}!"

List 3.3 : arg_arith.rb

num0 = ARGV[0].to_i
num1 = ARGV[1].to_i
puts "#{num0} + #{num1} = #{num0 + num1}"
puts "#{num0} - #{num1} = #{num0 - num1}"
puts "#{num0} * #{num1} = #{num0 * num1}"
puts "#{num0} / #{num1} = #{num0 / num1}"

List 3.4 : read_text.rb

filename = ARGV[0]
file = File.open(filename) # ①
text = file.read           # ②
print text                 # ③
file.close                 # ④

List 3.5 : read_text_simple.rb

filename = ARGV[0]
text = File.read(filename)
print text

List 3.6 : read_text_oneline.rb

print File.read(ARGV[0])

List 3.7 : read_line.rb

filename = ARGV[0]
file = File.open(filename)
file.each_line do |line|
  print line
end
file.close

List 3.8 : simple_grep.rb

pattern = Regexp.new(ARGV[0])
filename = ARGV[1]

file = File.open(filename)
file.each_line do |line|
  if pattern =~ line
    print line
  end
end
file.close

List 3.9 : hello_ruby2.rb

def hello
  puts "Hello, Ruby."
end

hello()

List 3.10 : grep.rb

def simple_grep(pattern, filename)
  file = File.open(filename)
  file.each_line do |line|
    if pattern =~ line
      print line
    end
  end
  file.close
end

List 3.11 : use_grep.rb

require_relative "grep"         # grep.rbの読み込み(「.rb」は不要)

pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
simple_grep(pattern, filename)  # simple_grepメソッドの起動

第4章 オブジェクトと変数・定数

List 4.1 : scopetest.rb

$x = 0
x = 0

require_relative "sub"

p $x   #=> 1
p x    #=> 0

List 4.2 : sub.rb

$x = 1  # グローバル変数に代入
x = 1   # ローカル変数に代入

第5章 条件判断

List 5.1 : ad2age.rb

# 西暦からRubyの年齢を返す
ad = ARGV[0].to_i
age = ad - 1993
puts age

List 5.2 : if_elsif.rb

a = 10
b = 20
if a > b
  puts "aはbよりも大きい"
elsif a < b
  puts "aはbよりも小さい"
else
  puts "aはbと同じ"
end

List 5.3 : unless.rb

a = 10
b = 20
unless a > b
  puts "aはbより大きくない"
end

List 5.4 : case.rb

tags = [ "A", "IMG", "PRE" ]
tags.each do |tagname|
  case tagname
  when "P", "A", "I", "B", "BLOCKQUOTE"
    puts "#{tagname} has a child."
  when "IMG", "BR"
    puts "#{tagname} has no child."
  else
    puts "#{tagname} cannot be used."
  end
end

List 5.5 : case_class.rb

array = [ "a", 1, nil ]
array.each do |item|
  case item
  when String
    puts "item is a String."
  when Numeric
    puts "item is a Numeric."
  else
    puts "item is something."
  end
end

第6章 繰り返し

List 6.1 : times.rb

7.times do
  puts "いちめんのなのはな"
end

List 6.2 : times2.rb

5.times do |i|
  puts "#{i}回目の繰り返しです。"
end

List 6.3 : times3.rb

5.times do |i|
  puts "#{i+1}回目の繰り返しです。"
end

List 6.4 : for.rb

sum = 0
for i in 1..5
  sum = sum + i
end
puts sum

List 6.5 : for_names.rb

names = ["awk", "Perl", "Python", "Ruby"]
for name in names
  puts name
end

List 6.6 : while.rb

i = 1
while i < 3
  puts i
  i += 1
end

List 6.7 : while2.rb

sum = 0
i = 1
while i <= 5
  sum += i
  i += 1
end
puts sum

List 6.8 : while3.rb

sum = 0
i = 1
while sum < 50
  sum += i
  i += 1
end
puts sum

List 6.9 : until.rb

sum = 0
i = 1
until sum >= 50
  sum += i
  i+= 1
end
puts sum

List 6.10 : while_not.rb

sum = 0
i = 1
while !(sum >= 50)
  sum += i
  i += 1
end
puts sum

List 6.11 : each_names.rb

names = ["awk", "Perl", "Python", "Ruby"]
names.each do |name|
  puts name
end

List 6.12 : each.rb

sum = 0
(1..5).each do |i|
  sum = sum + i
end
puts sum

List 6.13 : break_next.rb

puts "breakの例:"
i = 0
["Perl", "Python", "Ruby", "Scheme"].each do |lang|
  i += 1
  if i == 3
    break
  end
  p [i, lang]
end

puts "nextの例:"
i = 0
["Perl", "Python", "Ruby", "Scheme"].each do |lang|
  i += 1
  if i == 3
    next
  end
  p [i, lang]
end

List 6.14 : ten_lines_grep.rb

pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
max_matches = 10      # 出力する最大数
matches = 0           # マッチした行数
file = File.open(filename)
file.each_line do |line|
  if matches >= max_matches
    break
  end
  if pattern =~ line
    matches += 1
    puts line
  end
end
file.close

List 6.15 : strip.rb

file = File.open(ARGV[0])
file.each_line do |line|
  next if /^\s*$/ =~ line  # 空白行
  next if /^#/ =~ line     # ハッシュ記号で始まる行
  puts line
end
file.close

List 6.16 : hello.rb

# Hello, world
puts "hello, world"

# 日本語
puts "こんにちは世界"

# 中文
puts "你好,世界"

List 6.17 : stripped_hello.rb

puts "hello, world"
puts "こんにちは世界"
puts "你好,世界"

第7章 メソッド

List 7.1 : times_with_param.rb

5.times do |i|
  puts "#{i}回目の繰り返しです。"
end

List 7.2 : hello_with_name.rb

def hello(name)
  puts "Hello, #{name}."
end

hello("Ruby")

List 7.3 : hello_with_default.rb

def hello(name="Ruby")
  puts "Hello, #{name}."
end

hello()         # 引数を省略して呼び出す
hello("Newbie") # 引数を指定して呼び出す

List 7.4 : myloop.rb

def myloop
  while true
    yield               # ブロックを実行する
  end
end

num = 1                 # numを初期化する
myloop do
  puts "num is #{num}"  # numを表示する
  break if num > 10     # numが10を超えていたら抜ける
  num *= 2              # numを2倍する
end

第8章 クラスとモジュール

List 8.1 : receipt.rb

class Receipt
  def initialize(name)
    @name = name        # インスタンス変数の初期化
    @lines = []
  end

  def lines=(lines)
    @lines = lines
  end

  def calc
    total = 0
    @lines.each do |line|
      total += line[:price] * line[:num]
    end
    total
  end

  def output
    puts "レシート #{@name}"
    @lines.each do |line|
      puts "#{line[:name]} #{line[:price]}円 x #{line[:num]}"
    end
    puts "合計金額: #{calc}円"
  end
end

r = Receipt.new("ストアA")
r.lines = [{name: "卵", price: 200, num: 1},
           {name: "大根", price: 100, num: 2}]
r.output

List 8.2 : receipt.rb(抜粋)

class Receipt
# …
  def name         # @nameを参照する
    @name
  end

  def name=(name)  # @nameを変更する
    @name = name
  end
# …
end

List 8.3 : receipt.rb(抜粋)

class Receipt
  attr_accessor :name
# …
  def output
    puts "レシート #{self.name}"
  end
end
# …

List 8.4 : receipt_count.rb

class Receipt
  @@count = 0           # publishメソッドの呼び出し回数

  def Receipt.count     # 呼び出し回数を参照するためのクラスメソッド
    @@count
  end

  def initialize(name)
    @name = name        # インスタンス変数の初期化
    @lines = []
  end

  def lines=(lines)
    @lines = lines
  end

  def calc
    total = 0
    @lines.each do |line|
      total += line[:price] * line[:num]
    end
    total
  end

  def output            # インスタンスメソッド
    puts "レシート #{@name}"
    @lines.each do |line|
      puts "#{line[:name]} #{line[:price]}円 x #{line[:num]}"
    end
    puts "合計金額: #{calc}円"
    @@count += 1        # 呼び出し回数を加算する
  end
end

r1 = Receipt.new("ストアA")
r2 = Receipt.new("ストアB")

p Receipt.count      #=> 0
r1.output
r2.output
p Receipt.count      #=> 2

List 8.5 : access_test.rb

class AccessTest
  def pub
    puts "pub is a public method."
  end

  public :pub   # pubメソッドをpublicに設定(指定しなくてもよい)

  def priv
    puts "priv is a private method."
  end

  private :priv # privメソッドをprivateに設定
end

access = AccessTest.new
access.pub
access.priv

List 8.6 : point.rb

class Point
  attr_accessor :x, :y   # アクセスメソッドを定義する
  protected :x=, :y=     # x=とy=をprotectedにする

  def initialize(x=0.0, y=0.0)
    @x, @y = x, y
  end

  def swap(other)        # x、yの値を入れ替えるメソッド
    tmp_x, tmp_y = @x, @y
    @x, @y = other.x, other.y
    other.x, other.y = tmp_x, tmp_y   # 同一クラス内では
                                      # 呼び出すことができる
    return self
  end
end

p0 = Point.new
p1 = Point.new(1.0, 2.0)
p [ p0.x, p0.y ]         #=> [0.0, 0.0]
p [ p1.x, p1.y ]         #=> [1.0, 2.0]

p0.swap(p1)
p [ p0.x, p0.y ]         #=> [1.0, 2.0]
p [ p1.x, p1.y ]         #=> [0.0, 0.0]

p0.x = 10.0              #=> エラー(NoMethodError)

List 8.7 : ext_string.rb

class String
  def count_word
    ary = self.split(" ")   # selfを空白文字区切りで
                            # 配列に分解する
    return ary.size         # 分解後の配列の要素数を返す
  end
end

str = "Just Another Ruby Newbie"
p str.count_word            #=> 4

List 8.8 : ring_array.rb

class RingArray < Array  # スーパークラスを指定する
  def [](i)              # 演算子[]の再定義
    idx = i % size       # 新しいインデックスを求める
    super(idx)           # スーパークラスの同名のメソッドを呼ぶ
  end
end

wday = RingArray["日", "月", "火", "水", "木", "金", "土"]
p wday[6]   #=> "土"
p wday[11]  #=> "木"
p wday[15]  #=> "月"
p wday[-1]  #=> "土"

List 8.9 : alias_sample.rb

class C1                 # C1クラスの定義
  def hello              # helloを定義
    "Hello"
  end
end

class C2 < C1            # C1クラスを継承してC2クラスを定義
  alias old_hello hello  # 別名old_helloを設定

  def hello              # helloを再定義
    "#{old_hello}, again"
  end
end

obj = C2.new
p obj.old_hello          #=> "Hello"
p obj.hello              #=> "Hello, again"

List 8.10 : mixin_sample.rb

module MyModule
  # 共通して提供したいメソッドなど
end

class MyClass1
  include MyModule
  # MyClass1に固有のメソッドなど
end

class MyClass2
  include MyModule
  # MyClass2に固有のメソッドなど
end

List 8.11 : hello_module.rb

module HelloModule          # module文
  VERSION = "1.0"           # 定数の定義

  def hello(name)           # メソッドの定義
    puts "Hello, #{name}."
  end
  module_function :hello    # helloをモジュール関数として公開する
end

p HelloModule::VERSION      #=> "1.0"
HelloModule.hello("Alice")  #=> Hello, Alice.

include HelloModule         # インクルードしてみる
p VERSION                   #=> "1.0"
hello("Alice")              #=> Hello, Alice.

List 8.12 : mixin_test.rb

module M
  def meth
    "meth"
  end
end

class C
  include M  # モジュールMをインクルードする
end

c = C.new
p c.meth     #=> "meth"

List 8.13 : prepend_test.rb

module M
  def meth
    "M#meth"
  end
end

class C
  prepend M  # 継承順序でモジュールMをクラスCの手前に追加する

  def meth
    "C#meth"
  end
end

c = C.new
p C.ancestors #=> [M, C, Object, Kernel, BasicObject]
p c.meth      #=> "M#meth"

List 8.14 : fetch_and_downcase.rb

def fetch_and_downcase(ary, index)
  if str = ary[index]
    return str.downcase
  end
end

ary = ["Boo", "Foo", "Woo"]
p fetch_and_downcase(ary, 1)  #=> "foo"

List 8.15 : http_get.rb

require "net/http"
require "uri"
url = URI.parse("https://www.ruby-lang.org/ja/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
doc = http.get(url.path)
puts doc.body

第9章 演算子

List 9.1 : point.rb

class Point
  attr_accessor :x, :y

  def initialize(x=0, y=0)
    @x, @y = x, y
  end

  def inspect  # pメソッドで「(x, y)」と表示する
    "(#{x}, #{y})"
  end

  def +(other)  # x、yのそれぞれを足す
    self.class.new(x + other.x, y + other.y)
  end

  def -(other)  # x、yのそれぞれを引く
    self.class.new(x - other.x, y - other.y)
  end
end

point0 = Point.new(3, 6)
point1 = Point.new(1, 8)

p point0           #=> (3, 6)
p point1           #=> (1, 8)
p point0 + point1  #=> (4, 14)
p point0 - point1  #=> (2, -2)

List 9.2 : point.rb(抜粋)

class Point
  attr_accessor :x, :y

  def initialize(x=0, y=0)
    @x, @y = x, y
  end

  def inspect  # pメソッドで「(x, y)」と表示する
    "(#{x}, #{y})"
  end

  def +(other)  # x、yのそれぞれを足す
    self.class.new(x + other.x, y + other.y)
  end

  def -(other)  # x、yのそれぞれを引く
    self.class.new(x - other.x, y - other.y)
  end

  def +@
    dup                     # 自分の複製を返す
  end

  def -@
    self.class.new(-x, -y)  # x、yのそれぞれの正負を逆にする
  end

  def ~@
    self.class.new(-y, x)   # 90度反転させた座標を返す
  end
end

point = Point.new(3, 6)
p +point  #=> (3, 6)
p -point  #=> (-3, -6)
p ~point  #=> (-6, 3)

List 9.3 : point.rb(抜粋)

class Point
  attr_accessor :x, :y

  def initialize(x=0, y=0)
    @x, @y = x, y
  end

  def inspect  # pメソッドで「(x, y)」と表示する
    "(#{x}, #{y})"
  end

  def +(other)  # x、yのそれぞれを足す
    self.class.new(x + other.x, y + other.y)
  end

  def -(other)  # x、yのそれぞれを引く
    self.class.new(x - other.x, y - other.y)
  end

  def +@
    dup                     # 自分の複製を返す
  end

  def -@
    self.class.new(-x, -y)  # x、yのそれぞれの正負を逆にする
  end

  def ~@
    self.class.new(-y, x)   # 90度反転させた座標を返す
  end

  def [](index)
    case index
    when 0
      x
    when 1
      y
    else
      raise ArgumentError, "out of range `#{index}'" end
  end

  def []=(index, val)
    case index
    when 0
      self.x = val
    when 1
      self.y = val
    else
      raise ArgumentError, "out of range `#{index}'"
    end
  end
end

point = Point.new(3, 6)
p point[0]           #=> 3
p point[1] = 2       #=> 2
p point[1]           #=> 2
p point[2]           #=>エラー(ArgumentError)

第10章 エラー処理と例外

List 10.1 : wc.rb

ltotal = 0                           # 行数の合計
wtotal = 0                           # 単語数の合計
ctotal = 0                           # 文字数の合計
ARGV.each do |file|
  begin
    input = File.open(file)          # ファイルを開く@<b>{(A)}
    l = 0                            # file内の行数
    w = 0                            # file内の単語数
    c = 0                            # file内の文字数
    input.each_line do |line|
      l += 1
      c += line.size
      line.sub!(/^\s+/, "")          # 行頭の空白を削除
      ary = line.split(/\s+/)        # 空白文字で分解する
      w += ary.size
    end
    input.close                      # ファイルを閉じる
    printf("%8d %8d %8d %s\n", l, w, c, file)  # 出力を整形する
    ltotal += l
    wtotal += w
    ctotal += c
  rescue => ex
    puts ex.message                  # 例外のメッセージを出力@<b>{(B)}
  end
end

printf("%8d %8d %8d %s\n", ltotal, wtotal, ctotal, "total")

第11章 ブロック

List 11.1 : hash_each.rb

sum = 0
outcome = {"参加費"=>1000, "ストラップ代"=>1000, "懇親会会費"=>4000}
outcome.each do |pair|
  sum += pair[1]  # 値を指定している
end
puts "合計 : #{sum}"

List 11.2 : hash_each2.rb

sum = 0
outcome = {"参加費"=>1000, "ストラップ代"=>1000, "懇親会会費"=>4000}
outcome.each do |item, price|
  sum += price
end
puts "合計 : #{sum}"

List 11.3 : file_each.rb

file = File.open("sample.txt")
file.each_line do |line|
  print line
end
file.close

List 11.4 : file_open.rb

File.open("sample.txt") do |file|
  file.each_line do |line|
    print line
  end
end

List 11.5 : file_open_no_block.rb

file = File.open("sample.txt")
begin
  file.each_line do |line|
    print line
  end
ensure
  file.close
end

List 11.6 : sort_comp_count.rb

# %w(...) は各単語を要素とする配列を生成するリテラルです
ary = %w(
  Ruby is a open source programming language with a focus
  on simplicity and productivity. It has an elegant syntax
  that is natural to read and easy to write
)

call_num = 0    # ブロックの呼び出し回数
sorted = ary.sort do |a, b|
  call_num += 1 # ブロックの呼び出し回数を加算する
  a.length <=> b.length
end

puts "ソートの結果 #{sorted}"
puts "配列の要素数 #{ary.length}"
puts "ブロックの呼び出し回数 #{call_num}"

List 11.7 : myloop.rb

def myloop
  while true
    yield               # ブロックを実行する
  end
end

num = 1                 # numを初期化する
myloop do
  puts "num is #{num}"  # numを表示する
  break if num > 10     # numが10を越えていたら抜ける
  num *= 2              # numを2倍する
end

List 11.8 : total.rb

def total(from, to)
  result = 0                # 合計の値
  from.upto(to) do |num|    # fromからtoまで処理する
    if block_given?         #   ブロックがあれば
      result += yield(num)  #     ブロックで処理した値を足す
    else                    #   ブロックがなければ
      result += num         #     そのまま足す
    end
  end
  return result             # メソッドの結果を返す
end

p total(1, 10)                  # 1から10の和 => 55
p total(1, 10) {|num| num ** 2} # 1から10の2乗の値の和 => 385

List 11.9 : block_args_test.rb

def block_args_test
  yield()             # ブロック変数なし
  yield(1)            # ブロック変数1つ
  yield(1, 2, 3)      # ブロック変数3つ
end

puts "ブロック変数を|a|で受け取る"
block_args_test do |a|
  p [a]
end
puts

puts "ブロック変数を|a, b, c|で受け取る"
block_args_test do |a, b, c|
  p [a, b, c]
end
puts

puts "ブロック変数を|*a|で受け取る"
block_args_test do |*a|
  p [a]
end
puts

List 11.10 : param_grouping.rb

hash = {a: 100, b: 200, c: 300}
hash.each_with_index do |(key, value), index|
  p [key, value, index]
end

List 11.11 : proc1.rb

hello = Proc.new do |name|
  puts "Hello, #{name}."
end

hello.call("World")
hello.call("Ruby")

List 11.12 : total2.rb

def total2(from, to, &block)
  result = 0               # 合計の値
  from.upto(to) do |num|   # fromからtoまで処理する
    if block               #   ブロックがあれば
      result +=            #     ブロックで処理した値を足す
           block.call(num)
    else                   #   ブロックがなければ
      result += num        #     そのまま足す
    end
  end
  return result            # メソッドの結果を返す
end

p total2(1, 10)                    # 1から10の和 => 55
p total2(1, 10) {|num| num ** 2}  # 1から10の2乗の値の和 => 385

List 11.13 : call_each.rb

def call_each(ary, &block)
  ary.each(&block)
end

call_each [1, 2, 3] do |item|
  p item
end

List 11.14 : local_and_block.rb

x = 0            # xを初期化
y = 0            # yを初期化
ary = [1, 2, 3]

ary.each do |x|  # ブロック変数としてxを使用する
  y = x          # yにxを代入する
end

p [x, y]         # xとyの値を確認する

List 11.15 : local_and_block2.rb

x = y = z = 0       # xとyとzを初期化
ary = [1, 2, 3]
ary.each do |x; y|  # ブロック変数x、ブロックローカル変数yを使用
  y = x             # ブロックローカル変数yを代入
  z = x             # ブロックローカルでない変数zを代入
  p [x, y, z]       # ブロック内のx、y、zの値を確認する
end
puts
p [x, y, z]         # x、y、zの値を確認する

第12章 数値(Numeric)クラス

ファイル名のあるソースコードはありません。

第13章 配列(Array)クラス

List 13.1 : list.rb

list = ["a", "b", "c", "d"]
for i in 0..3
  puts "#{i+1}番目の要素は#{list[i]}です。"
end

List 13.2 : sum_list.rb

list = [1, 3, 5, 7, 9]
sum = 0
for i in 0..4
  sum += list[i]
end
puts "合計:#{sum}"

List 13.3 : sum_list2.rb

list = [1, 3, 5, 7, 9]
sum = 0
list.each do |elem|
  sum += elem
end
puts "合計:#{sum}"

List 13.4 : list2.rb

list = ["a", "b", "c", "d"]
list.each_with_index do |elem, i|
  puts "#{i+1}番目の要素は#{elem}です。"
end

List 13.5 : sum_with_each.rb

ary1 = [1, 2, 3, 4, 5]
ary2 = [10, 20, 30, 40, 50]
ary3 = [100, 200, 300, 400, 500]

i = 0
result = []
while i < ary1.length
  result << ary1[i] + ary2[i] + ary3[i]
  i += 1
end
p result  #=> [111, 222, 333, 444, 555]

List 13.6 : sum_with_zip.rb

ary1 = [1, 2, 3, 4, 5]
ary2 = [10, 20, 30, 40, 50]
ary3 = [100, 200, 300, 400, 500]

result = []
ary1.zip(ary2, ary3) do |a, b, c|
  result << a + b + c
end
p result  #=> [111, 222, 333, 444, 555]

第14章 文字列(String)クラス

ファイル名のあるソースコードはありません。

第15章 ハッシュ(Hash)クラス

List 15.1 : word_count.rb

# 単語数のカウント
counts = Hash.new(0)

# 単語の集計
File.open(ARGV[0]) do |f|
  f.each_line do |line|
    words = line.split
    words.each do |word|
      counts[word] += 1 
    end
  end
end

# 結果のソート
sorted = counts.sort_by {|c| c[1] }

# 結果の出力
sorted.each do |word, count|
  puts "#{word}: #{count}"
end

第16章 正規表現(Regexp)クラス

List 16.1 : scan1.rb

"abracatabra".scan(/.a/) do |matched|
  p matched
end

List 16.2 : scan2.rb

"abracatabra".scan(/(.)(a)/) do |matched|
  p matched
end

List 16.3 : scan3.rb

"abracatabra".scan(/(.)(a)/) do |a, b|
  p a+"-"+b
end

List 16.4 : url_match.rb

str = "https://www.ruby-lang.org/ja/"
%r|https?://([^/]*)/| =~ str
puts "server address: #{$1}"

第17章 IOクラス

List 17.1 : out.rb

3.times do |i|
  $stdout.puts "#{Random.rand}"        # 標準出力へ
  $stderr.puts "#{i+1}回出力しました"  # 標準エラー出力へ
end

List 17.2 : tty.rb

if $stdin.tty?
  puts "Stdin is a TTY."
else
  puts "Stdin is not a TTY."
end

List 17.3 : stdout_put.rb

$stdout.puts "String", :Symbol, 1/100r

List 17.4 : stdout_putc.rb

$stdout.putc(82)  # 82は「R」のASCIIコード
$stdout.putc("Ruby")
$stdout.putc("\n")

List 17.5 : simple_grep_gz.rb

pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
if /\.gz$/ =~ filename
  file = IO.popen("zcat #{filename}")
else
  file = File.open(filename)
end
file.each_line do |line|
  if pattern =~ line
    print line
  end
end

List 17.6 : read_uri.rb

require "open-uri"

filename = "ruby-2.6.1.zip"               # ソースコードのファイル名
version = filename.scan(/\d+\.\d+/).first # 「2.6」の部分を取り出す

# RubyのソースコードのURL
url = "https://cache.ruby-lang.org/pub/ruby/#{version}/#{filename}"

# URLを指定して読み込み用のIOオブジェクトを得る
open(url) do |remote|
  # 書き込み用のファイルをバイナリモードで開く
  File.open(filename, "wb") do |local|
    while data = remote.read(10000)   # 10Kバイトずつデータを読んで
      local.write(data)               # ファイルに書き込む
    end
  end
end

List 17.7 : stringio_puts.rb

require "stringio"

io = StringIO.new
io.puts("A")
io.puts("B")
io.puts("C")
io.rewind
p io.read  #=> "A\nB\nC\n"

List 17.8 : stringio_gets.rb

require "stringio"

io = StringIO.new("A\nB\nC\n")
p io.gets  #=> "A\n"
p io.gets  #=> "B\n"
p io.gets  #=> "C\n"

第18章 FileクラスとDirクラス

List 18.1 : traverse.rb

def traverse(path)
  if File.directory?(path)  # ディレクトリの場合
    dir = Dir.open(path)
    while name = dir.read
      next if name == "."   # ※
      next if name == ".."  # ※
      traverse(path + "/" + name)
    end
    dir.close
  else
    process_file(path)      # ファイルに対する処理
  end
end

def process_file(path)
  puts path                 # ひとまず出力するだけ
end

traverse(ARGV[0])

List 18.2 : traverse_by_glob.rb

def traverse(path)
  Dir.glob(["#{path}/**/*", "#{path}/**/.*"]) do |name|
    unless File.directory?(name)
      process_file(name)
    end
  end
end

def process_file(path)
  puts path                 # ひとまず出力するだけ
end

traverse(ARGV[0])

List 18.3 : listdir.rb

require "find"

IGNORES = [ /^\./, /^\.svn$/, /^\.git$/ ]

def listdir(top)
  Find.find(top) do |path|
    if File.directory?(path)  # pathがディレクトリならば
      dir, base = File.split(path)
      IGNORES.each do |re|
        if re =~ base             # 無視したいディレクトリの場合
          Find.prune              # それ以下の検索を省略する
        end
      end
      puts path                   # 出力する
    end
  end
end

listdir(ARGV[0])

第19章 エンコーディング(Encoding)クラス

List 19.1 : utf8mac.rb

# encoding: UTF-8
Dir.glob("*.txt") do |filename|
  if filename == "ルビー.txt"
    puts "found."
    exit
  end
end
puts "not found."

List 19.2 : utf8mac_fix.rb

# encoding: UTF-8
Dir.glob("*.txt") do |filename|
  if filename.encode("UTF8-MAC") == "ルビー.txt".encode("UTF8-MAC")
    puts "found."; exit
  end
end
puts "not found."

List 19.3 : utf8mac_str.rb

# encoding: utf-8
str = "ビ"
puts "size: #{str.size}"
p str.each_byte.map {|b| b.to_s(16)}
puts "size: #{str.encode("UTF8-MAC").size}"
p str.encode("UTF8-MAC").each_byte.map {|b| b.to_s(16)}

第20章 TimeクラスとDateクラス

ファイル名のあるソースコードはありません。

第21章 Procクラス

List 21.1 : power_of.rb

def power_of(n)
  lambda do |x|
    return x ** n
  end
end

cube = power_of(3)
p cube.call(5)  #=> 125

List 21.2 : prefix.rb

def prefix(ary, obj)
  result = []                # 結果の配列を初期化する
  ary.each do |item|         # 要素を1つずつ見ながら
    result << item           # 要素を結果の配列に追加する
    if item == obj           # 要素が条件に一致するものがあれば
      return result          # 結果の配列を返す
    end
  end
  return result              # すべての要素を検査し終わった場合
end

p prefix([1, 2, 3, 4, 5], 3) #=> [1, 2, 3]

List 21.3 : total2.rb

def total2(from, to, &block)
  result = 0               # 合計の値
  from.upto(to) do |num|   # fromからtoまで処理する
    if block               #   ブロックがあれば
      result +=            #     ブロックで処理した値を足す
           block.call(num)
    else                   #   ブロックがなければ
      result += num        #     そのまま足す
    end
  end
  return result            # メソッドの結果を返す
end

p total2(1, 10)                   # 1から10の和 => 55
p total2(1, 10) {|num| num ** 2}  # 1から10の2乗の値の和 => 385

List 21.4 : counter_proc.rb

def counter
  c = 0          # カウンターを初期化する
  Proc.new do    # callメソッドを呼ぶたびにカウンターに
    c += 1       # 1を足して返すProcオブジェクトを返す
  end
end

# カウンターc1を作成してカウントアップする
c1 = counter
p c1.call      #=> 1
p c1.call      #=> 2
p c1.call      #=> 3

# カウンターc2を作成してカウントアップする
c2 = counter   # カウンターc2を作成
p c2.call      #=> 1
p c2.call      #=> 2

# 再びc1をカウントアップする
p c1.call      #=> 4

List 21.5 : proc_source_location.rb

prc0 = Proc.new {nil}
prc1 = Proc.new {|a| a}

p prc0.source_location
p prc1.source_location

第22章 テキスト処理を行う

List 22.1 : get_akage.rb

require "open-uri"

url = "https://www.aozora.gr.jp/cards/000009/files/8_31220.html"
filename = "akage.html"

File.open(filename, "wb:UTF-8") do |f|
  text = open(url, "r:Shift_JIS:UTF-8").read
  f.write text
end

List 22.2 : cut_akage.rb

htmlfile = "akage.html"
textfile = "akage.txt"

html = File.read(htmlfile, encoding: "UTF-8")

File.open(textfile, "w:UTF-8") do |f|
  in_header = true
  html.each_line do |line|
    if in_header && /<div class="main_text">/ !~ line
      next
    else
      in_header = false
    end
    break if /<div class="after_text">/ =~ line
    f.write line
  end
end

List 22.3 : cut_akage2.rb

require "cgi/util"
htmlfile = "akage.html"
textfile = "akage.txt"

html = File.read(htmlfile, encoding: "UTF-8")

File.open(textfile, "w:UTF-8") do |f|
  in_header = true
  html.each_line do |line|
    if in_header && /<div class="main_text">/ !~ line
      next
    else
      in_header = false
    end
    break if /<div class="after_text">/ =~ line
    line.gsub!(/<[^>]+>/, "")
    esc_line = CGI.unescapeHTML(line)
    f.write esc_line
  end
end

List 22.4 : simple_grep.rb

pattern = Regexp.new(ARGV[0].encode("UTF-8"))
filename = ARGV[1]

File.open(filename, "r:UTF-8") do |file|
  file.each_line do |line|
    if pattern =~ line
      print line
    end
  end
end

List 22.5 : simple_scan.rb

pattern = Regexp.new(ARGV[0].encode("UTF-8"))
filename = ARGV[1]

count = 0
File.open(filename, "r:UTF-8") do |file|
  file.each_line do |line|
    if pattern =~ line
      line.scan(pattern) do |s|
        count += 1
      end
      print line
    end
  end
end
puts "count: #{count}"

List 22.6 : simple_count.rb

pattern = Regexp.new(ARGV[0].encode("UTF-8"))
filename = ARGV[1]

count = 0
File.read(filename, encoding: "UTF-8").scan(pattern) do |s|
  count += 1
end
puts "count: #{count}"

List 22.7 : simple_match.rb

pattern = Regexp.new(ARGV[0].encode("UTF-8"))
filename = ARGV[1]

count = 0
File.open(filename, "r:UTF-8") do |file|
  file.each_line do |line|
    if pattern =~ line
      line.scan(pattern) do |s|
        count += 1
      end
      print line.gsub(pattern) {|str| "<<#{str}>>"}
    end
  end
end
puts "count: #{count}"

List 22.8 : simple_match2.rb

pattern = Regexp.new("(.{10})("+ARGV[0].encode("UTF-8")+")(.{10})")
filename = ARGV[1]

count = 0
File.open(filename, "r:UTF-8") do |file|
  file.each_line do |line|
    line.scan(pattern) do |s|
      puts "#{s[0]}<<#{s[1]}>>#{s[2]}"
      count += 1
    end
  end
end
puts "count: #{count}"

List 22.9 : simple_match3.rb

pattern = Regexp.new(ARGV[0].encode("UTF-8"))
filename = ARGV[1]

count = 0
File.open(filename, "r:UTF-8") do |file|
  file.each_line do |line|
    line.scan(pattern) do |s|
      pre = "\u3000" * 10 + $`
      post = $'
      puts "#{pre[-10, 10]}<<#{s}>>#{post[0, 10]}"
      count += 1
    end
  end
end
puts "count: #{count}"

List 22.10 : simple_match4.rb

pattern = Regexp.new(ARGV[0].encode("UTF-8"))
filename = ARGV[1]
len = ARGV[2].to_i

count = 0
File.open(filename, "r:UTF-8") do |file|
  file.each_line do |line|
    line.scan(pattern) do |s|
      pre = "\u3000" * len + $`
      post = $'
      puts "#{pre[-len, len]}<<#{s}>>#{post[0, len]}"
      count += 1
    end
  end
end
puts "count: #{count}"

第23章 郵便番号データを検索する

List 23.1 : read_csv.rb

require "csv"            # csvライブラリを使う

code = ARGV[0]           # 引数を取り出す
start_time = Time.now    # 処理の開始時刻を取得する

# Shift_JISをUTF-8に変換する指定をしてCSVファイルを開く
CSV.open("KEN_ALL.CSV", "r:Shift_JIS:UTF-8") do |csv|
  csv.each do |record|
    # 郵便番号が引数の指定と一致したらそのレコードを表示する
    puts record.join(" ") if record[2] == code
  end
end
p Time.now - start_time  # 処理が終了した時刻との差を表示する

List 23.2 : jzipcode.rb

require "sqlite3"
require "csv"

class JZipCode
  CSV_COLUMN = {code: 2, pref: 6, city: 7, addr: 8}

  def initialize(dbfile)
    @dbfile = dbfile                                  # ①
  end

  def create(zipfile)
    return if File.exist?(@dbfile)                    # ②
    SQLite3::Database.open(@dbfile) do |db|           # ③
      db.execute(<<-SQL)
        CREATE TABLE IF NOT EXISTS zip_codes
        (code TEXT, pref TEXT, city TEXT, addr TEXT, alladdr TEXT)
      SQL
      db.execute("BEGIN TRANSACTION")                 # ④
      CSV.open(zipfile, "r:Shift_JIS:UTF-8") do |csv|
        csv.each do |rec|
          data = Hash.new                             # ⑤
          CSV_COLUMN.each {|key, index| data[key] = rec[index]}
          data[:alladdr] = data[:pref] + data[:city] + data[:addr]
          db.execute(<<-SQL, data)                    # ⑥
            INSERT INTO zip_codes VALUES
              (:code, :pref, :city, :addr, :alladdr)
          SQL
        end
      end
      db.execute("COMMIT TRANSACTION")                # ⑦
    end
    return true
  end
end

List 23.3 : jzipcode.rb

class JZipCode
# # …
  def find_by_code(code)
    ret = []
    SQLite3::Database.open(@dbfile) do |db|
      db.execute(<<-SQL, code) {|row| ret << row.join(" ")}
        SELECT code, alladdr
          FROM zip_codes
         WHERE code = ?
      SQL
    end
    return ret.map {|line| line + "\n"}.join
  end

  def find_by_address(addr)
    ret = []
    SQLite3::Database.open(@dbfile) do |db|
      like = "%#{addr}%"
      db.execute(<<-SQL, like) {|row| ret << row.join(" ")}
        SELECT code, alladdr
          FROM zip_codes
         WHERE alladdr LIKE ?
      SQL
    end
    return ret.map {|line| line + "\n"}.join
  end
end

List 23.4 : postal.rb

require_relative "jzipcode"

start_time = Time.now
db = File.join(__dir__, "jzipcode.db")
csv = File.join(__dir__, "KEN_ALL.CSV")
jzipcode = JZipCode.new(db)
jzipcode.create(csv)

keyword = ARGV[0]
result = jzipcode.find_by_code(keyword)
if result.empty?
  result = jzipcode.find_by_address(keyword)
end
puts result
puts
puts "#{Time.now - start_time}秒"

List 23.5 : Gemfile(bundle initの直後)

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# gem "rails"

List 23.6 : Gemfile(変更後)

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}"}

gem "sqlite3"