Go: Unicodeとrune型
なぜrune型があるのか?Unicodeとは?
Goの学習中にrune型が出てきて、よく分からなかったので調べてみました。
runes
パッケージルーンは、UTF-8でエンコードされたテキスト用の変換を提供します。
ISO/IEC 10646 (UCS) とUnicodeで使える8ビット符号単位(1〜4バイトの可変長)の文字符号化形式および文字符号化スキーム。
少し調べると主にUnicodeが関係しているようでしたので、まずはUnicodeから理解する必要があります。
Unicodeとは
-
全世界共通で使えるようにする定めた文字コードの業界規格。
-
世界中の文字を収録し、通し番号を割り当て、同じコード体系で使えるようにしたもの。
- 分かりやすく言うと、文字に数値を割り当てたものの集まり
-
"符号化文字集合"と呼ばれるものの1つ。
- コンピュータ上で「どのような文字や記号を扱うのか」ということを定義したもの。
- "変換対象となる文字の一覧"が書いてある表
-
16ビットで文字を表す
- Unicode一覧 0000-0FFF
- 緑色の表の名前が<U+(表左上)+縦軸の上3桁+横軸の1桁>となる。
- 例えばAだとしたら、U+0041となる。
- 縦軸の4桁の内の下1桁は横軸の値になる。
このU+0041という値は16進数で0041という値と対応付けられていることを意味する値である。
この"0041"という値は16ビットであり、コードポイント(符合点)と呼ぶ。
- 縦軸の4桁の内の下1桁は横軸の値になる。
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進数に変換したものです。
変換ツールで確認します。
- "あ"と入力して、調べるボタンを押す
- Unicode文字番号 -> U+3042
- (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