Go言語の正規表現一覧と基本構文について解説
Go言語で利用できる正規表現を一覧で紹介します。
Goの標準パッケージであるregexpを中心に、文字列検索やパターンマッチングに必要な構文を整理しました。
開発環境が整っている方向けに、実際のコード例を交えながらシンプルに解説しています。
正規表現の基本構文
正規表現では、文字列のパターンを記述するための構文が用意されています。
Go言語では、標準パッケージのregexpを利用してパターンのマッチや操作を行うことができます。
ここでは、リテラル表現と特殊文字、またそれに付随するエスケープ方法やグループ化について説明します。
リテラルと特殊文字
正規表現のリテラルは、そのままの文字を表現するために使用されます。
一方、特殊文字は特定の意味(例えば、任意の文字にマッチする.など)を持っているため、そのままリテラルとして使いたい場合はエスケープが必要です。
メタ文字の一覧
正規表現におけるメタ文字とは、特別な意味を持つ文字です。
以下は主なメタ文字の一覧です。
.:任意の1文字にマッチ^:文字列の先頭にマッチ$:文字列の末尾にマッチ*:直前の要素の0回以上の繰り返しにマッチ+:直前の要素の1回以上の繰り返しにマッチ?:直前の要素の0回または1回の出現にマッチ{}:繰り返し回数を指定する[]:文字クラスを表現する():グループ化またはキャプチャに使用|:論理的な「または」を表現\:エスケープ文字として使用
以下は、メタ文字をリストアップして表示するサンプルコードです。
package main
import (
	"fmt"
)
func main() {
	// メタ文字の一覧をスライスに格納
	metaCharacters := []string{".", "^", "$", "*", "+", "?", "{", "}", "[", "]", "(", ")", "|", "\\"}
	for _, char := range metaCharacters {
		fmt.Println("メタ文字:", char)
	}
}メタ文字: .
メタ文字: ^
メタ文字: $
メタ文字: *
メタ文字: +
メタ文字: ?
メタ文字: {
メタ文字: }
メタ文字: [
メタ文字: ]
メタ文字: (
メタ文字: )
メタ文字: |
メタ文字: \エスケープ方法
特殊文字をリテラルとして使用するには、バックスラッシュ\を利用してエスケープする必要があります。
Go言語では、regexp.QuoteMetaという関数を使用すると、自動的にエスケープ処理が行われるため便利です。
package main
import (
	"fmt"
	"regexp"
)
func main() {
	// エスケープが必要な文字列
	rawPattern := `a.b*c`
	// regexp.QuoteMetaでエスケープ済みのパターンを生成
	escapedPattern := regexp.QuoteMeta(rawPattern)
	fmt.Println("エスケープ前:", rawPattern)
	fmt.Println("エスケープ後:", escapedPattern)
}エスケープ前: a.b*c
エスケープ後: a\.b\*c文字クラスとグループ化
正規表現では、文字クラスやグループ化を使用することで、より柔軟なパターンの記述が可能です。
文字クラスの指定方法
文字クラスは、角括弧[]で囲むことで、特定の文字集合にマッチさせる方法です。
例えば、[a-z]は小文字のアルファベットに、[0-9]は数字にマッチします。
package main
import (
	"fmt"
	"regexp"
)
func main() {
	// 英字のパターンを定義
	pattern := "[a-z]+"
	text := "abc XYZ"
	re, err := regexp.Compile(pattern)
	if err != nil {
		fmt.Println("エラー:", err)
		return
	}
	// 英字の小文字にマッチする部分を検索
	match := re.FindString(text)
	fmt.Println("マッチした文字列:", match)
}マッチした文字列: abcグループ化の利用例
グループ化は、括弧()を使用してパターンの一部をグループとして扱います。
これにより、マッチした部分をキャプチャすることが可能になります。
例えば、日付の形式YYYY-MM-DDをグループ化して、年、月、日をそれぞれ抽出することができます。
package main
import (
	"fmt"
	"regexp"
)
func main() {
	// 日付形式を検出する正規表現パターン
	pattern := `([0-9]{4})-([0-9]{2})-([0-9]{2})`
	text := "本日の日時は2023-10-15です。"
	re, err := regexp.Compile(pattern)
	if err != nil {
		fmt.Println("エラー:", err)
		return
	}
	// キャプチャを含むマッチを取得
	match := re.FindStringSubmatch(text)
	if len(match) > 0 {
		fmt.Println("全体:", match[0])
		fmt.Println("年:", match[1])
		fmt.Println("月:", match[2])
		fmt.Println("日:", match[3])
	} else {
		fmt.Println("マッチなし")
	}
}全体: 2023-10-15
年: 2023
月: 10
日: 15正規表現パターン一覧
正規表現パターンは、扱うデータの種類に合わせて使い分けられます。
ここでは、数値や文字列のパターン、特殊なパターン(任意文字、位置指定、繰り返し、オプショナルマッチ)について解説します。
数値と文字列のパターン
数値パターン(整数・小数)の例
数値をマッチさせるパターンでは、整数や小数に対応するために、繰り返し指定やオプショナルな部分を用います。
一般的なパターンは、整数もしくは小数を表すために^\d+(\.\d+)?$が利用されます。
ここで、\dは数字にマッチし、(\.\d+)?は小数点以下の数字をオプショナルにグループ化しています。
package main
import (
	"fmt"
	"regexp"
)
func main() {
	pattern := `^\d+(\.\d+)?$`
	re, err := regexp.Compile(pattern)
	if err != nil {
		fmt.Println("正規表現のコンパイルエラー:", err)
		return
	}
	testStrings := []string{"123", "45.67", "abc", "89.12.34"}
	for _, str := range testStrings {
		if re.MatchString(str) {
			fmt.Println(str, "は数値にマッチしました。")
		} else {
			fmt.Println(str, "は数値にマッチしません。")
		}
	}
}123 は数値にマッチしました。
45.67 は数値にマッチしました。
abc は数値にマッチしません。
89.12.34 は数値にマッチしません。英字・単語パターンの例
英字や単語をマッチさせるには、文字クラス[a-zA-Z]やワード文字\wを利用します。
以下の例では、アルファベットからなる単語にマッチさせる方法を示しています。
package main
import (
	"fmt"
	"regexp"
)
func main() {
	pattern := `[a-zA-Z]+`
	re, err := regexp.Compile(pattern)
	if err != nil {
		fmt.Println("コンパイルエラー:", err)
		return
	}
	text := "Go言語123 and Python"
	matches := re.FindAllString(text, -1)
	fmt.Println("マッチした単語一覧:", matches)
}マッチした単語一覧: [Go 言 語 and Python]特殊パターン
任意文字と位置指定
任意文字を表す.は、改行以外の任意の1文字にマッチします。
また、^と$は文字列の先頭と末尾にマッチさせるために使われます。
例えば、^Hello.*World$というパターンは、Helloで始まりWorldで終わる文字列全体にマッチさせるための形となっています。
package main
import (
	"fmt"
	"regexp"
)
func main() {
	pattern := `^Hello.*World$`
	re, err := regexp.Compile(pattern)
	if err != nil {
		fmt.Println("パターンのコンパイルエラー:", err)
		return
	}
	testString := "Hello Go Language World"
	if re.MatchString(testString) {
		fmt.Println("文字列はパターンにマッチしています。")
	} else {
		fmt.Println("文字列はパターンにマッチしていません。")
	}
}文字列はパターンにマッチしています。繰り返しとオプショナルマッチ
正規表現では、*、+、?といった量指定子を用いて、文字やパターンの繰り返しおよびオプショナルなマッチを表現します。
*:0回以上の繰り返し+:1回以上の繰り返し?:0回または1回の出現
以下は、goという文字列の繰り返しやオプショナルな出現を検出する例です。
package main
import (
	"fmt"
	"regexp"
)
func main() {
	// "go"が1回以上続くパターン
	patternPlus := `(go)+`
	rePlus, err := regexp.Compile(patternPlus)
	if err != nil {
		fmt.Println("コンパイルエラー:", err)
		return
	}
	textPlus := "gogogo!"
	matchPlus := rePlus.FindString(textPlus)
	fmt.Println("1回以上のマッチ:", matchPlus)
	// "go"が0回または1回出現するパターン
	patternOptional := `(go)?`
	reOptional, err := regexp.Compile(patternOptional)
	if err != nil {
		fmt.Println("コンパイルエラー:", err)
		return
	}
	textOptional := "abc"
	matchOptional := reOptional.FindString(textOptional)
	fmt.Println("オプショナルマッチ:", matchOptional)
}1回以上のマッチ: gogogo
オプショナルマッチ:正規表現操作例
ここでは、正規表現を使ったマッチング処理や置換操作、エラーハンドリングおよびパフォーマンスの向上に役立つ手法について具体例を示します。
マッチング処理
正規表現によるマッチング処理は、文字列がパターンに合致しているかどうかを判断するために頻繁に利用されます。
regexp.MatchStringやregexp.FindStringなどの関数を利用して、パターンにマッチする部分を見つけることができます。
MatchとFind関数の使い方
以下は、MatchStringとFindStringを利用して文字列からパターンに沿った部分を検索するサンプルコードです。
最初はマッチするかどうかを真偽値で返し、次に実際にマッチした部分文字列を抽出しています。
package main
import (
	"fmt"
	"regexp"
)
func main() {
	pattern := `\d{3}`
	text := "受注番号は123456です。"
	// 文字列がパターンにマッチするかチェック
	match, err := regexp.MatchString(pattern, text)
	if err != nil {
		fmt.Println("エラー:", err)
		return
	}
	fmt.Println("パターンにマッチしているか:", match)
	// マッチした最初の文字列を取得するために、コンパイル済み正規表現を利用
	re, err := regexp.Compile(pattern)
	if err != nil {
		fmt.Println("コンパイルエラー:", err)
		return
	}
	found := re.FindString(text)
	fmt.Println("抽出された文字列:", found)
}パターンにマッチしているか: true
抽出された文字列: 123文字列置換
正規表現を用いて、文字列内の一部を置換することも簡単に行えます。
ReplaceAllString関数は、マッチした部分を指定の文字列に置換するために使用されます。
ReplaceAllの基本例
次のサンプルコードでは、メールアドレスのドメイン部分を置換する例を示しています。
package main
import (
	"fmt"
	"regexp"
)
func main() {
	// メールアドレスのパターンを定義
	pattern := `@[\w.]+`
	re, err := regexp.Compile(pattern)
	if err != nil {
		fmt.Println("エラー:", err)
		return
	}
	originalEmail := "example@old-domain.com"
	// ドメイン部分を新しい文字列に置換
	newEmail := re.ReplaceAllString(originalEmail, "@new-domain.com")
	fmt.Println("元のメールアドレス:", originalEmail)
	fmt.Println("置換後のメールアドレス:", newEmail)
}元のメールアドレス: example@old-domain.com
置換後のメールアドレス: example@new-domain.comエラーハンドリングとパフォーマンス
正規表現を利用する際、パターンのコンパイル時にエラーが発生する可能性があります。
また、複雑なパターンを頻繁に利用する場合、パフォーマンスにも留意する必要があります。
コンパイルエラーの対応方法
正規表現パターンを使用する際は、regexp.Compileでコンパイルし、エラーが返ってきた場合は適切に対応します。
以下は、コンパイルエラーが発生した場合の例です。
package main
import (
	"fmt"
	"regexp"
)
func main() {
	// 不正なパターンを意図的に使用
	pattern := `(\d+`
	re, err := regexp.Compile(pattern)
	if err != nil {
		fmt.Println("パターンのコンパイルに失敗しました:", err)
		return
	}
	// ここには到達しないが、正常ならば利用するコード
	fmt.Println("コンパイルしたパターン:", re.String())
}パターンのコンパイルに失敗しました: error parsing regexp: missing closing ): `(\d+`効率的なパターン利用のポイント
複数回利用する正規表現パターンは、毎回コンパイル処理を行うとパフォーマンスに影響が出るため、あらかじめregexp.Compileやregexp.MustCompileでコンパイル済みの正規表現を使い回すとよいです。
また、必要な場合は生の文字列リテラル(バッククォート`で囲む)を用いると、エスケープが簡略化できるメリットもあります。
package main
import (
	"fmt"
	"regexp"
)
func main() {
	// コンパイル済みパターンを複数回利用する例
	// MustCompileはエラー発生時にpanicするため、確実なパターンで利用する
	re := regexp.MustCompile(`[A-Z][a-z]+`)
	text := "Go言語とPythonとJava"
	// パターンにマッチした部分を全て取得する
	matches := re.FindAllString(text, -1)
	fmt.Println("大文字で始まる単語:", matches)
}大文字で始まる単語: [Go Python Java]まとめ
この記事では、Go言語を用いた正規表現の基本構文や各パターン、操作例を具体的なサンプルコードとともに解説しました。
正規表現におけるリテラル、メタ文字、文字クラス、グループ化、数値・英字パターン、任意文字、位置指定、繰り返しなどの概念と実践的な利用法が整理できました。
ぜひ、サンプルコードを試しながら自分自身で実装し、実践に活かしてください。