文字コードの理解

## はじめに
UnicodeとUTF-8等の理解を深めたいと思います。
バイナリコードを通し理解します。
わかっていたら当たり前のことを記載しているだけにすぎない内容です。

方法としてDBからのデータを確認することにします。
DBはSQL Serverを使用しています。


## 準備例
以下例を記載します。
### **従業員テーブルのCREATE例**
```sql
CREATE TABLE 従業員 (
    従業員ID INT IDENTITY(1,1) PRIMARY KEY, -- 自動採番
    部門ID INT NOT NULL,                   -- 部門IDは必須
    姓 NVARCHAR(50) NOT NULL,             -- 姓（例: 田中）は必須
    名 NVARCHAR(50) NOT NULL,             -- 名（例: 太郎）は必須
    性別 NVARCHAR(10) NOT NULL,           -- 性別（例: 男/女）は必須
    入社日 DATE NOT NULL,                 -- 入社日は必須
    基本給 INT NOT NULL                   -- 基本給は必須
);
```
1. **`従業員ID` 列**
   - `IDENTITY(1,1)` により、自動採番される設定。
   - 挿入時に `従業員ID` を明示的に指定しないことで、自動的に値が割り当てられます。

2. **`NOT NULL` 制約**
   - `NULL` を許可しない列 (`部門ID`, `姓`, `名`, `性別`, `入社日`, `基本給`) では、値を必ず指定する必要があります。

3. **`PRIMARY KEY` 制約**
   - `従業員ID` は一意でなければならず、重複する値を登録することはできません。

### **INSERT 文の例**
```sql
INSERT INTO 従業員 (部門ID, 姓, 名, 性別, 入社日, 基本給)
VALUES (1, '山田', '太郎', '男', '2025-01-01', 300000);
```

- `従業員ID` は自動採番されるため、`INSERT` 文で指定する必要はありません。

## 格納データ検証

### 方法 1: **Unicodeのコードポイントを取得**
SQL Serverでは、`UNICODE`関数を使用して、指定した文字列のUnicodeコードポイント（文字コード）を取得できます。

#### 実行例
「姓」列の1文字目のUnicodeコードポイントを取得する場合：
```sql
SELECT [姓], UNICODE([姓]) AS UnicodeCodePoint
FROM [SampleDB].[dbo].[従業員];
```

| 姓   | UnicodeCodePoint |
|------|------------------|
| 山田 | 23665            |

https://0g0.org/unicode/5C71/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/739a5291-fd94-7313-62a2-448a1d0d36d3.png)



---

### **Unicode コードポイントの確認**
- **「山田」**
  - 「山」の Unicode コードポイントは `23665` (16進数では `5C71`) です。


### **`UNICODE()` 関数の動作**
- SQL Server の `UNICODE()` 関数は、**指定された文字列の最初の文字の Unicode コードポイント**を返します。
- この場合、`[姓]` の値が「山田」のとき、最初の文字「山」の Unicode コードポイント `23665` が返されています。


### **結果の確認**
Unicode コードポイントが正しいかどうかを確認するには、次のように手動で検証できます：

#### **1. Unicode コードポイントを確認**
「山」の Unicode コードポイントを確認すると、次のようになります：
- **文字「山」**
  - 16進数：`U+5C71`
  - 10進数：`23665` （16進数を10進数に変換した値）

#### **2. 変換例**
- 16進数 `5C71` を10進数に変換：
  - \( 5 × 16^3 + C(12) × 16^2 + 7 × 16^1 + 1 × 16^0 = 23665 )
  - これが一致するため正しい結果といえます。


## 方法 2: **バイナリ表現**

#### 実行例
「姓」列のバイナリデータを確認する場合：
```sql
SELECT [姓], CONVERT(VARBINARY(MAX), [姓]) AS BinaryData
FROM [SampleDB].[dbo].[従業員];
```

| 姓   | BinaryData |
|------|------------------|
| 山田 | 0x715C3075       |


### VARBINARY(MAX) 型は
`VARBINARY(MAX)` 型は、単に **バイナリデータを格納する型** であり、エンコーディング形式（UTF-8、UTF-16、リトルエンディアン、ビッグエンディアンなど）を特定する情報を含んでいません。そのため、**格納されたデータを検証しないと、どのエンコーディング形式が使用されているかはわかりません**。

