Menu

Go言語の文字列操作:Split、Join、Contains、Replaceなど

Goで文字列を扱う方法を解説します。stringsパッケージ(Split、Join、Contains、Replace、Fields、TrimSpace、ToUpper)、strings.Builderによる効率的な文字列の組み立て、複数行の生文字列、不変性、そしてlenがバイト数を数える理由まで。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

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=valueuser@host のようなペアを分けるのに最もすっきりした方法です。Go 1.24では、スライスを作らずに要素を1つずつ返すイテレータ版の strings.SplitSeqstrings.FieldsSeq も追加されました。

検索する

関数戻り値
Contains(s, sub)ssub が現れれば true
ContainsAny(s, chars)chars のいずれかの文字が現れれば true
HasPrefix(s, p) / HasSuffix(s, p)sp で始まる、または終われば true
Index(s, sub)最初に一致したバイト位置、なければ -1
LastIndex(s, sub)最後に一致したバイト位置、なければ -1
Count(s, sub)重ならない一致の数
EqualFold(a, b)大文字小文字を区別しない等価比較

EqualFold 以外の検索はすべて大文字小文字を区別します。区別せずに検索するには、両方を小文字にするか、文字列全体の比較なら EqualFold を使います。

変形する

罠が1つあります。TrimTrimLeftTrimRight が受け取るのは接頭辞ではなく 文字の集合 です。strings.TrimLeft("abcab", "ab") は先頭の ab をすべて取り除き、"cab" を返します。決まった接頭辞や接尾辞を取り除くには、TrimPrefixTrimSuffix を使います。

連結と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.Itoastrconv.FormatFloatfmt.Sprintf で文字列になり、文字列は strconv.Atoistrconv.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.HasPrefixstrings.HasSuffixstrings.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バイトです。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める