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言語を用いた正規表現の基本構文や各パターン、操作例を具体的なサンプルコードとともに解説しました。
正規表現におけるリテラル、メタ文字、文字クラス、グループ化、数値・英字パターン、任意文字、位置指定、繰り返しなどの概念と実践的な利用法が整理できました。
ぜひ、サンプルコードを試しながら自分自身で実装し、実践に活かしてください。