1. **データの中立性**
   - `VARBINARY(MAX)` は、バイナリデータをそのまま保存するためのデータ型です。
   - 格納されたバイト列がどの文字コードやエンコーディング形式を使用しているかに関して、SQL Server はその情報を保持しません。

2. **エンコーディングは入力データ次第**
   - 格納されるデータが UTF-16 であれば UTF-16 のデータとして保存され、UTF-8 であれば UTF-8 のまま保存されます。
   - ただし、データを操作する際には、格納されているデータのエンコーディング形式を適切に解釈する必要があります。

3. **SQL Server の動作**
   - `CONVERT(VARBINARY(MAX), [列名])` を使用して文字列をバイナリデータに変換した場合、SQL Server 内部ではデフォルトで **UTF-16（リトルエンディアン）** を使用します。
   - そのため、SQL Server が直接生成したデータは UTF-16 リトルエンディアン形式であることが多いです。

### **検証**
`VARBINARY(MAX)` に格納されるデータがどのエンコーディング形式を使用しているかを確認するには、格納されたバイナリデータを検証する必要があります。


`SELECT [姓], CONVERT(VARBINARY(MAX), [姓])` の結果が `0x715C3075` となっている場合の意味を解析します。

- `0x715C3075` は、文字列 `山田` をデータベース内のエンコーディング（おそらく UTF-16）でエンコードしたバイナリ表現です。

#### **文字ごとのバイナリ解析**
UTF-16（リトルエンディアン形式）でエンコードされていると仮定します：
1. **「山」**
   - Unicode コードポイントは `U+5C71`（16進数）。
   - UTF-16 リトルエンディアンでは、低位バイトから並び替えられるため：
     - `0x5C71` → `71 5C`

2. **「田」**
   - Unicode コードポイントは `U+7530`（16進数）。
   - 同じくリトルエンディアンで：
     - `0x7530` → `30 75`

https://0g0.org/unicode/7530/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/83497ecf-9e9f-6715-53be-07a164e4ce9a.png)


#### **結果**
これらを連結すると：
- `山田` の UTF-16 バイナリ表現は `71 5C 30 75` となり、結果として `0x715C3075` になります。


`0x715C3075` は、`山田` を UTF-16 リトルエンディアン形式で表した正しいバイナリ表現です。

## Unicode、エンコーディング結果(UTF-8等)の関係
バイナリデータは、Unicode コードポイントを **実際に記録・送信・処理するための形式** に変換したものです。

バイナリデータは、Unicodeの表現をさらに低レイヤの **実際のデータ表現** に落とし込んだものです。ただし、Unicodeそのものは **文字コード体系** であり、実際のデータ（バイナリ）として表現されるには、エンコーディング形式が必要になります。


### **Unicode とバイナリデータの関係**
1. **Unicode とは**
   - Unicode は、すべての文字や記号に一意のコードポイントを割り当てる文字コード体系です。
   - 例:
     - 「田」の Unicode コードポイントは `U+7530`（16進数）。
     - これは抽象的な概念としての文字コードを定義したものです。
     

2. **エンコーディング（符号化）**
   - Unicode コードポイントを実際のバイナリデータに変換するために、エンコーディング形式が使用されます。
   - 代表的な Unicode エンコーディング形式：
     - **UTF-8**: 可変長エンコーディング。1～4バイトで表現されます。
     - **UTF-16**: 2バイトまたは4バイトで表現されます（リトルエンディアンまたはビッグエンディアンの違いあり）。
     - **UTF-32**: 固定長エンコーディングで、常に4バイト。

3. **バイナリデータ**
   - エンコーディングによって変換された結果がバイナリデータです。
   - 例:
     - 「田」を UTF-16 リトルエンディアンでエンコードすると、バイナリデータは `0x3075` になります。
     - 同じ「田」を UTF-8 でエンコードすると、バイナリデータは `0xE7 0x94 0xB0` になります。


### **Unicode とバイナリデータの階層**
次のような階層構造で理解できます：

1. **抽象的な文字**
   - 文字そのものの概念（例: 「田」や「A」）。
2. **Unicode コードポイント**
   - 各文字に一意に割り当てられた番号（例: 「田」＝`U+7530`）。
3. **エンコーディング（符号化）**
   - コードポイントをバイト列に変換する方法。
4. **バイナリデータ**
   - 実際にメモリやファイルに保存されるデータ（例: `0x3075`）。