正規表現がよくわかりません。 わかりやすく説明してもらえますか? また、オンラインツールや書籍があれば、そちらへのリンクもお願いします。
最も重要なのはコンセプトだ。構成要素がどのように機能するかを理解すれば、構文の違いは軽い方言にすぎない。正規表現エンジンのシンタックスの上には、使用しているプログラミング言語のシンタックスがあります。Perlのような言語ではこの複雑さはほとんどなくなりますが、C言語で正規表現を使用する場合は、他の考慮事項を念頭に置く必要があります。 正規表現を好きなように組み合わせられるブロックのようなものだと考えれば、自分でパターンを書いたりデバッグしたりする方法だけでなく、他の人が書いたパターンを理解する方法も学ぶことができます。
概念的に、最も単純な正規表現はリテラル文字です。パターン N は文字 'N' にマッチします。 正規表現が隣り合っている場合は、連続した文字列にマッチします。例えば、パターン Nick は 'N'の後に 'i'が続き、その後に 'c'が続き、その後に 'k'が続くシーケンスにマッチします。 Unix上でgrepを使ったことがある人なら、たとえ普通の文字列を検索するためであったとしても、すでに正規表現を使っていることになる!(grep の re は正規表現のことです)。
N
Nick
grep
re
少し複雑になりますが、'Nick'と'nick'のどちらかを [Nn]ick というパターンでマッチさせることができます。角括弧で囲まれた部分は文字クラスであり、囲まれた文字の1つだけにマッチします。a-c]は 'a'か 'b'か 'c'のいずれかにマッチします。 パターン.は特殊で、リテラルドットにのみマッチするのではなく、*あらゆる*文字にマッチします†。これは、本当に大きな文字クラス[-.?+%$A-Za-z0-9...]` と概念的には同じです。 文字クラスはメニューのようなものだと考えてください。
[Nn]ick
は 'a'か 'b'か 'c'のいずれかにマッチします。 パターン
は特殊で、リテラルドットにのみマッチするのではなく、*あらゆる*文字にマッチします†。これは、本当に大きな文字クラス
.を使えば入力の手間を省くことができる。例えば、数字をマッチさせたい場合、[0-9]と書くことができます。桁は頻繁にマッチする対象なので、代わりにdというショートカットを使うことができる。その他に、s(空白)やw(単語文字: 英数字やアンダースコア)がある。 大文字の変種はその補語なので、例えばS` は空白以外の文字にマッチする。
を使えば入力の手間を省くことができる。例えば、数字をマッチさせたい場合、
と書くことができます。桁は頻繁にマッチする対象なので、代わりに
というショートカットを使うことができる。その他に、
(空白)や
(単語文字: 英数字やアンダースコア)がある。 大文字の変種はその補語なので、例えば
そこから、パターンの一部を 数量子 で繰り返すことができる。例えば、パターン ab?c は 'abc' または 'ac' にマッチする。他の量化子は
ab?c
*
+
{n}
{n,}
{n,m}
[Nn]*ick
(と同等の
量化子はそのすぐ左のパターンを変更する。0abc+0は '0abc0'、 '0abcabc0'などにマッチすると思うかもしれないが、プラス量 子のすぐ左のパターンはcである。つまり、0abc+0は '0abc0'、 '0abcc0'、 '0abccc0'などにマッチする。 末尾にゼロを含む1つ以上の 'abc' のシーケンスにマッチするには、0(abc)+0を使用します。括弧は単位として定量化できるサブパターンを示す。また、正規表現エンジンでは、入力テキストのうち括弧で囲まれたグループにマッチする部分を保存またはキャプチャするのが一般的です。この方法でビットを抽出することは、インデックスやsubstr`をカウントするよりもはるかに柔軟で、エラーが発生しにくい。
は '0abc0'、 '0abcabc0'などにマッチすると思うかもしれないが、プラス量 子のすぐ左のパターンは
である。つまり、
は '0abc0'、 '0abcc0'、 '0abccc0'などにマッチする。 末尾にゼロを含む1つ以上の 'abc' のシーケンスにマッチするには、
を使用します。括弧は単位として定量化できるサブパターンを示す。また、正規表現エンジンでは、入力テキストのうち括弧で囲まれたグループにマッチする部分を保存またはキャプチャするのが一般的です。この方法でビットを抽出することは、インデックスや
先ほど、'Nick'と'nick'のどちらかにマッチする方法を見ました。もうひとつは、Nick|nickのように交互に並べる方法です。省略形には左側と右側のすべてが含まれることを覚えておいてください。例えば、*、(Nick|nick)のように、|の範囲を限定するために括弧を使用します。 別の例として、[a-c]をa|b|cと書くこともできますが、これは最適ではありません。
Nick|nick
(Nick|nick)
|
[a-c]
a|b|c
それ自体がマッチする文字もあるが、特別な意味を持つ文字もある。パターン d+ は、バックスラッシュの後に小文字のDが続き、その後にプラス記号が続く場合 にはマッチしない。バックスラッシュは以下の文字から特別な意味を取り除く。
d+
正規表現の量化子は貪欲である。つまり、パターン全体がうまくマッチするようにしながら、可能な限り多くのテキストにマッチする。 例えば、入力が こんにちは、お元気ですか? あなたは、".+"が 'Hello,'だけにマッチすると思っているかもしれないが、それが 'Hello'から 'you?'まですべてにマッチしたのを見て驚くだろう。 貪欲なものから慎重なものへと切り替えるには、量化詞に?これで、質問の例である((.+?))がどのように動作するか理解できたでしょう。これはリテラル左括弧の後に1つ以上の文字が続き、右括弧で終わるシーケンスにマッチします。 入力が '(123) (456)' の場合、最初のキャプチャは '123' になります。非貪欲な量化子は、パターンの残りの部分ができるだけ早くマッチングを開始するようにしたいのです。 (あなたの混乱についてですが、私は((.+?))`が同じことをする正規表現方言を知りません。どこかの段階で何かが失われてしまったのだろう)。
".+"
?これで、質問の例である
がどのように動作するか理解できたでしょう。これはリテラル左括弧の後に1つ以上の文字が続き、右括弧で終わるシーケンスにマッチします。 入力が '(123) (456)' の場合、最初のキャプチャは '123' になります。非貪欲な量化子は、パターンの残りの部分ができるだけ早くマッチングを開始するようにしたいのです。 (あなたの混乱についてですが、私は
特別なパターン ^ を使って入力の先頭だけにマッチさせ、 $ を使って入力の末尾だけにマッチさせます。quot;ブックエンド("bookends")をパターンで作って、前と後ろに何があるかはわかるけど、その間を全部教えてね"と言うのは便利なテクニックです。 例えば、次のようなコメントをマッチさせたいとします。 これはコメントです。 と書けばよい。
^
$
正規表現は再帰的なので、これらの基本的なルールを理解した後は、好きなように組み合わせることができます。
RegExr]1 (JavaScript用)
Perl用:YAPE: Regex Explain (JavaScript用)
Regex Coach]3 (CL-PPCRE に支えられたエンジン)
RegexPal]4 (JavaScript用)
正規表現オンラインテスター][5
正規表現バディ]6。
Regex 101]7 (PCRE、JavaScript、Python、Golang用)
Visual RegExp][8
Expresso]9(.NET用
Rubular]10 (Ruby用)
正規表現ライブラリ]11 (一般的なシナリオのための定義済み正規表現)
Txt2RE][12
Regex Tester]13 (JavaScript用)
Regex Storm]14 (.NET用)
Debuggex]15 (ビジュアル正規表現テスターおよびヘルパー)
マスタリング正規表現]16、第2版、第3版。
正規表現チートシート]19。
正規表現クックブック]20。
Teach Yourself 正規表現]21。
RegexOne - シンプルでインタラクティブな演習で学ぶ]22。
正規表現-知っておくべきすべて]23 (PDFシリーズ)
正規表現の構文まとめ]24。
正規表現の仕組み]25。
†: .はどのような文字にもマッチするという上記の記述は、厳密には正 しくないが、教育的な目的のために単純化したものである。ドットは改行を除くすべての文字にマッチしますが、実際には .+ のようなパターンが改行の境界を越えることはほとんどありません。Perlの正規表現には/sスイッチがあり、JavaのPattern.DOTALLなどは、.を任意の文字にマッチさせることができます。このような機能がない言語では、[˶'sS]のようなものを使えば、空白文字でも空白文字以外でも、つまり何でもマッチさせることができます。
.
.+
/s
Pattern.DOTALL
[˶'sS]
最も重要なのはコンセプトだ。構成要素がどのように機能するかを理解すれば、構文の違いは軽い方言にすぎない。正規表現エンジンのシンタックスの上には、使用しているプログラミング言語のシンタックスがあります。Perlのような言語ではこの複雑さはほとんどなくなりますが、C言語で正規表現を使用する場合は、他の考慮事項を念頭に置く必要があります。 正規表現を好きなように組み合わせられるブロックのようなものだと考えれば、自分でパターンを書いたりデバッグしたりする方法だけでなく、他の人が書いたパターンを理解する方法も学ぶことができます。
シンプルに始める
概念的に、最も単純な正規表現はリテラル文字です。パターン
N
は文字 'N' にマッチします。 正規表現が隣り合っている場合は、連続した文字列にマッチします。例えば、パターンNick
は 'N'の後に 'i'が続き、その後に 'c'が続き、その後に 'k'が続くシーケンスにマッチします。 Unix上でgrep
を使ったことがある人なら、たとえ普通の文字列を検索するためであったとしても、すでに正規表現を使っていることになる!(grep
のre
は正規表現のことです)。メニューからの注文
少し複雑になりますが、'Nick'と'nick'のどちらかを
[Nn]ick
というパターンでマッチさせることができます。角括弧で囲まれた部分は文字クラスであり、囲まれた文字の1つだけにマッチします。a-c]は 'a'か 'b'か 'c'のいずれかにマッチします。 パターン
.は特殊で、リテラルドットにのみマッチするのではなく、*あらゆる*文字にマッチします†。これは、本当に大きな文字クラス
[-.?+%$A-Za-z0-9...]` と概念的には同じです。 文字クラスはメニューのようなものだと考えてください。役立つショートカット
.
を使えば入力の手間を省くことができる。例えば、数字をマッチさせたい場合、
[0-9]と書くことができます。桁は頻繁にマッチする対象なので、代わりに
dというショートカットを使うことができる。その他に、
s(空白)や
w(単語文字: 英数字やアンダースコア)がある。 大文字の変種はその補語なので、例えば
S` は空白以外の文字にマッチする。一度だけでは十分ではない
そこから、パターンの一部を 数量子 で繰り返すことができる。例えば、パターン
ab?c
は 'abc' または 'ac' にマッチする。他の量化子は*
(0回以上)+
(1回以上){n}
(ちょうどn回){n,}
(少なくとも n 回){n,m}
(少なくともn回、m回以下) これらのブロックをいくつか組み合わせると、パターン[Nn]*ick
は以下の全てにマッチする。(と同等の
dd+`)は任意の非負整数にマッチする。グループ化
量化子はそのすぐ左のパターンを変更する。0abc+0
は '0abc0'、 '0abcabc0'などにマッチすると思うかもしれないが、プラス量 子のすぐ左のパターンは
cである。つまり、
0abc+0は '0abc0'、 '0abcc0'、 '0abccc0'などにマッチする。 末尾にゼロを含む1つ以上の 'abc' のシーケンスにマッチするには、
0(abc)+0を使用します。括弧は単位として定量化できるサブパターンを示す。また、正規表現エンジンでは、入力テキストのうち括弧で囲まれたグループにマッチする部分を保存またはキャプチャするのが一般的です。この方法でビットを抽出することは、インデックスや
substr`をカウントするよりもはるかに柔軟で、エラーが発生しにくい。代替
先ほど、'Nick'と'nick'のどちらかにマッチする方法を見ました。もうひとつは、
Nick|nick
のように交互に並べる方法です。省略形には左側と右側のすべてが含まれることを覚えておいてください。例えば、*、(Nick|nick)
のように、|
の範囲を限定するために括弧を使用します。 別の例として、[a-c]
をa|b|c
と書くこともできますが、これは最適ではありません。エスケープ
それ自体がマッチする文字もあるが、特別な意味を持つ文字もある。パターン
d+
は、バックスラッシュの後に小文字のDが続き、その後にプラス記号が続く場合 にはマッチしない。バックスラッシュは以下の文字から特別な意味を取り除く。欲張り
正規表現の量化子は貪欲である。つまり、パターン全体がうまくマッチするようにしながら、可能な限り多くのテキストにマッチする。 例えば、入力が こんにちは、お元気ですか? あなたは、
".+"
が 'Hello,'だけにマッチすると思っているかもしれないが、それが 'Hello'から 'you?'まですべてにマッチしたのを見て驚くだろう。 貪欲なものから慎重なものへと切り替えるには、量化詞に?これで、質問の例である
((.+?))がどのように動作するか理解できたでしょう。これはリテラル左括弧の後に1つ以上の文字が続き、右括弧で終わるシーケンスにマッチします。 入力が '(123) (456)' の場合、最初のキャプチャは '123' になります。非貪欲な量化子は、パターンの残りの部分ができるだけ早くマッチングを開始するようにしたいのです。 (あなたの混乱についてですが、私は
((.+?))`が同じことをする正規表現方言を知りません。どこかの段階で何かが失われてしまったのだろう)。アンカー
特別なパターン
^
を使って入力の先頭だけにマッチさせ、$
を使って入力の末尾だけにマッチさせます。quot;ブックエンド("bookends")をパターンで作って、前と後ろに何があるかはわかるけど、その間を全部教えてね"と言うのは便利なテクニックです。 例えば、次のようなコメントをマッチさせたいとします。 これはコメントです。 と書けばよい。自分で作る
正規表現は再帰的なので、これらの基本的なルールを理解した後は、好きなように組み合わせることができます。
正規表現を書いたりデバッグしたりするためのツール:
RegExr]1 (JavaScript用)
Perl用:YAPE: Regex Explain (JavaScript用)
Regex Coach]3 (CL-PPCRE に支えられたエンジン)
RegexPal]4 (JavaScript用)
正規表現オンラインテスター][5
正規表現バディ]6。
Regex 101]7 (PCRE、JavaScript、Python、Golang用)
Visual RegExp][8
Expresso]9(.NET用
Rubular]10 (Ruby用)
正規表現ライブラリ]11 (一般的なシナリオのための定義済み正規表現)
Txt2RE][12
Regex Tester]13 (JavaScript用)
Regex Storm]14 (.NET用)
Debuggex]15 (ビジュアル正規表現テスターおよびヘルパー)
書籍
マスタリング正規表現]16、第2版、第3版。
正規表現チートシート]19。
正規表現クックブック]20。
Teach Yourself 正規表現]21。
無料リソース
RegexOne - シンプルでインタラクティブな演習で学ぶ]22。
正規表現-知っておくべきすべて]23 (PDFシリーズ)
正規表現の構文まとめ]24。
正規表現の仕組み]25。
脚注
†:
.
はどのような文字にもマッチするという上記の記述は、厳密には正 しくないが、教育的な目的のために単純化したものである。ドットは改行を除くすべての文字にマッチしますが、実際には.+
のようなパターンが改行の境界を越えることはほとんどありません。Perlの正規表現には/s
スイッチがあり、JavaのPattern.DOTALL
などは、.
を任意の文字にマッチさせることができます。このような機能がない言語では、[˶'sS]
のようなものを使えば、空白文字でも空白文字以外でも、つまり何でもマッチさせることができます。