【Goで学ぶコンピューターサイエンス】進数とエンコーディング

## 進数

進数とは、数を表現する方法です。
日常的に使っている「10進数」もその一つですが、コンピュータでは特に以下の進数がよく使われます。

### 2進数（Binary）
- 基数：2
- 使用する数字：0, 1
- コンピュータの最も基本的な数値表現。ビット（0または1）を用いる

### 8進数（Octal）
- 基数：8
- 使用する数字：0〜7
- Linuxのパーミッション管理で使われている

### 16進数（Hexadecimal）
- 基数：16
- 使用する数字：0〜9、A〜F（10進数の10〜15を表す）
- 大量の2進数を短く表現するために便利。メモリのアドレス指定やカラーコードで使われている

## エンコーディング

エンコーディングとは、データを特定のルールに基づいて符号化することです。

### 文字エンコーディング
- **ASCII**:
  - 7ビットで英数字や記号を表現
  - 例：「A」は10進数で65、2進数で01000001
- **UTF-8**:
  - 可変長のエンコーディング
  - ASCIIと互換性があり、世界中の文字を表現可能

### データエンコーディング
- **Base64**:
  - バイナリデータをテキスト形式に変換
  - メールやURLでバイナリデータを扱う際に便利
- **URLエンコーディング**:
  - 特殊文字を % と16進数で表現します。Webで安全にデータを転送するために使用


## 進数のGoサンプルコード

### **10進数から他の進数への変換**

```go
package main

import "fmt"

func main() {
    decimal := 255

    fmt.Printf("Decimal: %d\n", decimal)
    fmt.Printf("Binary: %b\n", decimal)   // 2進数
    fmt.Printf("Octal: %o\n", decimal)    // 8進数
    fmt.Printf("Hexadecimal: %X\n", decimal) // 16進数
}
```

```
Decimal: 255
Binary: 11111111
Octal: 377
Hexadecimal: FF
```

### 進数を10進数に変換

```go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	binary := "11111111" // 2進数
	octal := "377"       // 8進数
	hex := "FF"          // 16進数

	// 2進数→10進数
	decimalBinary, _ := strconv.ParseInt(binary, 2, 64)
	fmt.Printf("Binary to Decimal: %d\n", decimalBinary)

	// 8進数→10進数
	decimalOctal, _ := strconv.ParseInt(octal, 8, 64)
	fmt.Printf("Octal to Decimal: %d\n", decimalOctal)

	// 16進数→10進数
	decimalHex, _ := strconv.ParseInt(hex, 16, 64)
	fmt.Printf("Hexadecimal to Decimal: %d\n", decimalHex)
}
```

```
Binary to Decimal: 255
Octal to Decimal: 255
Hexadecimal to Decimal: 255
```

## エンコーディングのGoサンプルコード

### 文字列をバイトに変換（UTF-8）

```go
package main

import "fmt"

func main() {
	text := "こんにちは🌏" // 日本語と地球の絵文字を含む文字列
	bytes := []byte(text)

	fmt.Printf("Text: %s\n", text)
	fmt.Printf("Bytes: %v\n", bytes)

	fmt.Println("\nDetails of each byte (UTF-8 encoding):")
	// 各バイトを2進数で表示
	for i, b := range bytes {
		fmt.Printf("Byte %d: %08b\n", i, b)
	}

	fmt.Println("\nCharacter-wise breakdown:")
	// 各文字のUTF-8エンコーディングを表示
	for i, r := range text {
		fmt.Printf("Character %d: '%c' (code point: %U)\n", i, r, r)
	}
}
```

