【Go入門】stringsパッケージ ~ 文字列操作(検索/結合/分割/置換/トリム)

シェアする

こんにちは。Go入門ブログの第28回です。

今回は、Goにおける文字列操作の方法について解説します。
Goにおいて文字列操作には、stringsパッケージが提供する関数を使用します。
stringsは、UTF-8エンコードされた文字列を操作する機能を提供する標準パッケージです。

stringsパッケージが提供する関数はどれもシンプルかつ便利ですが、本記事では特に使用頻度が高いものを用途別にまとめて紹介します。

文字列の検索と判別

Index / Lastindex関数 – 文字列の位置検索

任意の文字列に対し、特定の部分文字列が含まれる位置を検索した場合、strings.Index関数とstrings.LastIndex関数が使用できます。

func Index(s string, substr string) int
func LastIndex(s string, substr string) int

これらの関数はいずれも、第1引数sにs指定された文字列を対象に、第2引数substrが出現するインデックスを数値で返します。
両者を比較すると、sの中にsubstrが複数含まれる場合の結果が異なります。
Index関数は、substrが最初に出現した位置を返し、Index関数はsubstrが最後に出現した位置を返します。

なお、substrが検索対象に含まれない場合、-1を返します。

サンプルと実行結果は以下の通りです。

package main

import (
    "fmt"
    "strings"
)

func main() {
    // 検索対象文字列
    trgStr := "go-stringsパッケージ-string.Index"

    // "str"の位置を検索
    idx := strings.Index(trgStr, "str")
    lidx := strings.LastIndex(trgStr, "str")

    // 検索結果の出力
    fmt.Printf("strが最初に出現する位置は%dです。\n", idx)
    fmt.Printf("strが最後に出現する位置は%dです。\n", lidx)

}
$ go run main.go
strが最初に出現する位置は3です。
strが最後に出現する位置は26です。
Contains関数 – 文字列の有無検索

strings.Contains関数は、strings.Indexと同じく文字列検索を行う関数です。
ただし、指定した部分文字列の位置を取得するのではなく、部分文字列が含まれるかどうかの判別のみを行います。

func Contains(s string, chars string) bool

第1引数sにs指定された文字列内に、第2引数substrが含まれる場合はtrueを、含まれない場合はfalseを返します。
目的に応じてIndex・LastIndexと使い分けてください。

サンプルと実行結果は以下の通りです。

package main

import (
    "fmt"
    "strings"
)

// strings.Containsを用いて文字列を検索する関数
func charCheck(trg string, substr string) {
    if strings.Contains(trg, substr) {
        // trgにsubstrが含まれる場合
        fmt.Printf("文字列「%s」に%sは含まれます。\n", trg, substr)
    } else {
        // trgにsubstrが含まれない場合
        fmt.Printf("文字列「%s」に%sは含まれません。\n", trg, substr)
    }
}

func main() {
    // 検索対象文字列
    trgStr := "stringsパッケージ"

    // trgStrに含まれる文字列で検索実行
    charCheck(trgStr, "ing")

    // trgStrに含まれない文字列で検索実行
    charCheck(trgStr, "ed")

}
$ go run main.go
文字列「stringsパッケージ」にingは含まれます。
文字列「stringsパッケージ」にedは含まれません。
HasPrefix / HasSuffix関数 – 文字列の先頭/末尾の検索

strings.HasPrefix関数およびstrings.HasSuffix関数は、strings.Contains関数の派生といえる機能です。

func HasPrefix(s string, prefix string) bool
func HasSuffix(s string, suffix string) bool

strings.HasPrefixは、第1引数sに指定された文字列の先頭が、第2引数prefixと一致するかを判別します。
strings.HasSuffixは、第1引数sに指定された文字列の末尾が、第2引数suffixと一致するかを判別します。
関数名が表す通り、プレフィックス(接頭辞)やサフィックス(接尾辞)で管理するような文字列データの判別に役立つでしょう。

サンプルと実行結果は以下の通りです。

package main

import (
    "fmt"
    "strings"
)

