|
| 1 | +# 第10章: 標準ライブラリの活用 |
| 2 | + |
| 3 | +Rubyの強力な点の一つは、多くの一般的なタスクを処理するための豊富な「標準ライブラリ」が同梱されていることです。これらは "batteries included"(電池付属)とよく表現されます。 |
| 4 | + |
| 5 | +他の言語で `import` や `include` を使うのと同様に、Rubyでは `require` を使ってこれらのライブラリをロードします。ただし、`File` や `Time`、`Regexp` のようなコア機能の多くは、`require` なしで利用可能です。 |
| 6 | + |
| 7 | +この章では、特に使用頻度の高い標準ライブラリの機能を見ていきます。 |
| 8 | + |
| 9 | +## ファイル操作 (File, Dir, Pathname) |
| 10 | + |
| 11 | +ファイルシステムとのやり取りは、多くのアプリケーションで不可欠です。 |
| 12 | + |
| 13 | +### Fileクラスによる読み書き |
| 14 | + |
| 15 | +`File` クラスは、ファイルに対する基本的な読み書き操作を提供します。 |
| 16 | + |
| 17 | +**ファイルの書き込みと追記:** |
| 18 | + |
| 19 | +```ruby:file_io_example.rb |
| 20 | +# 1. ファイルへの書き込み (上書き) |
| 21 | +# シンプルな方法は File.write です |
| 22 | +File.write('sample.txt', "Hello, Ruby Standard Library!\n") |
| 23 | + |
| 24 | +# 2. ファイルへの追記 |
| 25 | +# mode: 'a' (append) オプションを指定します |
| 26 | +File.write('sample.txt', "This is a second line.\n", mode: 'a') |
| 27 | + |
| 28 | +puts "File 'sample.txt' created and updated." |
| 29 | +``` |
| 30 | + |
| 31 | +```ruby-exec:file_io_example.rb |
| 32 | +File 'sample.txt' created and updated. |
| 33 | +``` |
| 34 | + |
| 35 | +```text-readonly:sample.txt |
| 36 | +``` |
| 37 | + |
| 38 | +**ファイルの読み込み:** |
| 39 | + |
| 40 | +```ruby:file_read_example.rb |
| 41 | +# 'sample.txt' が存在すると仮定 |
| 42 | + |
| 43 | +# 1. ファイル全体を一度に読み込む |
| 44 | +content = File.read('sample.txt') |
| 45 | +puts "--- Reading all at once ---" |
| 46 | +puts content |
| 47 | + |
| 48 | +# 2. 1行ずつ処理する (大きなファイルに効率的) |
| 49 | +puts "\n--- Reading line by line ---" |
| 50 | +File.foreach('sample.txt') do |line| |
| 51 | + print "Line: #{line}" |
| 52 | +end |
| 53 | + |
| 54 | +# 3. 処理後にファイルをクリーンアップ |
| 55 | +File.delete('sample.txt') |
| 56 | +puts "\n\nFile 'sample.txt' deleted." |
| 57 | +``` |
| 58 | + |
| 59 | +```ruby-exec:file_read_example.rb |
| 60 | +--- Reading all at once --- |
| 61 | +Hello, Ruby Standard Library! |
| 62 | +This is a second line. |
| 63 | + |
| 64 | +--- Reading line by line --- |
| 65 | +Line: Hello, Ruby Standard Library! |
| 66 | +Line: This is a second line. |
| 67 | + |
| 68 | +File 'sample.txt' deleted. |
| 69 | +``` |
| 70 | + |
| 71 | +### DirクラスとPathname |
| 72 | + |
| 73 | +`Dir` クラスはディレクトリの内容を操作するために使われます。特に `Dir.glob` はワイルドカードを使ってファイルやディレクトリを検索するのに便利です。 |
| 74 | + |
| 75 | +しかし、パスの連結や解析を文字列として扱うのは面倒です。`Pathname` ライブラリは、パスをオブジェクトとして扱うための優れたインターフェースを提供します。 |
| 76 | + |
| 77 | +```ruby-repl:1 |
| 78 | +irb(main):001:0> # Dir.glob はワイルドカードでファイルリストを取得できます |
| 79 | +irb(main):002:0> Dir.glob('*.rb') # (irbを実行しているディレクトリによります) |
| 80 | +=> [] |
| 81 | +
|
| 82 | +irb(main):003:0> # Pathname を使うには require が必要 |
| 83 | +irb(main):004:0> require 'pathname' |
| 84 | +=> true |
| 85 | +
|
| 86 | +irb(main):005:0> # 文字列の代わりに Pathname オブジェクトを作成 |
| 87 | +irb(main):006:0> base_path = Pathname.new('/usr/local') |
| 88 | +=> #<Pathname:/usr/local> |
| 89 | +
|
| 90 | +irb(main):007:0> # + や / 演算子で安全にパスを連結できます |
| 91 | +irb(main):008:0> lib_path = base_path + 'lib' |
| 92 | +=> #<Pathname:/usr/local/lib> |
| 93 | +irb(main):009:0> bin_path = base_path / 'bin' |
| 94 | +=> #<Pathname:/usr/local/bin> |
| 95 | +
|
| 96 | +irb(main):010:0> # パスの解析 |
| 97 | +irb(main):011:0> file_path = Pathname.new('/var/log/app.log') |
| 98 | +=> #<Pathname:/var/log/app.log> |
| 99 | +irb(main):012:0> file_path.basename # ファイル名 |
| 100 | +=> #<Pathname:app.log> |
| 101 | +irb(main):013:0> file_path.extname # 拡張子 |
| 102 | +=> ".log" |
| 103 | +irb(main):014:0> file_path.dirname # 親ディレクトリ |
| 104 | +=> #<Pathname:/var/log> |
| 105 | +irb(main):015:0> file_path.absolute? # 絶対パスか? |
| 106 | +=> true |
| 107 | +``` |
| 108 | + |
| 109 | +## 日付と時刻 (Time, Date) |
| 110 | + |
| 111 | +Rubyには `Time`(組み込み)と `Date`(要 `require`)の2つの主要な日時クラスがあります。 |
| 112 | + |
| 113 | + * **Time:** 時刻(タイムスタンプ)をナノ秒までの精度で扱います。 |
| 114 | + * **Date:** 日付(年月日)のみを扱い、カレンダー計算に特化しています。 |
| 115 | + |
| 116 | +```ruby-repl:2 |
| 117 | +irb(main):001:0> # Time (組み込み) |
| 118 | +irb(main):002:0> now = Time.now |
| 119 | +=> 2025-11-04 11:32:00 +0900 (JST) |
| 120 | +irb(main):003:0> now.year |
| 121 | +=> 2025 |
| 122 | +irb(main):004:0> now.monday? |
| 123 | +=> false |
| 124 | +irb(main):005:0> now.to_i # UNIXタイムスタンプ |
| 125 | +=> 1762309920 |
| 126 | +
|
| 127 | +irb(main):006:0> # strftime (string format time) でフォーマット |
| 128 | +irb(main):007:0> now.strftime("%Y-%m-%d %H:%M:%S") |
| 129 | +=> "2025-11-04 11:32:00" |
| 130 | +
|
| 131 | +irb(main):008:0> # Date (require が必要) |
| 132 | +irb(main):009:0> require 'date' |
| 133 | +=> true |
| 134 | +irb(main):010:0> today = Date.today |
| 135 | +=> #<Date: 2025-11-04 ((2461014j,0s,0n),+0s,2299161j)> |
| 136 | +irb(main):011:0> today.strftime("%A") # 曜日 |
| 137 | +=> "Tuesday" |
| 138 | +
|
| 139 | +irb(main):012:0> # 文字列からのパース |
| 140 | +irb(main):013:0> christmas = Date.parse("2025-12-25") |
| 141 | +=> #<Date: 2025-12-25 ((2461065j,0s,0n),+0s,2299161j)> |
| 142 | +irb(main):014:0> (christmas - today).to_i # あと何日? |
| 143 | +=> 51 |
| 144 | +``` |
| 145 | + |
| 146 | +## JSONのパースと生成 (json) |
| 147 | + |
| 148 | +現代のWeb開発においてJSONの扱いは不可欠です。`json` ライブラリは、JSON文字列とRubyのHash/Arrayを相互に変換する機能を提供します。 |
| 149 | + |
| 150 | +```ruby-repl:3 |
| 151 | +irb(main):001:0> require 'json' |
| 152 | +=> true |
| 153 | +
|
| 154 | +irb(main):002:0> # 1. JSON文字列 -> Rubyオブジェクト (Hash) へのパース |
| 155 | +irb(main):003:0> json_data = '{"user_id": 123, "name": "Alice", "tags": ["admin", "ruby"]}' |
| 156 | +=> "{\"user_id\": 123, \"name\": \"Alice\", \"tags\": [\"admin\", \"ruby\"]}" |
| 157 | +
|
| 158 | +irb(main):004:0> parsed_data = JSON.parse(json_data) |
| 159 | +=> {"user_id"=>123, "name"=>"Alice", "tags"=>["admin", "ruby"]} |
| 160 | +irb(main):005:0> parsed_data['name'] |
| 161 | +=> "Alice" |
| 162 | +irb(main):006:0> parsed_data['tags'] |
| 163 | +=> ["admin", "ruby"] |
| 164 | +
|
| 165 | +irb(main):007:0> # 2. Rubyオブジェクト (Hash) -> JSON文字列 への生成 |
| 166 | +irb(main):008:0> ruby_hash = { |
| 167 | +irb(main):009:1* status: "ok", |
| 168 | +irb(main):010:1* data: { item_id: 987, price: 1500 } |
| 169 | +irb(main):011:1* } |
| 170 | +=> {:status=>"ok", :data=>{:item_id=>987, :price=>1500}} |
| 171 | +
|
| 172 | +irb(main):012:0> # .to_json メソッドが便利です |
| 173 | +irb(main):013:0> json_output = ruby_hash.to_json |
| 174 | +=> "{\"status\":\"ok\",\"data\":{\"item_id\":987,\"price\":1500}}" |
| 175 | +
|
| 176 | +irb(main):014:0> # 人が読みやすいように整形 (pretty generate) |
| 177 | +irb(main):015:0> puts JSON.pretty_generate(ruby_hash) |
| 178 | +{ |
| 179 | + "status": "ok", |
| 180 | + "data": { |
| 181 | + "item_id": 987, |
| 182 | + "price": 1500 |
| 183 | + } |
| 184 | +} |
| 185 | +=> nil |
| 186 | +``` |
| 187 | + |
| 188 | +## 正規表現 (Regexp) と match |
| 189 | + |
| 190 | +Rubyの正規表現 (Regexp) は、Perl互換の強力なパターンマッチング機能を提供します。`/pattern/` リテラルで記述するのが一般的です。 |
| 191 | + |
| 192 | +### マッチの確認 (`=~` と `match`) |
| 193 | + |
| 194 | + * `=~` 演算子: マッチした位置のインデックス(0から始まる)を返すか、マッチしなければ `nil` を返します。 |
| 195 | + * `String#match`: `MatchData` オブジェクトを返すか、マッチしなければ `nil` を返します。`MatchData` は、キャプチャグループ(`()`で囲んだ部分)へのアクセスに便利です。 |
| 196 | + |
| 197 | +```ruby-repl:4 |
| 198 | +irb(main):001:0> text = "User: [email protected] (Alice Smith)" |
| 199 | +=> "User: [email protected] (Alice Smith)" |
| 200 | +
|
| 201 | +irb(main):002:0> # =~ は位置を返す |
| 202 | +irb(main):003:0> text =~ /alice/ |
| 203 | +=> 6 |
| 204 | +irb(main):004:0> text =~ /bob/ |
| 205 | +=> nil |
| 206 | +
|
| 207 | +irb(main):005:0> # String#match は MatchData を返す |
| 208 | +irb(main):006:0> # パターン: (ユーザー名)@(ドメイン) |
| 209 | +irb(main):007:0> match_data = text.match(/(\w+)@([\w\.]+)/) |
| 210 | +=> #<MatchData "[email protected]" 1:"alice" 2:"example.com"> |
| 211 | +
|
| 212 | +irb(main):008:0> # マッチしたオブジェクトからキャプチャを取得 |
| 213 | +irb(main):009:0> match_data[0] # マッチ全体 |
| 214 | + |
| 215 | +irb(main):010:0> match_data[1] # 1番目の () |
| 216 | +=> "alice" |
| 217 | +irb(main):011:0> match_data[2] # 2番目の () |
| 218 | +=> "example.com" |
| 219 | +``` |
| 220 | + |
| 221 | +### 検索と置換 (`scan` と `gsub`) |
| 222 | + |
| 223 | + * `String#scan`: マッチするすべての部分文字列を(キャプチャグループがあればその配列として)返します。 |
| 224 | + * `String#gsub`: マッチするすべての部分を置換します (Global SUBstitute)。 |
| 225 | + |
| 226 | +```ruby-repl:5 |
| 227 | +irb(main):001:0> log = "ERROR: code 500. WARNING: code 404. ERROR: code 403." |
| 228 | +=> "ERROR: code 500. WARNING: code 404. ERROR: code 403." |
| 229 | +
|
| 230 | +irb(main):002:0> # scan: 'ERROR: code (数字)' にマッチする部分をすべて探す |
| 231 | +irb(main):003:0> log.scan(/ERROR: code (\d+)/) |
| 232 | +=> [["500"], ["403"]] |
| 233 | +
|
| 234 | +irb(main):004:0> # gsub: 'ERROR' を 'CRITICAL' に置換する |
| 235 | +irb(main):005:0> log.gsub("ERROR", "CRITICAL") |
| 236 | +=> "CRITICAL: code 500. WARNING: code 404. CRITICAL: code 403." |
| 237 | +
|
| 238 | +irb(main):006:0> # gsub はブロックと正規表現を組み合わせて高度な置換が可能 |
| 239 | +irb(main):007:0> # 数字(コード)を [] で囲む |
| 240 | +irb(main):008:0> log.gsub(/code (\d+)/) do |match| |
| 241 | +irb(main):009:1* # $1 は最後のマッチの1番目のキャプチャグループ |
| 242 | +irb(main):010:1* "code [#{$1}]" |
| 243 | +irb(main):011:1> end |
| 244 | +=> "ERROR: code [500]. WARNING: code [404]. ERROR: code [403]." |
| 245 | +``` |
| 246 | + |
| 247 | +## この章のまとめ |
| 248 | + |
| 249 | + * Rubyには、`require` でロードできる豊富な**標準ライブラリ**が付属しています。 |
| 250 | + * **File**クラスはファイルの読み書きを、**Pathname**はパス操作をオブジェクト指向的に行います。 |
| 251 | + * **Time**は時刻を、**Date**は日付を扱います。`strftime` でフォーマットできます。 |
| 252 | + * **json**ライブラリは `JSON.parse`(文字列→Hash)と `to_json`(Hash→文字列)を提供します。 |
| 253 | + * **Regexp**(`/pattern/`)はパターンマッチングに使います。`String#match` で `MatchData` を取得し、`scan` や `gsub` で検索・置換を行います。 |
| 254 | + |
| 255 | +これらは標準ライブラリのごく一部です。他にもCSVの処理 (`csv`)、HTTP通信 (`net/http`)、テスト (`minitest`) など、多くの機能が提供されています。 |
| 256 | + |
| 257 | +### 練習問題1: JSON設定ファイルの読み書き |
| 258 | + |
| 259 | +1. `config.json` ファイルを読み込み、内容をJSONパースしてRubyのHashに変換してください。 |
| 260 | +2. そのHashの `logging` の値を `true` に変更し、さらに `:updated_at` というキーで現在時刻(文字列)を追加してください。 |
| 261 | +3. 変更後のHashをJSON文字列に変換し、`config_updated.json` という名前でファイルに保存してください。(読みやすさのために `JSON.pretty_generate` を使っても構いません) |
| 262 | + |
| 263 | +```json-readonly:config.json |
| 264 | +{"app_name": "RubyApp", "version": "1.0", "logging": false} |
| 265 | +``` |
| 266 | + |
| 267 | +```ruby:practice10_1.rb |
| 268 | +``` |
| 269 | + |
| 270 | +```ruby-exec:practice10_1.rb |
| 271 | +``` |
| 272 | + |
| 273 | +```json-readonly:config_updated.json |
| 274 | +``` |
| 275 | + |
| 276 | +### 練習問題2: ログファイルからの情報抽出 |
| 277 | + |
| 278 | +1. `system.log` というファイルを1行ずつ読み込みます。 |
| 279 | +2. 正規表現を使い、`[INFO]` で始まり、かつ `logged in` という文字列を含む行だけを検出してください。 |
| 280 | +3. マッチした行から、IPアドレス(`192.168.1.10` のような形式)を正規表現のキャプチャグループを使って抽出し、IPアドレスだけをコンソールに出力してください。 |
| 281 | + |
| 282 | +```text-readonly:system.log |
| 283 | +[INFO] 2025-11-04 User 'admin' logged in from 192.168.1.10 |
| 284 | +[WARN] 2025-11-04 Failed login attempt for user 'guest' |
| 285 | +[INFO] 2025-11-04 Service 'payment_gateway' started. |
| 286 | +``` |
| 287 | + |
| 288 | +```ruby:practice10_2.rb |
| 289 | +``` |
| 290 | + |
| 291 | +```ruby-exec:practice10_2.rb |
| 292 | +``` |
0 commit comments