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>)
」と直接記載します。
m
m
フラグがセットされていない場合、^
・$
は、入力文字列の頭と末を指しますm
フラグをセットした場合、^
・$
は、行頭と行末を指します
i
i
フラグがセットされていない場合、大文字・小文字を区別します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言語と違って、文字列はイミュータブル(変更不可)であるのが保証されているので、データにアクセスできるスライスへのキャストや、スライスから文字列へのキャストは、 必ずデータのコピーが発生するので注意が必要ですね。