Perl/正規表現

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

Perl > Perl/正規表現


Perlは強力な 正規表現 をサポートしています。正規表現とは、大まかにいうと、検索の機能を高度化しやすくしたものです。Perl以外のJavaやJavaSciptやPHPなども正規表現をサポートしていますが、Perlは歴史的な経緯により、比較的に古くから本Perlは正規表現を本格的にサポートしています。なので、正規表現につよいプログラミング言語といえばPerlが有名です。

正規表現とは[編集]

冒頭でも述べたように、

(再掲)正規表現とは、大まかにいうと、検索の機能を高度化しやすくしたものです。Perl以外のJavaやJavaSciptやPHPなども正規表現をサポートしていますが、Perlは歴史的な経緯により、比較的に古くから本Perlは正規表現を本格的にサポートしています。なので、正規表現につよいプログラミング言語といえばPerlが有名です。(以上、再掲)

何がどう「正規」なのかというと、学問の歴史的には、20世紀の中・後期から情報科学と言語学と応用数学との融合的な分野で「形式言語」という理論があり、機械で文字処理をできるようにしようとして、数学の集合論や群論などを活用した抽象的な概念がいろいろと考えだされた過去があり、ある条件を代数系を『正規』何とか などといったのですが、しかし21世紀の現代のプログラミング業界では、そういう過去の経緯は不要な知識なので、「正規」のことばの由来の説明は省略します。

形式言語の理論は、まだコンピュータやインターネットなどが21世紀の現代みたいに普及するよりもずっと前に、電子回路や半導体などにはあまり詳しくない学者の頭の中で考えられた用語や概念なので、いまさら「正規」が何かとかを暗記しても、現代ではあまり教育的な価値は無いからです。

なお、数学の形式言語などの学習書を読んでも(『オートマトン』や『形式言語』などの科目名の書籍に数学での正規表現について書いてある)、使用されている記号などが全くプログラミングの記号とは異なるので、あまりプログラミングには役立ちません。


基礎[編集]

マッチング[編集]

下記のコードは、(代入命令ではなく)論理式です。 このコードは $foo が 文字列 bar を含むかどうか真偽値を返します。

含まれていれば真を返し、含まれてなければ偽を返します。

コード例
if("Wikibooks" =~ /book/){
  print "Match!";
}else{
  print "Not match.";
}
実行結果
Match!

書式は

$検索対象の変数 =~ / 検索したい文字列/

です

検索したい文字列を、変数が内部に含んでいるかどうかを調べる際に、 =~ および / /</code を使います。

「/」はスラッシュ記号です。日本語キーボードなら「め」と「?」のあるボタンです。(バックスラッシュではない。)


上記のコードは真偽を返すので、主に条件分岐を行う際に用いられます。


=~ の結果を反転する演算子 !~ も存在します。

コード例
if("Wikibooks" !~ /books/){
  print "Not match.";
}else{
  print "Match!";
}


実行結果
Match!


/.../ 内ではダブルクォーテーション ("...") と同じく、変数やエスケープシーケンスが評価されます。

コード例
 $bar = "books";
 "Wikibooks" =~ /$bar/ # マッチする。

m を前置すると正規表現を囲う記号にスラッシュ以外を用いることができます。 [ ], ( ), < >, { } などの括弧は開き括弧と閉じ括弧が対応するように用います。

$foo =~ m/bar/;
$foo =~ m#bar#;
$foo =~ m@bar@;
$foo =~ m!bar!;
$foo =~ m{bar};
$foo =~ m(bar);

囲み記号にシングルクォーテーションを用いると、変数やエスケープシーケンスが評価されるのを防ぐことができます。

$foo = "books";
"Wikibooks" =~ m/$foo/; # マッチする
"Wikibooks" =~ m'$foo'; # マッチしない


ドット演算子[編集]

正規表現の中でのドット演算子は、任意の1文字以上の文字を意味します。なお、半角スペースも文字として扱います。

