Goの文字列は変更できないバイトの並びで、通常はUTF-8のテキストです。演算子は文字列そのものに対して使え(+、==、<、インデックス、スライス)、それ以外はすべて標準の strings パッケージにあります。
文字列を分割する
| 関数 | 分割する位置 | 備考 |
|---|---|---|
Split(s, sep) | すべての sep | 空の要素を残す。Split("", ",") は空文字列1つの [""] を返す |
SplitN(s, sep, n) | 最大 n 個に | 最後の要素に残りが入る |
SplitAfter(s, sep) | すべての sep | 各要素に区切り文字を残す |
Fields(s) | 連続する空白 | 空の要素を返さない |
FieldsFunc(s, f) | f がtrueになる文字 | 独自の区切り |
Cut(s, sep) | 最初の sep だけ | 前、後ろ、見つかったかを返す(Go 1.18) |
Split("", ",") はつまずきやすいケースです。空の入力に対して、長さ0ではなく長さ1のスライスが返ります。ユーザー入力を分割するときは、Fields を使うか先に s == "" をチェックすれば、存在しない空の要素を避けられます。
strings.Cut は、key=value や user@host のようなペアを分けるのに最もすっきりした方法です。Go 1.24では、スライスを作らずに要素を1つずつ返すイテレータ版の strings.SplitSeq と strings.FieldsSeq も追加されました。
検索する
| 関数 | 戻り値 |
|---|---|
Contains(s, sub) | s に sub が現れれば true |
ContainsAny(s, chars) | chars のいずれかの文字が現れれば true |
HasPrefix(s, p) / HasSuffix(s, p) | s が p で始まる、または終われば true |
Index(s, sub) | 最初に一致したバイト位置、なければ -1 |
LastIndex(s, sub) | 最後に一致したバイト位置、なければ -1 |
Count(s, sub) | 重ならない一致の数 |
EqualFold(a, b) | 大文字小文字を区別しない等価比較 |
EqualFold 以外の検索はすべて大文字小文字を区別します。区別せずに検索するには、両方を小文字にするか、文字列全体の比較なら EqualFold を使います。
変形する
罠が1つあります。Trim、TrimLeft、TrimRight が受け取るのは接頭辞ではなく 文字の集合 です。strings.TrimLeft("abcab", "ab") は先頭の a と b をすべて取り除き、"cab" を返します。決まった接頭辞や接尾辞を取り除くには、TrimPrefix と TrimSuffix を使います。
連結とstrings.Builder
+ は文字列をつなぎ、fmt.Sprintf は値を整形して1つの文字列にします。文字列は変更できないので、どちらも毎回新しい文字列を確保します。数回の操作なら問題ありませんが、ループの中では遅くなります。+= のたびに、それまでに作ったものすべてがコピーされるからです。代わりに strings.Builder を使います。
ゼロ値の strings.Builder はすぐに使えます。io.Writer を実装しているので、fmt.Fprintf で直接書き込めます。結果のおおよその大きさがわかっているなら、先に b.Grow(n) を呼びます。書き込んだ後の Builder はコピーせず、ポインタで渡してください。
複数行の文字列と生文字列
Goの文字列リテラルには2種類あります。
ダブルクォートの文字列はエスケープ(\n、\t、\"、\\、\u00e9)を処理し、1行に収める必要があります。バッククォートの生文字列は改行も含めてすべてをそのまま扱うので、複数行の文字列、正規表現、JSON、Windowsのパスを書くのに使います。生文字列にバッククォートは入れられず、中のキャリッジリターンは取り除かれます。上のクエリは改行で始まっていることに注意してください。リテラルは開きバッククォートの直後から始まるからです。
長さ、インデックス、部分文字列
len(s) は バイト 数です。s[i] は バイト で、s[i:j] はバイト位置による部分文字列です。
ï と é はそれぞれ2バイトを使うので、この文字列は12バイトで10文字です。位置が strings.Index などから得たもの(文字の境界にあるバイトオフセット)であれば、バイト単位のスライスは安全です。「最初のn文字」を取り出すには、先に []rune に変換します。日本語のように1文字が3バイトになるテキストでは特に重要です。UTF-8の詳細はルーンとバイトのページで説明しています。
文字列は変更できない
バイトをその場で変更することはできません。s[0] = 'H' はコンパイルエラーになります(cannot assign to s[0] (neither addressable nor a map index expression))。strings の関数はすべて新しい文字列を返します。編集するには、[]byte か []rune に変換してスライスを変更し、元に戻します。
b := []byte("hello")
b[0] = 'H'
s := string(b) // "Hello"
変更できないからこそ、部分文字列は安価です。s[2:5] はコピーせずに s とメモリを共有します。その裏返しとして、巨大な文字列の小さな部分文字列が、巨大な文字列全体をメモリに残し続けます。大きな入力の短い一部を保持するときは strings.Clone(Go 1.20)を使います。
文字列を比較する
== と != は内容を比較し、< と > はバイトごとに比較します。ASCIIならアルファベット順です。strings.Compare もありますがあまり必要になりません。[]string に対する slices.Sort は < を使います。英語以外のテキストをロケールに沿って並べるには、golang.org/x/text/collate を使います。
他の型から変換する
数値は strconv.Itoa、strconv.FormatFloat、fmt.Sprintf で文字列になり、文字列は strconv.Atoi と strconv.ParseFloat で数値になります。string(42) と書いてはいけません。コードポイント42の文字 "*" になります。型変換のページに完全な表があります。
よくある質問
Goで文字列を分割するには?
strings.Split(s, sep) を使います:strings.Split("a,b,c", ",") は ["a" "b" "c"] を返します。連続する空白で分割して空の要素を除くには strings.Fields(s) を使います。strings.SplitN は分割数を制限し、strings.Cut は最初の区切り文字で1回だけ分割します。
Goで複数行の文字列を書くには?
バッククォートで囲んだ生文字列リテラルを使います。
query := `SELECT id, name
FROM users
WHERE active = true`
バッククォートの間にあるものは、改行やバックスラッシュも含めてすべてそのまま扱われます。生文字列の中にバッククォートは入れられません。
Goで文字列が部分文字列を含むか調べるには?
strings.Contains(s, substr) が bool を返します。関連する関数に、strings.HasPrefix、strings.HasSuffix、strings.Index(位置または-1)、strings.ContainsAny(文字の集合のいずれか)、大文字小文字を区別しない比較の strings.EqualFold があります。
Goで文字列を連結する最速の方法は?
数個の文字列なら + で十分です。ループの中で文字列を組み立てるなら strings.Builder を使い、WriteString を繰り返し呼んで、最後に一度だけ String() を呼びます。ループ内で += を繰り返すと毎回文字列全体がコピーされるので、文字列が長くなるほど遅くなります。スライスを区切り文字で結合するなら strings.Join を使います。
なぜlenが文字列の長さを間違って返すのですか?
len(s) が返すのは文字数ではなくバイト数です。Goの文字列はUTF-8で、ASCII以外の文字は2から4バイトを使うので、len("héllo") は6になります。Unicodeのコードポイントの数には utf8.RuneCountInString(s) を使います。日本語の文字は多くが3バイトです。