Go言語は、シンプルな関数を組み合わせてプログラミングをすることが多く、また、関数毎にエラーチェックが発生するので、全般的にコードが長くなりがちです。
ちょっとした事をしたい時でも、コードをそれなりに書く必要があるのですが、毎回ゼロから書くのは面倒なので、よくやる処理を自分コピペ用にメモしておこうと思います。
ここでは、Go言語で、正規表現や文字列操作する方法の自分用メモを記載しました。
正規表現
まずは下記の一番凝ったやつから記します。
「全てのマッチ箇所を取得かつ、サブマッチあり」
regext.MustCompile()で正規表現オブジェクトを作成しますRegexp.FindAllStringSubmatch()で、全てのマッチ箇所とサブマッチ文字列を取得します- 1つのマッチ結果は
[]stringに格納され、インデックス0が全体のマッチ箇所。1以降がサブマッチ文字列になります - 全てのマッチ結果を取得するので、結果は1つのマッチ結果(
[]string)のスライス[][]stringで返ります Regexp.FindAllStringSubmatch()の2番目の引数は、取得するマッチ数で、全部のマッチ箇所を取得する場合は-1とします
str := ` 2020-01-01_test # comment 2020-02-02_TEST 2020-03-03_TEST` re := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})_(TEST)`) res := re.FindAllStringSubmatch(str, -1) for _, v := range res { fmt.Println(v) // [2020-02-02_TEST 2020 02 02 TEST] // [2020-03-03_TEST 2020 03 03 TEST] }
フラグ
検索フラグを使う時は、正規表現定義の中で「(?<flag>)」と直接記載します。
mmフラグがセットされていない場合、^・$は、入力文字列の頭と末を指しますmフラグをセットした場合、^・$は、行頭と行末を指します
iiフラグがセットされていない場合、大文字・小文字を区別しますiフラグをセットした場合、大文字・小文字を区別しません
str := ` 2020-01-01_test # comment 2020-02-02_TEST 2020-03-03_TEST` re := regexp.MustCompile(`(?mi)(\d{4})-(\d{2})-(\d{2})_(TEST)$`) res := re.FindAllStringSubmatch(str, -1) for _, v := range res { fmt.Println(v) // [2020-01-01_test 2020 01 01 test] // [2020-02-02_TEST 2020 02 02 TEST] // [2020-03-03_TEST 2020 03 03 TEST] }
置換
Regexp.ReplaceAllString()で置換します- マッチ文字列は
$0・$1...で表します
str := `2020-01-01_test` re := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2}).+$`) res := re.ReplaceAllString(str, "$1/$2/$3") fmt.Println(res) // 2020/01/01
関数名の「All」「String」
関数名には「All」あり・なし、「String」あり・なしバージョンがあります。
例)FindStringSubmatch()、FindAllSubmatch()など
All
最初にマッチした部分だけ取得なら「All」をなしにします。
String
Go言語の仕様上、[]byteをstringに変換すると、[]byteからstringへ、データのコピーが行われます。また、逆の場合もデータのコピーが行われます。
データを[]byteで扱っている場合は、検索のために都度stringに変換しているとコピーが発生して効率が悪いので、「String」なしの関数を使えば、
入出力データをstringではなく[]byteで扱うようにできます。
マッチするかだけ確認
Regexp.MatchString()を使えばマッチの確認だけ行えます
str := "TEST" re := regexp.MustCompile(`(?i)test`) res := re.MatchString(str) fmt.Println(res) // true
文字列を区切りで分割(Split)
str := `a, b; c| d e` re := regexp.MustCompile(`[,;\|\s]+`) res := re.Split(str, -1) fmt.Println(res) // [a b c d e]
文字列操作(strings)
stringsパッケージを使った文字列操作もよくやるのでまとめておきます。
文字列からインデックスで文字を取り出す
stringは[]byteでデータを保持しています。
stringをインデックスでアクセスすると、指定位置の文字ではなく、指定位置のbyteのデータを指します。
また、stringのlen()は、文字数ではなくバイト数を返します。
stringの[]byteからインデックスで文字を取り出すには、一旦キャストで文字(rune)に分解すると楽です。
str := "日本語" runes := []rune(str) for i := 0; i < len(runes); i++ { fmt.Println(string(runes[i]), " --- ", string(runes[i:])) // 日 --- 日本語 // 本 --- 本語 // 語 --- 語 }
ただし、stringから文字(rune)にキャストすると、文字列データのコピーが発生します。[]runeからstringの変換もデータのコピーが発生します。
前後の空白を取り除く
strings.TrimSpace()を使うと、文字列の前後の空白を削除してくれます。空白は全角スペースやタブも空白とみなされます。
str := " \t TEST " fmt.Println(strings.TrimSpace(str)) // TEST
大文字・小文字変換
str := "TEST" fmt.Println(strings.ToLower(str)) // test str = "test" fmt.Println(strings.ToUpper(str)) // TEST
文字列を含むかチェック
正規表現を使うほどではないけど、ある文字列が、ある文字列を含むかちょっと調べたい時は、strings.Index()を使うと楽です。
strings.Index()はマッチする位置を返し、マッチしない場合は-1を返すので、-1かどうかを見て、文字列を含むかを判定することができます。
str := "TEST" fmt.Println(strings.Index(str, "E")) // 1 fmt.Println(strings.Index(str, "e")) // -1
結合
strings.Join()で文字列の結合ができます。
chars := []string{"T", "E", "S", "T"} fmt.Println(strings.Join(chars, ",")) // T,E,S,T
感想など
Go言語はC言語と違って、文字列はイミュータブル(変更不可)であるのが保証されているので、データにアクセスできるスライスへのキャストや、スライスから文字列へのキャストは、 必ずデータのコピーが発生するので注意が必要ですね。