文字列

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.MatchStringregexp.FindStringなどの関数を利用して、パターンにマッチする部分を見つけることができます。

MatchとFind関数の使い方

以下は、MatchStringFindStringを利用して文字列からパターンに沿った部分を検索するサンプルコードです。

最初はマッチするかどうかを真偽値で返し、次に実際にマッチした部分文字列を抽出しています。

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.Compileregexp.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言語を用いた正規表現の基本構文や各パターン、操作例を具体的なサンプルコードとともに解説しました。

正規表現におけるリテラル、メタ文字、文字クラス、グループ化、数値・英字パターン、任意文字、位置指定、繰り返しなどの概念と実践的な利用法が整理できました。

ぜひ、サンプルコードを試しながら自分自身で実装し、実践に活かしてください。

関連記事

Back to top button
目次へ