Go言語(golang) 文字列/string
文字列型(string)の基本的な使い方とbyteとruneについて
目次
- 文字列(string型)
- 文字数の取得
- 文字列から一文字取得
- 文字列の切り取り
- 連結演算子(文字列の連結)
- ヒアドキュメント
- シングルバイト文字(byte)とマルチバイト文字(rune)
- 文字列に関する便利な標準パッケージ
文字列(string型)
文字列は ""
ダブルクオートで囲む
package main func main() { s := "hello world" println(s) // hello world }
エスケープシーケンス
\" ダブルクオート \n 改行(line feed) \r 復帰(carriage return) \t 水平タブ \v 垂直タブ
文字列は ""
で囲むので "
を文字列に含めることはできません。
なので \
(バックスラッシュ) に続けて "
を記述することでそれを使うことができます。
その他にも \n
で改行、 \t
で水平タブなどを表現します。
package main func main() { var s string // "" を出力する s = "\"hello\"" println(s) // "hello" // 改行 s = "hello\nworld" println(s) // hello // world // 水平タブ s = "\thello" println(s) // hello }
文字数の取得
package main func main() { s := "hello world" length := len(s) println(length) // 11 }
文字列から一文字取得
文字列(string型)は、バイトスライス([]byte)としてスライスと同様にインデックス番号(添え字)で文字(byte)が取得できる
package main func main() { s := "hello world" b := s[0] println(string(b)) // h }
文字列の切り取り
これもスライスと同じで [start:end]
で抜き出すことができる。
package main func main() { s := "hello world" b := s[0:5] println(string(b)) // hello }
連結演算子(文字列の連結)
+
を使って文字列を連結できる
package main func main() { a := "Hello" + "World" println(a) // HelloWorld // 変数でも同じ b := "Hello" c := "World" d := b + c println(d) // HelloWorld // 片方が変数でも同じ e := "Hello" f := e + "World" println(f) // HelloWorld g := "Hello" // += で g = g + "World" と同じ意味になる g += "World" println(g) // HelloWorld }
ヒアドキュメント
""
の代わりに `` (バッククォート)で囲むことで、そのまま複数行の文字列を書くことができます。
エスケープシーケンスはそのまま表示されるので、以下のように \n\r\t\v
がそのまま出力されます。
package main func main() { s := `foo\n\r\t\v bar baz` println(s) // foo\n\r\t\v // bar // baz }
シングルバイト文字(byte)とマルチバイト文字(rune)
byte (uint8 の別名)。8ビット = 1バイト ジングルバイト文字
e-words: シングルバイト文字rune (int32 の別名)。1バイト以上なのでマルチバイト文字(日本語など)
e-words: マルチバイト文字
package main import "fmt" func main() { s := "こんにちは" // 5文字なのに 15 になる fmt.Println(len(s)) // 15 // マルチバイト文字は正しく出力されない fmt.Println(string(s[0])) // ã // []byteにすると1バイトごとにスライスになる。 // 1バイトごとのスライスを数えるので上の len(s) は 15 になるし、 // s[0] での抜き出しも正しく出力されない fmt.Println([]byte(s)) // [227 129 147 227 130 147 227 129 171 227 129 161 227 129 175] // []runeにすると文字ごとにスライスになる fmt.Println([]rune(s)) // [12371 12435 12395 12385 12399] }
マルチバイトの文字数取得
unicode/utf8パッケージ
package main import "unicode/utf8" func main() { s := "こんにちは" // []rune にしてから長さを数える r := []rune(s) println(len(r)) // 5 // runeの長さを数える関数を使う println(utf8.RuneCountInString(s)) // 5 }
マルチバイトの文字を取得
一度 []rune に変換する
package main func main() { s := "hello こんにちは" r := []rune(s) println(string(r[6])) // こ println(string(r[7])) // ん println(string(r[8])) // に println(string(r[9])) // ち println(string(r[10])) // は }
マルチバイト文字の切り取り
package main import "strings" func main() { s := "hello こんにちは" // 一度 []rune にして抜き出す r := []rune(s) println(string(r[6:len(s)])) // こんにちは // strings パッケージの Index 関数を使えば開始位置がわかる target := "こんにちは" if i := strings.Index(s, target); i != -1 { println(s[i : i+len(target)]) // こんにちは } }
文字列のループ
for 文は、[]byte をインデックス番号で取得するので byte/uint8 になる。
なので、これもマルチバイト文字は正確に扱うことができない。
package main import ( "reflect" ) func main() { s := "hello" for i := 0; i < len(s); i++ { kind := reflect.TypeOf(s[i]).String() println(kind) // uint8 } }
対して、for...range のは、変数に rune/int32 がセットされる。 []rune としてループする
package main import ( "reflect" ) func main() { s := "hello" for _, v := range s { kind := reflect.TypeOf(v).String() println(kind) // int32 } }
シングルバイト文字とマルチバイト文字の判別
package main import ( "fmt" "unicode/utf8" ) func main() { s := "hello こんにちは 12345" for _, v := range s { if v < utf8.RuneSelf { fmt.Println("Singlebyte char:", string(v)) } else { fmt.Println("Multibyte char :", string(v)) } } }
文字列に関する便利な標準パッケージ
stringsパッケージ
https://golang.org/pkg/strings/fmtパッケージ
https://golang.org/pkg/fmt/strconvパッケージ
https://golang.org/pkg/strconv/textパッケージ
https://golang.org/pkg/text/