コード例
if("dog cat" =~ /..g/) {
  print "Match!";
}else{
  print "Not match.";
}
実行結果
Match!


たとえば「,,g」なら文字列 〇〇g となる単語(〇は任意)のことで、dogがこの条件を満たすのでマッチです。


いっぽう、下記のようにドットをもう一つ余計に増やすと、マッチしなくなります。なぜなら、gの前には2文字しかないから、です。

コード例
if("dog cat" =~ /...g/) {
  print "Match!";
}else{
  print "Not match.";
}
実行結果
Not Match.


dogの「g」の前には1文字以上の文字があるので、ドットが1つでもマッチします。

コード例
if("dog cat" =~ /.g/) {
  print "Match!";
}else{
  print "Not match.";
}
実行結果
Match!


メタキャラクタ[編集]

正規表現において特殊な意味を持つ以下の文字をメタキャラクタと呼びます。

+ * ? . ( ) [ ] { } | \ $ ^

これらの文字をあらわすには \+, \* のようにバックスラッシュでエスケープします。


アンカー[編集]

アンカーとは長さを持たない正規表現です。

代表的なものに、文字列先頭にマッチする ^、文字列末尾にマッチする $ があります。

書式
"Wikibooks" =~ /^Wiki/; # マッチする
"Wikibooks" =~ /books$/; # マッチする
"Wikibooks" =~ /Wiki$/; # マッチしない


コード例
if("Wikibooks" =~ /^Wiki/) {
  print "Match!";
}else{
  print "Not match.";
}
実行結果
Match!

文字列「Wiki」は検索対象の先頭にあるのでマッチします。

if("Wikibooks" =~ /^books/) {
  print "Match!";
}else{
  print "Not match.";
}
実行結果
Not Match.

文字列「books」は検索対象に含まれて居いますが、しかし先頭には無いのでマッチしないのです。


他に、単語の境界(正確には、単語の先頭あるいは末尾)にマッチする \b、それ以外の部分にマッチする\B があります。

つまり、その位置に半角スペースまたは単語の先頭あるいは終わりがある場合に、マッチすることになります。

書式
"dog cat"  =~ /a\b/; # マッチしない
"dog cat" =~ /g\b/; # マッチする


コード例
if("dog cat" =~ /g\b/) {
  print "Match!";
}else{
  print "Not match.";
}
実行結果
Match!

dogのgの後ろに半角スペースがあるので、結果はマッチです。


コード例
if("dog cat" =~ /a\b/) {
  print "Match!";
}else{
  print "Not match.";
}
実行結果
Not Match.

catのなかに「a」がありますが、位置が単語の境界ではないので、マッチしないです。


文字クラス[編集]

/[ ]/ のように、スラッシュの内側をブラケットで囲んだ部分は文字クラス(キャラクタクラス、character class)というものになります。

文字「クラス」というものの、C++言語などでいう『クラス』とは無関係です。

単に、複数の文字を指定する際に、ブラケット記号/[ ]/を使うだけです。

書式
"Wikibooks" =~ /[abc]/   # a と c は含まれないが、b は含まれるのでマッチする


コード例
if("Wikibooks" =~ /[abc]/){
  print "Match!";
}else{
  print "Not match.";
}
結果
Match!


  • 文字クラスのハイフン

/[ - ]/ のように、文字クラス/[ ]/の中においてハイフン (-) を用いることにより、文字の範囲を指定することができます。

たとえば、/[a-z]/ と書けば、英語の小文字にマッチします。(なお Perl の正規表現では、大文字と小文字を区別します。)

あるいは /[0-9]/ と書けば、十進数の数字にマッチします。


書式
$foo =~ /[abc]/;
$foo =~ /[a-c]/;   # 上の文と等価
$foo =~ /[01234]/;
$foo =~ /[0-4]/;   # 上の文と等価

ハイフンの前の文字は、後ろの文字よりも文字コードにおいて前でなければなりません。


