今回は O'Reilly の「正規表現」をもとに「一般的な正規表現の使い方」についてまとめます。
さまざまな言語を集約すると「一般的に正規表現ではこんなものが使えるよ」といったまとめになっています。
なので、言語やフレームワーク、利用するツールによって使える機能や使い方が微妙に異なるので、具体的なものは各リファレンスを参照してください。
単一文字
ドット
"."
(ドット) は「改行以外のすべての文字」にマッチします。
似たような指定方法に「否定文字クラスを利用した方法(例: [^"]
)」がありますが、否定文字クラスを利用する場合は改行にもマッチする点に注意します。
文字クラス
"[<文字群>]"
で指定された文字のうちのいずれかにマッチします(例: [abcd]
(= a~d のいずれか1文字))。
角括弧内では「 "-"(ハイフン) 」がメタ文字になります。
"-"(ハイフン) を利用して文字範囲を指定することができます(例: [a-d]
(= a~d のいずれか1文字))。
"-"(ハイフン) を文字として利用したい場合、最初に書く必要があります(例: [-a-z]
(= a~zおよび"-"のいずれか1文字))。
また、通常の正規表現構文中ではメタ文字となる "."(ドット)、 "*"(アスタリスク) "?"(クエスチョン) "+"(プラス) は、文字クラス内では単純文字として扱われるのでエスケープ不要です(例: [.*?+]
)。
"[^<文字群>]" のように "^"(ハット) を先頭に付けると「指定された文字を含まない任意の1文字」にマッチします(例: [^a-z]
(= a~z 以外の任意の1文字))。
文字クラスの略記
文字クラスのうち代表的なものには略記があります。
略記 |
説明 |
\d |
数字。
一般的には [0-9] と同じ。 |
\D |
数字以外。
一般的には [^0-9] と同じ。 |
\w |
単語の一部とみなせる文字。
一般的には [a-zA-Z0-9_] と同じ。
Unicodeサポートする場合、文字数字すべてを表現する場合もある。 |
\W |
単語の一部ではない文字。
一般的には [^\w] と同じ。 |
\s |
空白文字。
一般的には [ \f\n\r\r\t\v] と同じ。 |
\S |
空白文字以外。
一般的には [^\s] と同じ。 |
Unicodeプロパティ
"\p{<属性>}"
は「指定された Unicode属性 を持つ文字」にマッチします(例: \p{Letter}
(= 文字とみなせるもの))。
逆に "\P{<属性>}"
は「指定された Unicode属性 を持たない文字」にマッチします(例: \P{Letter}
(= 文字とみなせないもの))。
そもそも Unicode文字 には プロパティやスクリプト、ブロックといった属性情報が各Unicodeコードポイントに対して設定されています。
プロパティは「一般カテゴリ(General Category)」とも呼ばれたりするもので 文字(Letter)、記号(Mark)、数字(Number) といった「文字の大まかな分類」を定義しています。
スクリプトはラテン文字(Latin)、ギリシャ文字(Greek)、キリル文字(Cyrillic) といった「言語としての分類」を定義しています。
日本語の場合、ひらがな(Hiragana)、カタカナ(Katakana)、漢字(Han) といった分類になっています。
ブロックはスクリプトと同様にUnicodeコードポイントの範囲を示しますが文字が存在しない範囲も含めた範囲になっています。
一般カテゴリ (General Category)
一般カテゴリは「大分類」と「小分類」の2段階で分類されます。
その分類内容は以下の通りです。
正規表現では「大分類」か「値」を利用します(例: \p{Letter}
、 \p{Lu}
)。
大分類 |
小分類 |
値 |
説明 |
Letter |
Uppercase |
Lu |
大文字。 |
Lowercase |
Ll |
小文字。 |
Titlecase |
Lt |
単語の冒頭で使われる文字。 |
Modifier |
Lm |
特殊用途の文字風記号。 |
Other |
Lo |
大文字や小文字の区別のない文字。修飾子でもない文字(ヘブライ語、チベット語、日本語など)。 |
Mark |
Nonspacing |
Mn |
アクセント、特定の「母音記号」、声調記号など他の文字に変更を加える文字。 |
Spacing Combining |
Mc |
それ自体が文字幅を持つ変更用文字(主として、ベンガル語、タミル語、クメール語などで使われる母音記号)。 |
Enclosing |
Me |
他の文字を囲む円、正方形、ひし形などの記号。 |
Number |
Decimal Digit |
Nd |
さまざまな書記系における0から9までの数字(中国語、日本語、韓国語は含まない)。 |
Letter |
Nl |
主としてローマ数字。 |
Other |
No |
上付き数字、符号としての数字、数値でないが数をあらわす文字(中国語、日本語、韓国語は含まない)。 |
Punctuation |
Connector |
Pc |
アンダースコアなど言語学的な特別な意味を持つ句読点。 |
Dash |
Pd |
あらゆる種類のハイフンとダッシュ。 |
Open |
Ps |
(、「、《 などの開き括弧。 |
Close |
Pe |
)、」、》 などの閉じ括弧。 |
Initial Quote |
Pi |
«、“、‹ などの開きクォート。 |
Final Quote |
Pf |
»、”、› などの閉じクォート。 |
Other |
Po |
*、?、!、&、・ などその他すべての句読点や括弧など。 |
Symbol |
Math |
Sm |
+、÷、≦ などの数学記号。 |
Currency |
Sc |
$、€、¥、¢ などの通貨記号。 |
Modifier |
Sk |
^、`、¨、´ などのアクセント記号。 |
Other |
So |
©、®、⇨ などのその他特殊記号、罫線、展示、文字として扱われない漢字など。 |
Separator |
Space |
Zs |
さまざまなスペース記号。 |
Line |
Zl |
行区切り子文字。(U+2028) |
Paragraph |
Zp |
段落区切り子文字。(U+2029) |
Other |
Control |
Cc |
[TAB]、[CR]、[LF]など ASCII と Latin-1 の制御文字。 |
Format |
Cf |
基本的な整形情報を示すための表示されない文字。 |
Private Use |
Co |
私的に使うための文字を格納するために確保されたコードポイント。 |
Not Assigned |
Cn |
文字が割り当てられていないコードポイント。 |
上記の表で示された具体的なUnicode文字がどのようなものかは、以下のサイトで検索ができます。
スクリプト (Script)
スクリプトでは「書記系」を指定して検索できます。
指定できる値の一部を以下に載せます。
書記系指定する場合も一般カテゴリと同様に値を指定します(例: \p{Hira}
、\p{Hani}
)。
値 |
説明 |
Hira |
ひらがな。 |
Kana |
カタカナ。 |
Hani |
漢字。 |
Latn |
ローマ字。 |
Hebr |
ヘブライ文字。 |
Cyrl |
キリル文字。 |
上記の表で示された具体的なUnicode文字がどのようなものかは、以下のサイトで検索ができます。
ブロック (Block)
スクリプトに似ていますが、ブロックはUnicodeコードポイントの範囲を指定することができます。
以下に指定できるブロックの一部を載せます。
スクリプトと違って範囲指定なので文字が割り当たっていない場合があったり、ブロックに無関係の文字が割り当たっていることがあります。
指定方法は一般カテゴリと同様でブロック名を利用して指定します(例:\p{Hiragana}
、\p{Katakana}
)。
範囲 |
値 |
説明 |
U+3040 - U+309F |
Hiragana |
ひらがな。 |
U+30A0 - U+30FF |
Katakana |
カタカナ。 |
U+0000 - U+007F |
Basic Latin |
ローマ字。 |
U+0590 - U+05FF |
Hebrew |
ヘブライ文字。 |
U+0400 - U+04FF |
Cyrillic |
キリル文字。 |
上記の表で示された具体的なUnicode文字がどのようなものかは、以下のサイトで検索ができます。
キャプチャ
"()"
(丸括弧) を利用すると丸括弧で囲まれた範囲で "文字列" のカタマリを作れます。
以下ではこのグループ化された「文字列のカタマリ」の使い方について見ていきます。
グループ化して後方参照
"(…)"
でグループ化された「文字列のカタマリ」は一時的に保存(キャプチャ)され、"\1"
"\2"
... といった指定で後からキャプチャした文字列を参照して検索文字として再利用することができます。
// 対象文字列
abc-def-fed-cba
// 正規表現
/(\w)-\1/g
// 検索結果
abc-def-fed-cba
グループ化のみ
"(?:…)"
を利用すると一時的な保存(キャプチャ)を行わずにグループ化ができます。
キャプチャしないため速度がはやくなり、キャプチャしないので後方参照の番号を変えなくて済むようになります。
// 対象文字列
username@sample.company.com
// 正規表現
/(?:.*)@(.*\.com)/g
// 検索結果
username@sample.company.com
// 0: username@sample.company.com
// 1: sample.company.com
名前付きでグループ化して後方参照
"(?<name>…)"
を利用すると名前付きで一時的な保存(キャプチャ)が行えます。
名前付きでキャプチャした文字列は \k<name>
で呼び出せます。
// 対象文字列
username@sample.company.com
// 正規表現
/(?<name>.*)@(?<domain>.*\.com)/g
// 検索結果
username@sample.company.com
// 0: username@sample.company.com
// 1: username
// 2: sample.company.com
// groups: {
// name: username
// domain: sample.company.com
// }
アトミックグループ
"(?>…)"
を利用すると括弧内の部分式がマッチした際、バックトラックするための分岐を捨ててマッチした文字列を固定化(アトミック、変更不可)します。
// 対象文字列
Hello World !
// 正規表現1
/.*!/g
// 検索結果1 (= 正規表現1でマッチする文字列)
Hello World !
// 正規表現2
/(?>.*)!/g
// 検索結果2 (= 正規表現2でマッチする文字列)
Hello World !
上記の例のように正規表現1では "Hello World !" 全文にマッチしますが、アトミックグループを利用した正規表現2では一切マッチしません。
そもそも、正規表現の検索動作は欲張りに動きます。
上記の正規表現1の例だと以下のような動作をします。
.*
でマッチできる限りマッチします(= .*
で Hello World !
にマッチする)。
- 行末までいってしまうと行き過ぎなので、1文字ずつ戻って
!
を探します(= バックトラック)。
- 幸いにも1文字戻ると
.*!
が成り立つので、結果として Hello World !
を返します。
上記の正規表現2の例のようにアトミックグループを利用すると、 .*
でマッチした後、バックトラックするための情報をすべて捨ててしまうので戻ることができず、文字列マッチが失敗して終わります。
選択
"…|…|..."
を利用すると「指定された文字列のいずれか」といった条件分岐ができます。
正規表現中の一部で分岐を行いたい場合、"(…|…|...)"
のようにグループ化を併用することで部分的に分岐が利用できます。
// 対象文字列
This is a pen.
This or that.
// 正規表現
/this is|or that/ig
// 検索結果
This is a pen.
This or that.
上記の通り、グループ化を利用しない場合は正規表現全体に対して分岐処理が行われます(= this is
または or that
にマッチする)。
もし "This is that" または "This or that" にマッチさせたいのであれば this (is|or) that
のように指定します。
最大量指定子
*
、 +
、 ?
、 {num, num}
(範囲指定) を利用すると直前の対象(文字、文字クラスまたはグループ)を繰り返し、欲張りなマッチを行うことができます。
欲張りなマッチではできるだけ広くマッチするような検索を行います。
*
: 0回以上の繰り返し
+
: 1回以上の繰り返し
?
: 0回または1回の繰り返し
{min, max}
: min回以上、max回以下の繰り返し
// 対象文字列
The name "McDonald's" is said "makudonarudo" in Japanese.
// 正規表現
/".*"/g
// 検索結果
The name "McDonald's" is said "makudonarudo" in Japanese.
最小量指定子
*?
、 +?
、 ??
、 {num, num}?
(範囲指定) を利用すると直前の対象(文字、文字クラスまたはグループ)を繰り返し、控えめなマッチを行うことができます。
控えめなマッチではできるだけマッチする文字列が少なくなるような検索を行います。
// 対象文字列
The name "McDonald's" is said "makudonarudo" in Japanese.
// 正規表現
/".*?"/g
// 検索結果
The name "McDonald's" is said "makudonarudo" in Japanese.
絶対最大量指定子
*+
、 ++
、 ?+
、 {num, num}
(範囲指定) を利用すると直前の対象(文字、文字クラスまたはグループ)を繰り返し、固定化しながら欲張りなマッチを行います。
絶対最大量指定子は基本的に最大量指定子と同じ動作をしますが、一度マッチした文字列に対してはバックトラックを行いません(アトミックグループと同じ動作)。
.*+
は (?>.*)
と同じ結果になります。
つまり、絶対最大量指定子は繰り返しのあるアトミックグループの糖衣構文になります。
// 対象文字列
The name "McDonald's" is said "makudonarudo" in Japanese.
// 正規表現
/".*+"/g
// 検索結果(= マッチしない)
The name "McDonald's" is said "makudonarudo" in Japanese.
位置
正規表現には「位置」にマッチするものがあります。
「位置」にマッチするので幅はありません。
行頭・行末
"^"
(ハット) は行頭に、 "$"
(ドル記号) は行末の位置にマッチします。
// 対象文字列
He threw three free throws
She sees cheese
// 正規表現1
/^he/ig
// 検索結果1(= 行頭にある he にマッチする)
He threw three free throws
She sees cheese
// 正規表現2
/cheese$/ig
// 検索結果2(= 行末にある cheese にマッチする)
He threw three free throws
She sees cheese
単語の境界
\b
は単語の境界位置にマッチします。
// 対象文字列
He threw three free throws
She sees cheese
// 正規表現
/e\b/g
// 検索結果(= "e"で終わる単語にマッチする)
He threw three free throws
She sees cheese
後先読み
前後に指定された文字列があるかどうかで位置を特定するアンカーになります。
後先読みは「後読み」「先読み」と「肯定」「否定」の組み合わせで4種類存在します。
種類 |
指定子 |
説明 |
肯定先読み |
(?=…) |
見ている位置より先(右側)に指定した条件の文字列があるような位置。 |
否定先読み |
(?!…) |
見ている位置より先(右側)に指定した条件の文字列が存在しないような位置。 |
肯定後読み |
(?<=…) |
見ている位置より手前(左側)に指定した条件の文字列があるような位置。 |
否定後読み |
(?<!…) |
見ている位置より手前(左側)に指定した条件の文字列が存在しないような位置。 |
// 対象文字列
He threw three free throws
She sees cheese
// 正規表現1
/thr(?=ee)/g
// 検索結果1(= three の thr にマッチする)
He threw three free throws
She sees cheese
// 正規表現2
/(?<=ch)ee(?=se)/g
// 検索結果2(= cheese の ee にマッチする)
He threw three free throws
She sees cheese
オプション
g
グローバル
グローバル検索を行います。
テキスト全体に対して繰り返し一致する文字列を探します。
i
大文字小文字を区別しない
大文字小文字を区別しない検索を行います。
x
フリーフォーマット
正規表現を複数行に渡って記述できるようにします。
このモードでは文字クラス以外の空白は無視されます。
#
から 改行 までの間の文字列はコメントとみなされます。
$text =~ {
\b
# URLを$1にキャプチャする
(
http://hostname
(
/path
)?
)
}{<a href="$1">$1</a>}gix;
s
単一行モード(= ドット全マッチモード)
"."
(ドット) の動作を変更します。
通常モードでは "."(ドット) は改行にマッチしませんが、単一行モードでは改行にもマッチするようになります。
m
複数行モード (= 拡張行アンカーマッチモード)
"^"
(ハット) と "$"
(ドル記号) の動作を変更します。
通常モードでは与えられた文字列に対する先頭または末尾のみにしかマッチしません(文字列中に含まれる改行にはマッチしない)が、
複数行モードでは文字列中に含まれる改行にもマッチするようになります。
今回は「一般的な正規表現」についてまとめました。
参考になったでしょうか?
本記事がお役に立っていると嬉しいです!!