// strings.HasPrefixとstrings.HasSuffixを用いて文字列を検索する関数
func charCheck(trg string, substr string) {
    if strings.HasPrefix(trg, substr) {
        // 先頭がsubstrと一致する場合
        fmt.Printf("文字列「%s」の先頭は%sです。\n", trg, substr)
    } else if strings.HasSuffix(trg, substr) {
        // 末尾がsubstrと一致する場合
        fmt.Printf("文字列「%s」の末尾は%sです。\n", trg, substr)
    } else {
        fmt.Printf("文字列「%s」の先頭・末尾はいずれも%sではありません。\n", trg, substr)
    }
}

func main() {
    // 検索対象文字列
    leftGo := "Go-XXXXXX"
    rightGo := "XXXXXX-Go"
    midGo := "XXX-Go-XXX"

    // 先頭がGoの文字列をチェック
    charCheck(leftGo, "Go")

    // 末尾がGoの文字列をチェック
    charCheck(rightGo, "Go")

    // 中間にGoを含む文字列をチェック
    charCheck(midGo, "Go")

}
$ go run main.go
文字列「Go-XXXXXX」の先頭はGoです。
文字列「XXXXXX-Go」の末尾はGoです。
文字列「XXX-Go-XXX」の先頭・末尾はいずれもGoではありません。

文字列の結合と分割

Join関数 – 文字列の結合

strings.Join関数は、文字列型の配列[]string型に含まれる複数の文字列を結合し、新しい文字列を返します。

func Join(array []string, sep string) string

第2引数には、セパレータ文字列を指定できます。
これを指定することで、要素間に任意の文字列を挿入することができます。

strings.Join関数は、配列の要素を+=演算子とループによって文字列結合するよりも高速に動作するでしょう。
サンプルと実行結果は以下の通りです。

package main

import (
    "fmt"
    "strings"
)

func main() {
    // 結合対象の配列
    arr := []string{"hoge", "fuga", "piyo"}

    // 配列の要素をカンマ + 半角スペースで区切って結合
    s1 := strings.Join(arr, ", ")
    // 結合した文字列を出力
    fmt.Println(s1)

    // 配列の要素を区切り文字を指定せずに結合
    s2 := strings.Join(arr, "")
    // 結合した文字列を出力
    fmt.Println(s2)

}
$ go run main.go
hoge, fuga, piyo
hogefugapiyo
Split / SplitN関数 – 文字列の分割

先ほどのstrings.Join関数と逆の機能を持つのが、strings.Split関数とstrings.SplitN関数です。

func Split(s string, sep string) []string
func SplitN(s string, sep string, n int) []string

Split関数は、第1引数sを第2引数に指定されたセパレータ文字列sepで分割し、配列にして返します。
sepがsに存在しない場合は、要素数が1の配列が返されます。
また、セパレータsepが指定されていない場合、UTF-8文字ごとに分割されます。

SplitN関数は、Split関数と同等の機能に加え、第3引数nに配列の長さ(要素数)を指定することができます。
セパレータの数がいくつであっても、第3引数nを超えて分割は行われず、分割されなかった文字列は最後の要素にすべて格納されます。

サンプルと実行結果は以下の通りです。

package main

import (
    "fmt"
    "strings"
)

func main() {
    // 分割対象の文字列
    s := "hoge, fuga, piyo"

    // 文字列をカンマ + 半角スペースで分割
    arr1 := strings.Split(s, ", ")
    // 配列を出力
    fmt.Println(arr1)

    // 文字列をカンマ + 半角スペースで分割(要素数: 2)
    arr2 := strings.SplitN(s, ", ", 2)
    // 配列を出力
    fmt.Println(arr2)
}
$ go run main.go
[hoge fuga piyo]
[hoge fuga, piyo]

文字列の置換とトリム

Replace / ReplaceAll関数 – 文字列の置換

オーソドックスな文字列置換には、strings.Replace関数とstrings.ReplaceAll関数が利用できます。

func Replace(s string, old string, new string, n int) string
func ReplaceAll(s string, old string, new string) string

どちらの関数も、第1引数sに含まれる第2引数oldを、第3引数で指定したnewに書き換えます。
なお、oldに空文字列を指定した場合、文字列の最初とそれぞれのUTF-8文字の間にマッチします。

両者で異なるのは、Replace関数のみ、第4引数nを指定する点です。