書式
$foo =~ /[b-a]/; # エラー
$foo =~ /[5-3]/; # エラー
$foo =~ /[a-3]/; # エラー


コード例
if("Wikibooks" =~ /[a-c]/){
  print "Match!ggg";
}else{
  print "Not matchggg.";
}


結果
Match!


エスケープ

ハイフン自体を文字クラスに含めるには、文字クラスの一番前か一番後ろに記述するか、バックスラッシュでエスケープします。(なお、Windows環境では表示で、バックスラッシュの代わりに、通貨記号の円マークが表示されるかもしれません。)

書式
$foo =~ /[-ab]/;  # ハイフンまたは a か b にマッチする
$foo =~ /[ab-]/;  # 上の文と等価
$foo =~ /[a\-b]/; # 上の文と等価

開きブラケット ([) に ^ を後置すると、否定キャラクタクラスを表現することができます。

$foo =~ /[^0-9]/; # 数字以外にマッチする

グループ化[編集]

丸括弧で括った部分はグループ化されます。

グループ化した部分は後から参照することができます。これを後方参照といいます。 同じ正規表現内で後方参照を行うには、\1, \2... を用います。

 $foo =~ /(abc)\1/; # abc が2回連続する文字列にマッチする

また、正規表現外で後方参照を行うには、スカラー変数 $1, $2... を用います。

"Wikibooks" =~ /(wiki)/i;
print $1; # 'Wiki' と出力される。

選択[編集]

縦線を用いると正規表現を選択することができます。

$foo =~ /abc|def/; # abc あるいは def にマッチする
コード例
$foo = "defabc" ;
if($foo =~ /abc|def/){
  print "Match!";
} else {
  print "Not match.";
}
表示結果
Match!


下記の書式例では、^$| よりも優先順位が高いため、abc で始まる文字列か def で終わる文字列にマッチします。

コード例
$foo =~ /^abc|def$/;


コード例
$foo = "defabc" ;
if($foo =~ /^abc|def$/){
  print "Match!";
} else {
  print "Not match.";
}
表示結果
Not match.


コード例
$foo = "abcdef" ;
if($foo =~ /^abc|def$/){
  print "Match!";
} else {
  print "Not match.";
}
表示結果
Match!


先頭と末尾の両方に接する「"abc"」か 「"def"」だけにマッチさせるには以下のようにします。

# カッコで両項ともククる方式
$foo =~ /^(abc|def)$/;

# あるいは各項に展開する方式
$foo =~ /^abc$|^def$/;


コード例
$foo = "defabc" ;
if($foo =~ /^abc$|^def$/){
  print "Match!";
} else {
  print "Not match.";
}
表示結果
Not match.
コード例
$foo = "def" ;
if($foo =~ /^abc$|^def$/){
  print "Match!";
} else {
  print "Not match.";
}
表示結果
Match!


コード例
$foo = "def555" ;
if($foo =~ /^abc$|^def$/){
  print "Match!";
} else {
  print "Not match.";
}
表示結果
Not match.

置き換え[編集]

s///演算子を用いると、文字列の置換を行うことができます。

$str = "Wikibooks";
$str =~ s/books/pedia/; # $str は "Wikipedia" になる

m//と同じく、スラッシュ以外の記号を用いることもできます。

<pre><nowiki>$foo =~ s/foo/bar/;
$foo =~ s#foo#bar#;
$foo =~ s@foo@bar@;
$foo =~ s!foo!bar!;
$foo =~ s{foo}{bar};
$foo =~ s(foo)(bar);</nowiki></pre>

修飾子[編集]

正規表現のメタキャラクタ、あるいはパターンマッチそのものの振る舞いを変えるために、修飾子を指定することができます。たとえば、正規表現がアルファベットの大文字小文字を区別せずにマッチするようにするためには、

m/^perl$/i; # perlやPerl、PERL、PeRl、pErLなどにマッチする。

のように、最後のスラッシュ(あるいは何らかの記号)の後に、i修飾子を付加します。

Perlの正規表現の修飾子
i 大文字小文字の同一視 (case-insensitive)
s 「.」が改行にもマッチするようにする (single line)
m 複数行として扱う (multi-line)
x 拡張正規表現を使う (extended)
e Perlのコードとして評価する (evaluation)
ee Perlのコードとして2回評価する (evaluation and evaluation)
g 連続して何回もマッチ (global)
o 一度だけコンパイルする (only once)

拡張正規表現[編集]

x修飾子を付けると正規表現内の空白や改行が無視され、「#」以降はコメントとして扱われます。

# 1と出力
print 1 if "Apple" =~ /
A
p
p
l
e
/x;

アトムとアサーション[編集]

「ABC」や「[0-9]」、「.*?」のように、何かにマッチする正規表現の構文をアトムといいます。最後の「.*?」は、アトム「.」に量指定子「*」、「?」が付いたもので、特に量指定子付きアトムといいます。

「^」や「$」、「|」のように、何かにマッチするわけではない正規表現の構文をアサーションといいます。

正規表現の構文は基本的にアトムとアサーションのどちらかに分けられます。ただし、\Qや\E、\uや\Uのような特殊なシーケンスはアトムでもアサーションでもありません。これらは構文のふるまいを変えるものです。

拡張構文[編集]

コメント[編集]

/(?#ここはコメント)/

クラスタ化専用カッコ[編集]

(?:...)はクラスタ化(正規表現をまとめること)のみに使われるカッコです。キャプチャを行わないため、マッチした部分を正規表現の中で\1、\2のように参照したり、後から$1、$2のような変数で参照したりすることができません。キャプチャを行う必要がない場合は、このカッコを使うことで効率化を図ることができます。

"Apple" =~ /^(?:Apple|Banana|Cherry)$/; # AppleかBananaかCherryにマッチ

これにはimsx修飾子を付けることもできます。i修飾子を付けるには、i-msx(iを指定しmsxを指定しない)とします。

"aPpLe" =~ /^(?i-msx:Apple|Banana|Cherry)$/;

単に修飾子を有効または無効にするためだけにこのカッコを使うこともできます。

/A(?i-msx)B/; # Aは大文字小文字を区別するが、Bは区別しない

ルックアラウンドアサーション[編集]

ルックアラウンドアサーションとは、直後または直前にパターンが出現すること、あるいは出現しないことを確認し、確認するだけで何にもマッチしないアサーションです。

  • 肯定先読み

直後にPATTERNが出現することを確認します。

/(?=PATTERN)/
  • 否定先読み

直後にPATTERNが出現しないことを確認します。

/(?!PATTERN)/

次の例では、「&amp;」以外の「&」をすべて「&amp;」に置換します。

$str =~ s/&(?!amp;)/&amp;/g;
  • 肯定後読み

直前にPATTERNが出現することを確認します。

/(?<=PATTERN)/
  • 否定後読み

直前にPATTERNが出現しないことを確認します。

/(?<!PATTERN)/

非バックトラックサブパターン[編集]

バックトラックしないPATTRENにのみマッチします。

(?>PATTERN)

コードサブパターン[編集]

(?{ CODE })という形で、正規表現の中にPerlのコードを埋め込むことができます。

/(?{ print "Hello, world!\n" })/; # Hello, world!と表示

(??{ CODE })という形では、CODEを評価した結果得られた正規表現にマッチします。

"ABC" =~ /^(??{ "A"."B"."C" })$/; # ABCにマッチする

条件付き展開[編集]

Perlの条件演算子?:のように、条件が真か偽かでマッチさせるパターンを変えることができます。

/(?(COND)TRUE|FALSE)/

または

/(?(COND)TRUE)/

CONDが真の場合はTRUE、偽の場合はFALSEのパターンにマッチします。


変換[編集]

このページ「Perl/正規表現」は、まだ書きかけです。加筆・訂正など、協力いただける皆様の編集を心からお待ちしております。また、ご意見などがありましたら、お気軽にトークページへどうぞ。