パターン: cat
catの部分にマッチ
cf. キーワード検索
キーワード: cat
catの単語にマッチ
| ^(キャレット) | 行の先頭 |
| $(ダラー) | 行の末尾 |
greyもしくはgrayにマッチさせたい。
長くなるので特別な理由が無い限り、以下、リテラルxは単にxと表記します
「詳説 正規表現」 p10から引用
文字クラスは、それ自体が小さな言語であると考えてほしい。どのメタ文字がサポートされているかという規則(とその振舞い)は、文字クラスの内部と外部とで完全に異なる。
| -(ダッシュ) | 文字クラスの中でのみメタ文字。ただし先頭で使うと非メタ文字 |
| ^(キャレット) | 文字クラスの先頭の場合はメタ文字。先頭以外で使うと非メタ文字 |
選択演算子 |(バー)の結合順位は低いので、通常丸カッコを併用する
greyもしくはgrayにマッチさせたい。
行頭のSubject または 行頭のFromにマッチさせたい場合は次のように書きます。
あるいは
つまり、colorとcolourの両方にマッチ
丸カッコで後置演算子の範囲を指定できます。
4にも4thにもマッチ
*と+は「正規表現のマッチ」(後述)を参照
部分パターンのより高度な利用。
$ perl -ne 'if (s/([0-9][0-9])x([0-9][0-9])/\\2x\\1/) {print;}'
12x89
89x12
$ perl -ne 'if (m/([0-9][0-9])x([0-9][0-9])/) {print "former is $1, latter is $2\\n";}'
12x89
former is 12, latter is 89
# 部分パターンの取り出し方はツール依存です。
丸カッコには独立した3つの用法があります。
$ perl -ne 'if (m/^(Subject|From)/) {print "found $1\\n";}'
From: me
found From
メタ文字にリテラルとしてマッチさせたい場合、\(バックスラッシュ)でエスケープします。
詳しくは「詳説 正規表現」p85もしくは各ツールのマニュアル参照。
| \w | 単語構成文字。多くの場合 [a-zA-Z0-9]または[a-zA-Z0-9\_] |
| \W | 非単語構成文字。\wを[^...]で反対の意味にしたもの |
| \s | 空白文字。多くの場合 [ \f\n\r\t\v] |
| \S | 非空白文字。\sを[^...]で反対の意味にしたもの |
| \d | 数字。多くの場合 [0-9] |
| \D | 非数字。多くの場合 [^0-9] |
POSIX(Portable Operating System Interface)
http://www.opengroup.org/certification/posix-home.html
POSIX(R) Certified by IEEE and The Open Group
Although coined to refer to the original IEEE standard 1003.1-1988, today POSIX(R) refers to a family or set of IEEE and ISO standards
| BRE | Basic Regular Expressions |
| ERE | Extended Regular Expressions |
教養としてだけ正規表現を知りたいなら、POSIX規格の正規表現を学んでください。
http://www.opengroup.org/austin/papers/posix_faq.html
The name POSIX was suggested by Richard Stallman. It is expected to be pronounced pahz-icks, as in positive, not poh-six, or other variations.
「詳説 正規表現」p66
「ドラゴンブック」 p110-112
| 演算 | 表記 | 定義 |
|---|---|---|
| LとMとの集合和 | L∪M | L∪M = {s|sはLまたはMの要素} |
| LとMとの連結 | LM | LM = {st|sはLの要素であり、tはMの要素} |
| Lのクリーネ閉包 | L* | Lの0回以上の出現 |
| Lの正の閉包 | L+ | Lの1回以上の出現 |
表記の前提: 正規表現rで表される言語(=記号列の集合)をL(r)と呼ぶことにします。
正規表現が表す言語を正規集合と呼びます。
「ドラゴンブック」p206
Q. 「なぜ字句解析は正規表現(lexの世界)で行い、構文解析は文脈自由文法(yaccの世界)なのか?」
A. 「正規表現の方が簡単だから」
$ echo toatob | perl -ne 'if (m/to(b?)/) {print "found $1\\n";}'
found
全体がマッチしますが、(b?)は部分マッチしません。
脳内正規表現エンジン
もっと良さそうに見える最後の tob にはマッチしません。
$ perl -ne 'if (m/^.*([0-9][0-9])/) {print "found $1\\n";}'
abc1abc23abc45xyz
found 45
脳内正規表現エンジン
$ perl -ne 'if (m/^.*([0-9][0-9])?/) {print "found $1\\n";}'
abc1abc23abc45xyz
found
(パターンはマッチしますが、丸カッコの部分マッチは無し)
脳内正規表現エンジン
$ perl -ne 'if (m/"(.*)"/) {print "found $1\\n";}'
"it looks working"
found it looks working
"am i good at perl?". "NO"
found "am i good at perl?". "NO" <===これは意図とは違うはず
常套手段のパターン: "([^"]*)"
$ perl -ne 'if (m/"([^"]*)"/) {print "found $1\\n";}'
"it looks working"
found it looks working
"am i good at perl?". "NO"
found am i good at perl? <===この場合は意図通り
$ perl -ne 'if (m/(\\.\\d\\d[1-9]?)\\d+/) {print "found $1\\n";}'
27.625
found .62
脳内正規表現エンジン
微妙。
正規表現のエンジンによって結果が異なります。
$ echo tonight | perl -ne 'if (m/to(ni|night)/) {print "found $1\\n";}'
found ni
$ echo tonight | perl -ne 'if (m/to(night|ni)/) {print "found $1\\n";}'
found night
Perl(従来型NFA)だとgreedyではありません。
DFAやPOSIX NFAでは上のどちらの場合でも night が部分マッチします。
$ echo tonight | perl -ne 'if (m/to(ni)*(ght)*/) {print "found $1, $2\\n";}'
found ni, ght
$ echo tonight | perl -ne 'if (m/to(ni)*(night)*/) {print "found $1, $2\\n";}'
found ni,
$ echo tonight | perl -ne 'if (m/to(ni)*(night)+/) {print "found $1, $2\\n";}'
found , night
バックトラックが肝。 簡易な実装例を見るのが一番分かりやすい。
NFAの形式論はDFAの説明と一緒に行います。
「プログラミング作法」 第9章 記法 の例を更に簡略化
/* textからregexpを検索
* サポートするメタ文字: .と* の2つのみ、*は直前の1文字の0回以上の繰り返し */
int match(const char *regexp, const char *text)
{
do {
if (matchhere(regexp, text)) {
return 1;
}
} while (*text++ != '\\0');
return 0;
}
/* textの先頭にあるregexpを検索 */
int matchhere(const char *regexp, const char *text)
{
if (regexp[0] == '\0') {
return 1;
}
if (regexp[1] == '*') {
return matchstar(regexp[0], regexp+2, text);
}
if (text[0] != '\\0' && (regexp[0] == '.' || regexp[0] == text[0])) {
return matchhere(regexp+1, text+1);
}
return 0;
}
/* c*regexpパターンをgreedy検索 */
int matchstar(int c, const char *regexp, const char *text)
{
const char *t;
for (t = text; *t != '\\0' && (*t == c || c == '.'); t++) {
; /* キャラクタcのマッチで行けるところまで行く */
}
do { /* バックトラック: 'c*'の後続の正規表現のマッチをバックトラックして検索 */
if (matchhere(regexp, t)) {
return 1;
}
} while (t-- > text);
return 0;
}
cf.
/* c*regexpパターンの非greedy検索 */
int matchstar(int c, const char *regexp, const char *text)
{
do {
if (matchhere(regexp, text)) {
return 1;
}
} while (text[0] != '\\0' && (*text++ == c || c == '.'));
return 0;
}
NFAもDFAもどちらも正規表現を認識可能。
すべてのNFAの状態を尽くすまで続ける
正規表現: (a|b)*abb の結果
| 状態 | a | b |
| A | B | C |
| B | B | D |
| C | B | C |
| D | B | E |
| E | B | C |
受理10のあるEが受理
DFAは事前に計算することで、テキストのサイズに対してNFAよりスケールする。
ほとんどのツールはNFA。
理由(予想)
最後はUnixっぽいかも。
もちろん、勝ち負けの世界ではありません。