UTF-8だと、ひらがなは3バイト、絵文字は4バイトなのが確認できる
```
Text: こんにちは🌏
Bytes: [227 129 147 227 130 147 227 129 171 227 129 161 227 129 175 240 159 140 143]

Details of each byte:
Byte 0: 11100011
Byte 1: 10000001
Byte 2: 10010011
Byte 3: 11100011
Byte 4: 10000010
Byte 5: 10010011
Byte 6: 11100011
Byte 7: 10000001
Byte 8: 10101011
Byte 9: 11100011
Byte 10: 10000001
Byte 11: 10100001
Byte 12: 11100011
Byte 13: 10000001
Byte 14: 10101111
Byte 15: 11110000
Byte 16: 10011111
Byte 17: 10001100
Byte 18: 10001111

Character-wise breakdown:
Character 0: 'こ' (code point: U+3053)
Character 3: 'ん' (code point: U+3093)
Character 6: 'に' (code point: U+306B)
Character 9: 'ち' (code point: U+3061)
Character 12: 'は' (code point: U+306F)
Character 15: '🌏' (code point: U+1F30F)
```

### Base64エンコーディング

```go
package main

import (
	"encoding/base64"
	"fmt"
	"os"
)

func main() {
	// 画像を読み込む
	filePath := "example.png"
	binaryData, err := os.ReadFile(filePath)
	if err != nil {
		fmt.Println("ファイルの読み込みに失敗しました:", err)
		return
	}

	fmt.Println("元のバイナリデータの長さ:", len(binaryData), "バイト")

	// Base64エンコード
	base64Encoded := base64.StdEncoding.EncodeToString(binaryData)
	fmt.Println("Base64エンコード後のデータ:")
	fmt.Println(base64Encoded[:100], "続く...") // データの一部を表示

	// Base64デコード
	decodedData, err := base64.StdEncoding.DecodeString(base64Encoded)
	if err != nil {
		fmt.Println("Base64デコードに失敗しました:", err)
		return
	}

	fmt.Println("デコード後のバイナリデータの長さ:", len(decodedData), "バイト")

	// デコードしたデータを新しいファイルとして保存
	outputPath := "decoded_example.png"
	err = os.WriteFile(outputPath, decodedData, 0644)
	if err != nil {
		fmt.Println("デコードしたデータの書き込みに失敗しました:", err)
		return
	}

	fmt.Println("デコードしたデータをファイルとして保存しました:", outputPath)
}
```

```
元のバイナリデータの長さ: 46710 バイト
Base64エンコード後のデータ:
iVBORw0KGgoAAAANSUhEUgAAAZAAAAH0CAMAAAAkBIuyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhp 続く...
デコード後のバイナリデータの長さ: 46710 バイト
デコードしたデータをファイルとして保存しました: decoded_example.png
```

### URLエンコーディング

```go
package main

import (
	"fmt"
	"net/url"
)

func main() {
	// サンプルデータ：エンコードする文字列
	rawString := "こんにちは! 😊 & Go=楽しい"

	// URLエンコーディング
	encoded := url.QueryEscape(rawString)
	fmt.Println("エンコード結果:", encoded)

	// エンコードされた文字列をデコード
	decoded, err := url.QueryUnescape(encoded)
	if err != nil {
		fmt.Println("デコードエラー:", err)
		return
	}
	fmt.Println("デコード結果:", decoded)

	// URLにクエリパラメータとして追加
	baseURL := "https://example.com/search"
	params := url.Values{}
	params.Add("query", rawString)
	fullURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
	fmt.Println("クエリ付きURL:", fullURL)

	// クエリパラメータを解析
	parsedURL, err := url.Parse(fullURL)
	if err != nil {
		fmt.Println("URL解析エラー:", err)
		return
	}
	queryParams := parsedURL.Query()
	fmt.Println("解析されたクエリパラメータ:", queryParams.Get("query"))
}
```

```
エンコード結果: %E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%21+%F0%9F%98%8A+%26+Go%3D%E6%A5%BD%E3%81%97%E3%81%84
デコード結果: こんにちは! 😊 & Go=楽しい
クエリ付きURL: https://example.com/search?query=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%21+%F0%9F%98%8A+%26+Go%3D%E6%A5%BD%E3%81%97%E3%81%84
解析されたクエリパラメータ: こんにちは! 😊 & Go=楽しい
```