帰ってきた「しっぽのさきっちょ」

String と Rune — プログラミング言語 Go

(初出: はじめての Go 言語 (on Windows) その4 - Qiita

文字列を示す string は不変(immutable)なオブジェクトだが,中身は byte 配列である。 したがって以下のように

package main

import "fmt"

func main() {
	nihongo := "日本語"
	size := len(nihongo)

	fmt.Printf("nihongo = %d bytes :", size)
	for i := 0; i < size; i++ {
		fmt.Printf(" %x", nihongo[i])
	}
	fmt.Print("\n")
}

string をダンプすると以下の結果になる1

C:>go run string01.go
nihongo = 9 bytes : e6 97 a5 e6 9c ac e8 aa 9e

string なんて名前なのに)文字単位で情報を保持しているわけではないため,最初の2文字を取り出すつもりでうっかり

package main

import "fmt"

func main() {
	nihongo := "日本語"

	fmt.Printf("nihongo = %s\n", nihongo)
	fmt.Printf("nippon = %s\n", nihongo[:2])
}

なんてなコードを書くと以下の結果になる。

C:>go run string02.go
nihongo = 日本語
nippon = ��

文字列を文字単位で扱うには rune を使う。 いや,ルーンってどんだけ厨二… ゲフンゲフン。

package main

import "fmt"

func main() {
	nihongo := "日本語"
	nihongoRune := []rune(nihongo)
	size := len(nihongoRune)

	fmt.Printf("nihongo = %d characters : ", size)
	for i := 0; i < size; i++ {
		fmt.Printf("%#U ", nihongoRune[i])
	}
	fmt.Printf("\n")
}
C:>go run string03.go
nihongo = 3 characters : U+65E5 '日' U+672C '本' U+8A9E '語'

または, string に対して for range 構文を使ってループを回すと文字(rune)単位で取得できる。

package main

import "fmt"

func main() {
	nihongo := "日本語"

	for pos, runeValue := range nihongo {
		fmt.Printf("%#U starts at byte position %d\n", runeValue, pos)
	}
}
C:>go run string03b.go
U+65E5 '日' starts at byte position 0
U+672C '本' starts at byte position 3
U+8A9E '語' starts at byte position 6

rune の実体は int32 で,内部表現は Unicode の符号点(code point)になっている。 stringrune 配列は相互変換できるので,文字列を切り取る場合は

package main

import "fmt"

func main() {
	nihongo := "日本語"

	fmt.Printf("nihongo = %s\n", nihongo)
	fmt.Printf("nippon = %s\n", string([]rune(nihongo)[:2]))
}

のように string → []runestring と変換していけば安全に処理できる。

C:>go run string02b.go
nihongo = 日本語
nippon = 日本

もう少し細かい処理が必要なら unicode/utf8 パッケージを使う手もある2

ブックマーク

Go 言語に関するブックマーク集はこちら


  1. ちなみに Go 言語で取り扱う文字列の文字エンコーディングは UTF-8 が既定である。ソースコードがそもそも UTF-8 を要求しているし(つまりリテラルの文字列はかならず UTF-8 になる), stringrune の相互変換も文字列が UTF-8 であることを前提にしている。しかし,実際には string の実体はただの byte 配列であり,中身が UTF-8 文字列であることを保証しているわけではない。通常は,未知の文字列についてはいったん byte 配列に格納しておいて,何らかの方法で UTF-8 に変換してから string にキャストするのが安全である(または各文字エンコーディング用の独自 type を作るか)。なお,文字エンコーディングの変換については別の記事で改めて紹介する。 [return]
  2. ちなみに strings パッケージは内部で unicode/utf8 パッケージを使っているようだ。 [return]