第4引数nには、置換する回数を指定します。
つまり、文字列sの先頭から検索し、oldが出現する最初のn回目まで、newへの書き換えを行います。
n回を越えてoldが出現する場合、n回目以降のoldに対しては置換は行われません。

これに対し、ReplaceAll関数は、置換回数nを指定しません。
つまり、文字列s内のすべてのoldをnewに書き換えます。

サンプルと実行結果は以下の通りです。

package main

import (
    "fmt"
    "strings"
)

func main() {
    // 置換前の文字列
    org := "foo bar foo bar foo bar foo bar"

    // barを2箇所のみbazに置換
    rep1 := strings.Replace(org, "bar", "baz", 2)
    // 置換後の文字列を出力
    fmt.Println(rep1)

    // fooをすべてbazに置換
    rep2 := strings.ReplaceAll(org, "foo", "baz")
    // 置換後の文字列を出力
    fmt.Println(rep2)
}
$ go run main.go
foo baz foo baz foo bar foo bar
baz bar baz bar baz bar baz bar
ToUpper / ToLower関数 – 大文字・小文字への変換

strings.ToUpper関数とstrings.ToLower関数は、それぞれ小文字から大文字、大文字から小文字への変換を行う関数です。

func ToUpper(s string) string
func ToLower(s string) string

ToUpper関数は、第1引数sに含まれるすべてのUnicode文字を大文字に変換した文字列を返します。
ToLower関数は、第1引数sに含まれるすべてのUnicode文字を小文字に変換した文字列を返します。

サンプルと実行結果は以下の通りです。

package main

import (
    "fmt"
    "strings"
)

func main() {
    // 置換前の文字列
    org := "hoge HOGE ほげ fuga FUGA ふが"

    // 小文字をすべて大文字に変換
    rep1 := strings.ToUpper(org)
    // 置換後の文字列を出力
    fmt.Println(rep1)

    // 大文字をすべて小文字に変換
    rep2 := strings.ToLower(org)
    // 置換後の文字列を出力
    fmt.Println(rep2)
}
$ go run main.go
HOGE HOGE ほげ FUGA FUGA ふが
hoge hoge ほげ fuga fuga ふが
Trim / TrimLeft / TrimRight関数 – 文字列のトリム

文字列のトリムには、strings.Trim関数、strings.TrimLeft関数、strings.TrimRight関数があります。
名前が表すとおり、それぞれ両端トリム、左トリム、右トリムと考えて差し支えありません。

func Trim(s string, cutset string) string
func TrimLeft(s string, cutset string) string
func TrimRight(s string, cutset string) string

は,s のの cutset に含まれる Unicode コードポイントを除いた スライスを返します。
Trim関数は、第1引数sの先頭と末尾から、第2引数cutsetに指定された文字列を取り除きます。
TrimLeft関数は、第1引数sの先頭から、第2引数cutsetに指定された文字列を取り除きます。
TrimRight関数は、第1引数sの末尾から、第2引数cutsetに指定された文字列を取り除きます。

サンプルと実行結果は以下の通りです。

package main

import (
    "fmt"
    "strings"
)

func main() {
    // トリム前の文字列
    org := "0X0X0X-123456789-X0X0X0"

    // 両端の0のみをトリム
    trm1 := strings.Trim(org, "0")
    // トリム後の文字列を出力
    fmt.Println(trm1)

    // 先頭の0X-をトリム
    trm2 := strings.TrimLeft(org, "0X-")
    // トリム後の文字列を出力
    fmt.Println(trm2)

    // 末尾の0X-のみをトリム
    trm3 := strings.TrimRight(org, "0X-")
    // トリム後の文字列を出力
    fmt.Println(trm3)

}
$ go run main.go
X0X0X-123456789-X0X0X
123456789-X0X0X0
0X0X0X-123456789

終わりに

当記事で紹介した関数以外にも、stringsパッケージには様々な機能が用意されています。
多機能であるに越したことはありませんが、正直なところ使いどころがあまりないような関数も多く存在します。
多くの場合、当記事で取り上げた関数で充分と思いますので、その他の機能は必要に応じてリファレンスを参照するとよいでしょう。

スポンサーリンク
スポンサーリンク
スポンサーリンク

シェアする

フォローする

スポンサーリンク
スポンサーリンク