📚

Go: Unicodeとrune型

2022/09/21に公開

なぜrune型があるのか?Unicodeとは?

Goの学習中にrune型が出てきて、よく分からなかったので調べてみました。
runes

パッケージルーンは、UTF-8でエンコードされたテキスト用の変換を提供します。

UTF-8

ISO/IEC 10646 (UCS) とUnicodeで使える8ビット符号単位(1〜4バイトの可変長)の文字符号化形式および文字符号化スキーム。

少し調べると主にUnicodeが関係しているようでしたので、まずはUnicodeから理解する必要があります。

Unicodeとは

Unicode

  • 全世界共通で使えるようにする定めた文字コードの業界規格。

  • 世界中の文字を収録し、通し番号を割り当て、同じコード体系で使えるようにしたもの。

    • 分かりやすく言うと、文字に数値を割り当てたものの集まり
  • "符号化文字集合"と呼ばれるものの1つ。

    • コンピュータ上で「どのような文字や記号を扱うのか」ということを定義したもの。
    • "変換対象となる文字の一覧"が書いてある表
  • 16ビットで文字を表す

    • Unicode一覧 0000-0FFF
    • 緑色の表の名前が<U+(表左上)+縦軸の上3桁+横軸の1桁>となる。
    • 例えばAだとしたら、U+0041となる。
      • 縦軸の4桁の内の下1桁は横軸の値になる。
        このU+0041という値は16進数で0041という値と対応付けられていることを意味する値である。
        この"0041"という値は16ビットであり、コードポイント(符合点)と呼ぶ。

UnicodeとUTF*

参考: Unicode -> UTF* への文字変換 image

文字列"あ"は、Unicodeで表記すると"U+3042"で、UTF-8で表記すると"E38182"ということになります。
つまり、"U+3042" == "E38182"ということです。

なぜrune型が必要なのか?

調べているとこちらの記事を見つけました。(こちらの記事が分かりやすすぎてこちらだけでも十分ということもなきにしもあらず)
試しにコードを打ってみます。

package main

import "fmt"

func main() {
	s := "あ"

	for i := 0; i < len(s); i++ {
		b := s[i] // byte
		fmt.Println(b)
	}

}

/* 実行結果 */
// 227
// 129
// 130

ここでなぜ3回出力されているのかという疑問が湧きました。(謎の数値も気になります)
試しに"あ"の長さを測ってみました-> len(s)
そうすると"3"と出力されました。
調べてみると日本語は1文字3バイトあるようです。

参考: UTF-8で日本語1文字が3バイトなのはなぜ?
227-> あ[0], 129-> あ[1], 130-> あ[2],
ということが分かりました。

記事を見てみると、このstringにindexでアクセスした時に得られるbyte値は、文字コードをUTF-8で1byteごとに区切った値ということが書いてあります。
"あ"のUFT-8での表現は、E3 81 82です。
この数値は16進数表記かつ2桁で1byteです。
これを踏まえて上のコードを16進数で出力してみます。

package main

import "fmt"

func main() {
	s := "あ"
	for i := 0; i < len(s); i++ {
    /* %xで16進数で出力する */
		fmt.Printf("% x", s[i]) // e3 81 82
	}

}

// "あ"のUTF-8の表現と一致する。

分かりづらいですが、UTF-8で表現された"あ"という文字を出力できたということです。
ここまでやって分かった事は、"あ"が複数byteで表現されていることもあり、"あ"という文字だったら3byte分まとめて読まないと正しく認識されないということです。
e3 81 82という3byte分は、e38182の3byte分は==U+3042です。

rune型にはUnicodeのコードポイントを表すことができます。
つまり、コードポイントを単位として扱うためにrune型を利用するのということになります。

package main

import "fmt"

func main() {
	s := "あ"
	/* ループの回数(=i)を棄却し、要素の中身(byte)だけ取り出す */
	for _, r := range s {
		// rune
		fmt.Println(r)
	}
}

// 12354

この時出力された"12354"という値は、Unicodeの番号を10進数に変換したものです。
変換ツールで確認します。

  1. "あ"と入力して、調べるボタンを押す
  2. Unicode文字番号 -> U+3042
  3. (HTML数値)文字参照(10進数表記) -> (&w12354)

つまり、"あ"という文字列を得るにはもう一段階必要です。
手段としては簡単で、string()を使うだけです。

package main

import "fmt"

func main() {
	s := "あ"
	/* ループの回数(=i)を棄却し、要素の中身(byte)だけ取り出す */
	for _, r := range s {
		// rune
		fmt.Println(string(r)) // string()を使用するだけ
	}
}

// あ

まとめ

参考にさせて頂いた記事に内容は集約してますが、実際自分で調べて試してみるとrune型の挙動や特徴、その周りの知識も学ぶことができました!!

Discussion