String Typeを見てみましょう。Stringは複雑なものになりがちですが、_簡単な部分_を見ていきましょう。Goが目指しているのものを覚えているでしょうか。効率的なコンパイル、効率的な実行、プログラミングを簡単にすること、です。
文字列型の変数を作ってみましょう。文字列はダブルクォート"
やバッククォート"`"を用いて作ることができます。バッククォートで作られる文字列はraw string literalです。なので、改行やスペースなどが全て含まれます。
package main
import (
"fmt"
)
var string_literal string
func main() {
s := "Hello, playground"
string_literal = `"Hello,
playground"`
fmt.Println(s)
fmt.Printf("%T\n", s)
fmt.Println(string_literal)
fmt.Printf("%T\n", string_literal)
}
Go Specでは文字列型について以下のようにしています。
文字列型は文字列の値の集合です。文字列の値とは(空文字を含む)データ(byte)が順番に並んでいるものです。文字列は変更することができません。一度作られれば、その文字列の内容を変更することは不可能です。定義済みのための文字列のための型はstringです。
そう、_データ(byte)が順番に並んでいるもの_です。byte
は型で、int8
のエイリアスです。文字列型をbyte型の並びに_型変換_してみましょう。bs := []byte(s)
package main
import (
"fmt"
)
var string_literal string
func main() {
s := "Hello, playground"
fmt.Println(s)
fmt.Printf("%T\n", s)
bs := []byte(s)
fmt.Println(bs)
fmt.Printf("%T\n", bs)
}
こんな出力結果が出たでしょうか。[72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 110 100]
ASCIIコーディングスキーマを見てみると、72
は"H"
、101
, 108
, 108
, 111
はそれぞれ"e"
, "l"
, "l"
, "o"
となっています。これは先程のコード中の文字列を表しています。アルファベットの文字を表現するためにコーディングスキーマを使っています。ここでは、10進数の数字で文字の並びを表しています。プログラミングでは16進数もよく使います。
UTF-8では、1から4バイトでコードポイントを作り、それぞれのコードポイントがそれぞれ1つの文字を表しています。
72
が"H"
と関連付けられているのを見ましたが、ASCIIコーディングスキーマでは72
は2進数の100 1000
と結びついています。これは16進数でいう48
です。
16進数、UTF-8についてもう少し詳しく見ていきましょう。fmt
package documentationの%x
周辺はこうなっています。
%s 処理されていない文字列またはスライス
%q ダブルクォートで囲まれたGoの文法的に安全にエスケープされた文字列
%x 1バイトにつき2つも文字の小文字の16進数
%X 1バイトにつき2つも文字の大文字の16進数
もう少し下の"Other flags:"セクションは以下のようになっています。
+ 数値型の記号を常に出力する
%q (%+q)の際にASCII文字のみの出力になるようにする
- スペースで左詰めする
# 代替フォーマット: 8進数の場合に0を追加 (%#o), 16進数の場合に0xを追加 (%#x);
16進数のために0Xを追加 (%#X); %pのために0xを抑制 (%#p);
%qために、strconv.CanBackquoteがtrueの場合にバッククォートで囲まれた生文字列を出力
%e, %E, %f, %F, %g, %Gのために10進数ポイントを常に出力
%gと%Gの際に末尾の0を削除しない
%U (%#U)の際に文字列が出力可能なら U+00078 'x'のように出力する
' ' (スペース) スペースを残す (% d);
出力される文字列や16進数ごとにスペースを追加する。(% x, % X)
0 0埋めする
数値の際には記号の後に0を移動する
%U (%#U)の際に文字列が出力可能なら U+00078 'x'のようにする
の部分にならってUTF-8の文字とともに%#U
を使ってみましょう。
package main
import (
"fmt"
)
var string_literal string
func main() {
s := "Hello, playground"
fmt.Println(s)
fmt.Printf("%T\n", s)
bs := []byte(s)
fmt.Println(bs)
fmt.Printf("%T\n", bs)
for i := 0; i < len(s); i++ {
fmt.Printf("%#U ", s[i])
}
}
s
の内容のindexや16進数での値を出力してみます。
package main
import (
"fmt"
)
var string_literal string
func main() {
s := "Hello, playground"
fmt.Println(s)
fmt.Printf("%T\n", s)
bs := []byte(s)
/* 文字列sの文字をそれぞれ10進数で */
fmt.Println(bs)
fmt.Printf("%T\n", bs)
/* 文字列sの文字をそれぞれUTF-8のコードポイントで */
for i := 0; i < len(s); i++ {
fmt.Printf("%#U ", s[i])
}
fmt.Println("")
/* 文字列sのそれぞれの文字を16進数で */
for i, v := range s {
fmt.Printf("At index position %d we have hex %#x\n", i, v)
}
}
ASCIIのコード表を使って出力が正しいか見てみましょう。
Binary | Hex | Glyph |
---|---|---|
100 1000 | 48 | H |
110 0101 | 65 | e |
110 1100 | 6C | l |
110 1100 | 6C | l |
110 1111 | 6F | o |
010 1100 | 2C | , |
010 0000 | 20 | space |
111 0000 | 70 | p |
110 1100 | 6C | l |
110 0001 | 61 | a |
111 1001 | 79 | y |
110 0111 | 67 | g |
111 0010 | 72 | r |
110 1111 | 6F | o |
111 0101 | 75 | u |
110 1110 | 6E | n |
110 0100 | 64 | d |
"Hello, playground"
のそれぞれの文字は以下のようになります。
10進数の場合
[72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 110 100]
UTF-8コードポイントの場合
U+0048 'H' U+0065 'e' U+006C 'l' U+006C 'l' U+006F 'o' U+002C ',' U+0020 ' ' U+0070 'p' U+006C 'l' U+0061 'a' U+0079 'y' U+0067 'g' U+0072 'r' U+006F 'o' U+0075 'u' U+006E 'n' U+0064 'd'
16進数の場合
0x48 0x65 0x6c 0x6c 0x6f 0x2c 0x20 0x70 0x6c 0x61 0x79 0x67 0x72 0x6f 0x75 0x6e 0x64
それぞれのコードポイントはint32
のエイリアスであるrune
としても表せます。rune
のコードポイントはUTF-8です。
おさらいです。文字列はダブルクォートやバッククォートによって囲むことで作ることができます。UTF-8でGoのコードを書いても文字列がUTF-8コードポイントになるわけではありません。
文字列はデータの並びです。文字列は変更することはできませんが、新しい値を割り当てることはできます。
package main
import (
"fmt"
)
var string_literal string
func main() {
s := "Hello, playground"
s = "Hello, Hawaii"
fmt.Println(s)
}
このように、string
のデータを変えることはできなくても、新しい値を割り当てることはできます。string
を構成するデータは変換したり、覗くこともできます。ASCIIとUTF-8コーディングスキーマとの変換もできました。
Golangのウェブサイトでは、アジア圏の文字を例に使っています。このセクションで覚えたことを使ってこれらの文字は_1バイト以上であること_を確認してみてください。
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}