string型の変数をfor文で回す
競プロで文字列を1文字ずつ扱う場面があって、改めてまとめてみる。
byte型とは
まずbyte
型について。
// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is // used, by convention, to distinguish byte values from 8-bit unsigned // integer values. type byte = uint
そもそもバイト=8ビットで、10進数だと0~255(28-1)までを表現できる。Goではbyte
型は8ビットの符号なし整数のエイリアスになっており、整数として演算できる。
文字列をfor文で回す
文字列にインデックスでアクセスするとbyte
型が取得でき、文字列をfor range
で回すとrangeの第2返り値ではrune
型が取得できる。
s := "string" for i, r := range s { fmt.Println(s[i]) // byte fmt.Println(r) // rune // 出力はどちらも 115 116 114 105 110 103 fmt.Println(string(s[i])) // string fmt.Println(string(r)) // string // 出力はどちらも s t r i n g }
s := "string" for i := 0; i < len(s); i++ { fmt.Println(s[i]) // 115 116 114 105 110 103 fmt.Println(string(s[i])) // s t r i n g }
このように普段アルファベットをfor
文で回す際は、特に何も注意することなく、1文字ずつ処理することができる。
Goでは符号化方式としてUTF-8
を採用している。UTF-8
はASCII
互換の符号化方式で、ASCII
で定義されているアルファベットなどは1バイトで表現される。
そのため1バイト(Goのbyte
型で0~255)で表現できるASCII文字は問題なく扱えるが、文字によっては1バイトで表現できないため、1バイトごとに出力しても意図する値を出力できない可能性がある。
例えば以下のように、日本語の「あ」をbyte
型で出力すると、以下のように計3バイトで出力される。
ちなみに組み込み関数のlen()
は対象文字列のバイトサイズを取得する。
s := "あ" for i := 0; i < len(s); i++ { fmt.Println(s[i]) // 227 129 130 fmt.Println(string(s[i])) // ã ? ? }
よってGoでは1バイトで表現できない文字列でも1文字ずつ扱うために、コードポイント単位で文字を扱うrune
が用意されている。
rune
型は対象の文字列のコードポイント(16進数)を10進数に変換したものが割り当てられる。
// rune is an alias for int32 and is equivalent to int32 in all ways. It is // used, by convention, to distinguish character values from integer values. type rune = int32
コードポイントは最大で21ビットの数値(16進数で5~6桁)なので、rune
型は32ビットの整数のエイリアスになっており、整数として演算できる。
// runeは文字をシングルクオートで囲って書く // 実体はint32なので計算もできる fmt.Println('あ') // 12354 fmt.Println('あ' * 2) // 24708 fmt.Println('い' - 'あ') // 2
rune
型を利用することで、日本語のような1バイト以上で表される文字列でも1文字ずつ(コードポイント単位で)扱うことができる。
例えば「あ」をrune
型で出力する場合、「あ」のコードポイントは「U+3042」なので、これを10進数に変換した「12354」が出力される。16進数で出力したい時はPrintf
で%x
を使う。
s := "あ" for _, r := range s { fmt.Println(r) // 12354 fmt.Printf("%x", r) // 3042 }
rune
型をstring
型に直すにはstring()
を使う。
s := "あ" for _, r := range s { fmt.Println(string(r)) // あ }
スライスを使うとこんな感じ。
s := "あ" // コードポイントのスライス fmt.Println([]rune(s)) // [12354] // UTF-8で符号化したバイトスライス fmt.Println([]byte(s)) // [227 129 130]
ちなみにslicing
するとbyte
では返ってこないので注意。
s := "ABCD" fmt.Println(s[2:3]) // C fmt.Println(s[2]) // 67