【Ruby】エンコーディング

# はじめに

こんにちは！アメリカの大学で語学を学びながら、独学でソフトウェアエンジニアを目指している者です。

現在たのしいRubyでエンコーディングクラスについて勉強しています。

本記事では、Rubyにおけるエンコーディング（文字コード）についてまとめたいと思います。エンコーディングの基本から、それぞれの種類の役割まで解説していきます。

## エンコーディングとは？

エンコーディングとは、<strong>文字をバイト列に変換する方式のこと</strong>です。コンピュータは基本的にバイト単位でデータを扱うため、文字を扱うためには文字をバイトに変換（またはその逆）するルールが必要になります。これが**エンコーディング**の役割です。

たとえば、「こんにちは」という日本語の文字列をコンピュータに伝える際、その文字列をどうバイト列に変換するかをエンコーディングが決定します。

### なぜエンコーディングが重要なのか？

現在では、ファイルやデータを扱うとき、さまざまな<strong>異なる言語</strong>や**文字体系**をサポートする必要があります。そのため、適切なエンコーディングを使用しないと、文字化けやエラーが発生してしまいます。
特に日本語などのマルチバイト文字を扱う際には、エンコーディングの理解が必要不可欠です。

## Rubyにおけるエンコーディングの種類

Rubyでは、エンコーディングに関して主に以下の3つの概念があります。

1. **スクリプトエンコーディング**
2. **内部エンコーディング**
3. **外部エンコーディング**

それぞれがどのような役割を持つのか見ていきましょう。

### 1. スクリプトエンコーディング

スクリプトエンコーディングとは、Rubyのスクリプトファイル自体のエンコーディングを示します。ソースコードに記載された文字列がどのエンコーディングで書かれているかをRubyに教えるためのものです。

Rubyでは、ファイルの先頭にエンコーディングの指定を書くことで、スクリプトエンコーディングを設定できます。

```ruby
# encoding: Shift_JIS
puts "こんにちは"  # Shift_JISで書かれた文字列を出力
```

指定がない場合、**デフォルトはUTF-8**が使われます。

### 2. 内部エンコーディング

内部エンコーディングとは、**Ruby内部で文字列をどのエンコーディングで扱うか**を示します。
ただし、Rubyはファイルを読み込む際、外部エンコーディングで解釈したデータをそのままのエンコーディングで保持します。**明示的に変換を指定しない限り、Rubyが自動で内部エンコーディングに変換することはありません**。

内部エンコーディングは、**文字列をRuby内部で統一的に処理するため**に存在します。ほとんどの場合、デフォルトでUTF-8が使用されますが、必要に応じて明示的に指定することもできます。

```ruby
File.open('example.txt', 'r:Shift_JIS:UTF-8') do |file|
  content = file.read
  puts content.encoding  # => #<Encoding:UTF-8>
end
```

この例では、Shift\_JISでエンコードされたファイルを読み込み、内部エンコーディングとしてUTF-8を指定しています。

### 3. 外部エンコーディング

外部エンコーディングは、**ファイルやIOオブジェクトを通じてデータを読み書きする際のエンコーディング**です。
例えば、ファイルを開くときや外部からデータを読み込むときに使われます。

次の例では、ファイルを開く際に**外部エンコーディングとしてShift\_JISを指定**しています。

```ruby
File.open('example.txt', 'r:Shift_JIS') do |file|
  content = file.read
  puts content.encoding  # => #<Encoding:Shift_JIS>
end
```

このように、外部エンコーディングはファイルやストリームの**元のエンコーディングをRubyに伝える**役割があります。

## エンコーディングの指定が重要な理由

エンコーディングを明確に指定する理由は、**正しくデータを解釈するため**です。データはただのバイト列として保存されているので、**そのバイト列をどのように解釈するかのルールが必要**です。

例えば、日本語の文字列を含むファイルがShift\_JISでエンコードされていたとします。その場合、ファイルを開くときにShift\_JISであることを指定しなければ、Rubyはそのバイト列を適切に解釈できず、文字化けを引き起こすことがあります。

```ruby
# Shift_JISで保存されたファイルをUTF-8の内部エンコーディングとして扱う
File.open('example.txt', 'r:Shift_JIS:UTF-8') do |file|
  content = file.read
  puts content.encoding  # => #<Encoding:UTF-8>
end
```

ここで `Shift_JIS` は、「このファイルのバイト列はShift\_JISでエンコードされている」という事実をRubyに教え、その上で**内部エンコーディングにUTF-8を使用して扱う**という役割を果たしています。

## 外部エンコーディングの指定理由と勘違いしやすいポイント

外部エンコーディングは、「ファイルをどのエンコーディングで読むか」を決めるために重要です。
ファイルの中身を正しく読み取るためには、そのファイルがどのようなエンコーディングで保存されているかをRubyに伝える必要があります。

たとえば、ファイルがShift_JISで保存されている場合、Rubyに対して「このファイルはShift_JISでエンコードされている」と伝える必要があります。これにより、Rubyはそのバイト列を正しく解釈し、内部エンコーディング（通常はUTF-8）に変換して処理できます。

- ファイルをUTF-8として読み込み、その内容をUTF-8として内部エンコーディングで扱う。
- ファイルのエンコーディングをShift\_JISとして指定することで、正しくバイト列を解釈し、内部ではUTF-8として扱う。

ここで勘違いしやすいのは、**外部エンコーディングは必ずしも出力専用の設定ではない**という点です。実際には、**ファイルの読み込み時にも適切にそのファイルの内容を解釈するために使用**されます。つまり、外部エンコーディングは読み込む際に「このファイルのデータをどう解釈すればいいか」という情報を提供しているのです。

そのため、ファイルを読み込む際にも外部エンコーディングを指定する必要があります。これにより、Rubyはファイル内のバイト列を正しく文字列として解釈し、内部で使用する文字コード（内部エンコーディング）に変換します。

## まとめ

Rubyでのエンコーディングにはスクリプトエンコーディング、内部エンコーディング、外部エンコーディングの3つがあり、それぞれが異なる目的を持っています。

- **スクリプトエンコーディング**：ソースコード自体のエンコーディングを指定
- **内部エンコーディング**：Ruby内で文字列をどのエンコーディングで扱うか
- **外部エンコーディング**：ファイルやIOからデータを読み書きする際のエンコーディング