golangの日記

Go言語を中心にプログラミングについてのブログ

Go言語(golang) 文字列/string

golang.png


文字列型(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)



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))
        }
    }
}



文字列に関する便利な標準パッケージ