##前提知識
###Goでの文字の扱い
文字列にインデックスでアクセスすると、byteになる(byteはuint8のエイリアス)。
func main() {
a := "abc"
fmt.Printf("%T\n", a[0]
str := "abc"
for i := 0; i < len(str); i++ {
fmt.Printf("%T\n", str[i])
}
}
//出力結果
//uint8
//uint8
//uint8
//uint8
文字列をrangeでループすると、runeが得られる。int32はruneのエイリアス。
func main() {
a := "abc"
for _, s := range a {
fmt.Printf("%T\n", s)
}
}
//出力結果
//int32
//int32
//int32
出力されるのはASCII。
func main() {
str := "abc"
for i := 0; i < len(str); i++ {
fmt.Println(str[i])
}
for _, s := range str {
fmt.Println(s)
}
}
//97
//98
//99
//97
//98
//99
%xを使うことで、UTF-8でのbyte表現を出力できる。
func main() {
str := "abc"
for i := 0; i < len(str); i++ {
fmt.Printf("%x\n", str[i])
}
for _, s := range str {
fmt.Printf("%x\n", s)
}
}
//61
//62
//63
//61
//62
//63
###rune
コンピュータは、扱う文字によってはバイトを複数組み合わせて文字を表現する。しかし、文字の数を扱う際には、マルチバイト文字が存在する以上byteを単位として数えるのは都合が悪い。そこで、一つの文字が一つだけ持つコードポイントを利用する仕組みがGo言語には用意されている。それがruneである。
###文字コードとコードポイント
####文字コード
人間が使う文字も最後には0と1の並びとしてコンピュータには解釈されるが、人間の使う文字にはそれぞれに番号が割り振られており、その番号が文字コードである。文字コードはいわば実際の文字とコンピュータが解釈する2進数の中間に位置し両者を対応づける存在と言える。この文字コードは符号化方式という文字集合を構成する文字の表現方式によって違いが出る。この符号化方式は、GoではUTF-8が使用されている。
####コードポイント
この文字コードと紛らわしいのがコードポイントで、コードポイントは文字集合(Unicodeなど)に属する文字に順々に割り振った数値のこと。つまり、コードポイントは2進数と関連はなく、文字集合が同じなら各文字のコードポイントは符号化方式(UTF-8、UTF-16など)によって違いがなく共通である。コードポイントは文字集合の種類によって異なる。
符号化方式、文字集合あたりの知識が曖昧な人は以下のリンク先のページを読むことをオススメします。
http://equj65.net/tech/charcode/
###マルチバイト文字
- 1バイトで表せるのは10進数で0〜255まで。
- つまり、1バイトでは0〜127までに割り当てられているASCII文字と、128〜255までにおさまる文字(各文字コードで異なる)しかコンピュータ上で表せない。
しかしそれでは困るので、0〜255までに割り当てられている文字以外も表すために複数のバイトが使用される。そのように複数のバイトを使用して表される文字をマルチバイト文字と言う。
##マルチバイトの文字列操作例
マルチバイトの文字列の操作例。
###文字列を逆順にする場合
Goでマルチバイトの文字をそのまま逆順にしようとすると、マルチバイトの関係でうまく行かない。
func main() {
a := "あいうえお"
fmt.Println(badReverse(a))
}
func badReverse(s string) string {
output := ""
for i := len(s)-1; i >= 0; i-- {
output += string(s[i])
}
return output
}
// 出力結果
//ããããã
マルチバイトを扱うために、文字列をruneにする。rangeで文字列にアクセスすると、runeが得られるがrangeだと昇順になるので、この場合は使えない。
func main() {
a := "あいうえお"
fmt.Println(reverse(a))
}
func reverse(s string) string {
var output bytes.Buffer
runes := []rune(s)
for i := len(runes)-1; i >= 0; i-- {
output.WriteRune(runes[i])
}
return output.String()
}
//出力結果
//おえういあ
###文字列の文字の数を数える場合
マルチバイト文字があると、lenで文字列を数えてもバイト数を数えることになってしまうので期待通りの結果が得られない。runeのスライスに変換して長さをlen()で出すか、unicode/utf-8にあるRuneCountInStringを使う。
func main() {
str := "Hello, 世界"
fmt.Printf("Byte: %d \n", len(str))
fmt.Printf("Rune: %d \n", utf8.RuneCountInString(str))
fmt.Printf("Rune: %d \n", len([]rune(str)))
}
###文字列の一部を切り出す場合
ここでも同様に、マルチバイトであることを考慮してruneに変換してから切り出す。
func main() {
str := "あいうえお"
fmt.Println(str[0:3])
r := []rune(str)
fmt.Println(string(r[0:3]))
}
//あ
//あいう