Go言語にはルーン(rune)という型があります。
Go言語を勉強し始めたとき、ルーンって何って感じで混乱したので、「ルーンとは何なのか?」を中心にGo言語のstring型、rune型について学んだ内容をまとめてみました。
一言で言えばルーンはUnicodeコードポイントのことなのですが、「文字コードとは?」、「Unicodeコードポイントとは?」ってところから整理していきます。
文字コード
文字コードとは「文字に割り当てられた数字」
コンピューターは0, 1しか理解できないので、文字を数字として変換して処理できるように文字に数字を割り当てています。
例えば、「あ → 00000001」というように各文字に数字が割り当てられている対応表があって、コンピューターはこの対応表を使って文字を数字に変換して処理しています。
そして、この対応表には「符号化文字集合」と「文字符号化方式」の2つがあって、コンピューターはこの2つの対応表を組み合わせて文字を処理しています。
- 符号化文字集合
- 「文字」と「文字に割り当てた番号」の対応表
- 例. Unicode
- 文字符号化方式
- 例. UTF-8, Shift-JIS
2つの表を組み合わせて↓な感じの対応表を作り「文字<=>数字」を変換しているイメージです。
青色で書いた文字符号化方式の部分が良く聞く「UTF-8」、「UTF-16」、「Shift-JIS」です。
「UTF-8」と「Shift-JIS」では、対応表が変わってくるのでShift-JISのデータをUTF-8で表示しようとすると文字化けしちゃうわけです。
ASCIIコード
ASCIIコードは「文字に割り当てた番号」と「コンピュータが扱う数字」が同じなので文字符号化方式を意識しなくて良いです。
繰り返しになりますが、この対応表を使って文字をコンピューターが理解できる数字に変換しているわけです。
Unicode
符号化文字集合。(「文字」と「文字に割り当てた番号」の対応表)
ASCIIコードだと英語以外の言語のデータが扱えないので、Unicodeができました。
Unicodeは、世界の全ての書記体系のすべての文字、アクセント、発音区別記号、タブなどの制御コード、絵文字に番号を振っています。
Unicodeが各文字に割り振っている番号のことをUnicodeコードポイントといいます。
そして、このUnicodeコードポイントのことをGo言語ではルーン(rune)と呼びます。
UTF-8
文字符号化方式。( )
言い換えると「Unicodeが各文字に割り当てた番号」と「 」の対応表がUTF-8です。
文字列は不変なバイト列。Goでは文字列はバイトのスライス。
-
Goのソースファイルは常にUTF-8でエンコードされ、Goの文字列はUTF-8として解釈される
-
len
関数は文字列中のバイト数を返す -
は文字列
str
- ASCIIではないコードポイントのUTF-8エンコーディングは2バイト以上になるので、文字列のi番目のバイトは必ずしもi番目の文字ではない
試しに、文字列の各バイトの値を見てみると、UnicodeとUTF-8の対応表と一致することを確認できます。
unicode table のUTF-8の項目をみると、hexが「E3 81 82」, dec(bytes)が「227 129 130」となっており、以下のコードの結果も同じになっています。
str := "あ" fmt.Printf("len: %d\n", len(str)) // => len: 3 for i := 0; i < len(str); i++ { fmt.Printf("%d: %d\n", i, str[i]) // 10進数で表示してみる // => 0: 227 // => 1: 129 // => 2: 130 } // 「% x」は16進数の2桁ごとの間にスペースを入れてくれる fmt.Printf("% x\n", str) // => e3 81 82
文字列リテラル
\
Usageなど改行を含む文字列を扱うときにバッククォートでの記法を使うとスッキリ書けます。
package main import ( "fmt" ) func main() { text := `こんちには、 私のブログを読んで頂きありがとうございます。 これからもよろしくおねがいします。` fmt.Println(text) }
ルーン
Unicodeコードポイントのことです。ルーン(rune)はint32のsynonym(別名)で、慣習的に値がUnicodeコードポイントであることを示すために使われています。
runeリテラルはシングルクォートで囲み、verbは%c
です。
rune型はint32型の別名なので、演算もできます。
r1 := 'あ' r2 := 'a' fmt.Printf("r1: %d, %c\n", r1, r1) // r1: 12354, あ fmt.Printf("r2: %d, %c\n", r2, r2) // r2: 97, a fmt.Println(r1 - r2) // 12257
ルーン数を知りたい場合は、unicode/utf8
str := "Hello, Go言語" fmt.Printf("len: %d\n", len(str)) // => len: 15 fmt.Printf("rune count: %d\n", utf8.RuneCountInString(str)) // => rune count: 11
ルーンごと(1文字ずつ)に処理をしたい場合には、rangeループが便利です。
str := "Hello, Go言語" for i, r := range str { fmt.Printf("%d\t%q\t%d\n", i, r, r) } // 出力結果 インデックス、文字、ルーン(unicodeコードポイント) 0 'H' 72 1 'e' 101 2 'l' 108 3 'l' 108 4 'o' 111 5 ',' 44 6 ' ' 32 7 'G' 71 8 'o' 111 9 '言' 35328 12 '語' 35486 // ASCIIではないルーンの場合インデックスが飛んでいる
rangeを使わないのであれば、次のようなforループを書くことになります。
str := "Hello, Go言語" for i := 0; i < len(str); { r, size := utf8.DecodeRuneInString(str[i:]) fmt.Printf("%d\t%c\t%d\n", i, r, r) // 単純に1ずつインクリメントしては、ルーンごとに処理できないので、バイトサイズを加算していく // i += size }
次の形式のコードポイントをダブルクォートで囲むだけです。
- 256未満のコードポイント`\xhh`
- 16ビット値 `\uhhhh`
- 32ビット値 `\Uhhhhhhhh`
※ hは16進数の1桁
実際にunicode tableのコードポイントを参考にいくつか出力してみます。
package main import "fmt" func main() { // A fmt.Println("\x41") fmt.Println("\u0041") fmt.Println("\U00000041") // あ fmt.Println("\u3042") fmt.Println("\U00003042") // emoji fmt.Println("\U0001F600") }
参考図書
Tour of Goを終えた人にオススメです!!