第7章 正規表現

7-1. パターンマッチ演算子

正規表現とは、ある特定の文字列ではなく、文字列の一部をパターン化して表現する手法をいいます。
複数行ある文章の中から文字列を検索する時に、検索したい文字列すべてを指定するのではなく、文字列の任意の一部を置き換え可能な状態にして検索をする場合に正規表現を用います。 たとえば、UNIXやWindowsでは、文字列を検索するときに使用される正規表現として、「*」を任意の文字列(空き文字も含む)に、「?」を任意の1文字として使用することができます。パターンを、app* と表現すると、 apple application appeal などの文字列がマッチすることになります。
この正規表現の方法は、当然ながらプログラム言語ごとに異なりますが、Perlの場合は他の言語に比べて非常に強力な正規表現によるパターンマッチ機能を備えており、より詳細にかつ容易にパターンマッチを行うことができるとされています。
Perlでは、パターンマッチ演算子として、スラッシュ ( / ) で囲んだ文字列が正規表現であるとされます。 性格上、正規表現は条件式と併用して用いられることが多いようです。この場合次のような構文があります。
構文 内容
if ( 文字列 =~ /パターン/) もし「文字列」の中に「パターン」が含まれていれば
(パターンマッチすれば真)
if ( 文字列 !~ /パターン/) もし「文字列」の中に「パターン」が含まれていなければ
(パターンマッチすれば偽)
$word = 'apple';

if ($word =~ /a/) {
	print "含まれています。\n";
} else {
	print "含まれていません。\n";
}
>  含まれています。
また、上記構文の省略形の位置づけとして、次の構文があります。
この場合、対象となる「文字列」は特殊変数 $_ が充てられます。
構文 内容
if (/パターン/) もし変数 $_ の中に「パターン」が含まれていれば
(パターンマッチすれば真)
if (!/パターン/) もし変数 $_ の中に「パターン」が含まれていなければ
(パターンマッチすれば偽)
$_ = 'apple';

if (/a/) {
	print "含まれています。\n";
} else {
	print "含まれていません。\n";
}
>  含まれています。
正規表現は通常スラッシュ ( / ) で囲んだ部分がパターンマッチの対象となりますが、このスラッシュを他の任意の記号に置き換えることもできます。
パターンの中に「スラッシュ」が多く含まれる場合に、正規表現を書きやすくするための措置と思われます。
構文 内容
m [区切文字] パターン [区切文字] /パターン/ に同じ
※「区切文字」とは、空白文字以外の記号を使用することができます。 (例: @, #, *, |, #, {}, [], () など)
$url = 'http://www.kent-web.com/';

if ($url =~ m|http://|) {
	print "含まれています。\n";
} else {
	print "含まれていません。\n";
}
>  含まれています。
パターンマッチ演算子には、正規表現を行う際のオプションとして、いくつかの修飾子が用意されています。
修飾子 内容
g 繰り返しマッチする (global)
i 大文字と小文字の区別をしない (case-insensitive)
m 文字列を複数行として扱う (multi-line)
o 変数展開を1度だけ行う (only once)
s 文字列を単一行として扱う (single line)
x 拡張正規表現を行う (extended)
コード例-1 (i修飾子の例)
$word = 'apple';

if ($word =~ /A/) {
	print "マッチします。\n";
} else {
	print "マッチしません。\n";
}
>  マッチしません。
$word = 'apple';

if ($word =~ /A/i) {
	print "マッチします。\n";
} else {
	print "マッチしません。\n";
}
>  マッチします。
コード例-2 (m修飾子の例)
$word = "AAA\nBBB\n";
if ($word =~ /^B/) {
	print "マッチします。\n";
} else {
	print "マッチしません。\n";
}
>  マッチしません。
$word = "AAA\nBBB\n";

if ($word =~ /^B/m) {
	print "マッチします。\n";
} else {
	print "マッチしません。\n";
}
>  マッチします。 
pagetop

7-2. メタ文字

正規表現を行ううえで、次のようなメタ文字と呼ばれる特殊な意味を持つ文字には、そのままではマッチできないため、必ず直前に \ を付加してエスケープする必要があります。
\ ^ . $ * ? | ( ) [ ] { }
メタ文字を「文字」としてマッチさせるためには、直前に \ を付ける
$price = 'This is $100';

if ($price =~ /\$/) {
	print "含まれています。\n";
} else {
	print "含まれていません。\n";
}
>  含まれています。 
メタ文字の正規表現の中での使い方としては、次にようなものがあります。
1. 任意の1文字と位置指定
メタ文字 正規表現上の意味
\ 直後の文字をエスケープ
^ 先頭にマッチ
$ 末尾にマッチ
. 改行を除く任意の1文字
コード例-1(^ の使用例)
$word = 'apple';

if ($word =~ /^a/) {
	print "マッチします。\n";
} else {
	print "マッチしません。\n";
}
>  マッチします。
コード例-2(. の使用例)
$word = 'apple';

if ($word =~ /app.e/) {
	print "マッチします。\n";
} else {
	print "マッチしません。\n";
}
>  マッチします。
コード例-3($ の使用例)
$word = 'apple';

if ($word =~ /e$/) {
	print "マッチします。\n";
} else {
	print "マッチしません。\n";
}
>  マッチします。
2. パターン論理和
メタ文字 正規表現上の意味
| 選択
複数の文字列をパイプで分割すると、複数の文字列のどれかにマッチさせます。
$word = 'apple';

if ($word =~ /a|b|c/) {
	print "マッチします。\n";
} else {
	print "マッチしません。\n";
}
>  マッチします。
3. パターングループ
メタ文字 正規表現上の意味
( ) グループ化
複数の文字列をまとめた形でパターンマッチを行います。
またこのパターングループでマッチした文字列は、特殊変数 ( $ + 数字 ) に、マッチした順に代入することができます。( ex. $1, $2, $3, ... )
$word = 'apple';

if ($word =~ /^(...)(.)(.)/) {
	print "$1\n";
	print "$2\n";
	print "$3\n";
}
>  app
>  l
>  e
4. 文字クラス
メタ文字 正規表現上の意味
[ ] 指定内の任意表現
マッチングの対象となる文字列中に、[ ] (ブラケット)で囲んだ文字のいずれかをマッチさせるため表現です。
このとき、ブラケット中の先頭部に、「 ^ 」(キャレット)があれば、逆の意味となり、「含まれていない」文字にマッチするという意味になります。
また、ブラケット中に「 - 」(マイナス)があれば、範囲指定を意味します。
$word = 'apple';

if ($word =~ /[a-z]/) {
	print "英小文字が含まれています。\n";
} else {
	print "英小文字は含まれていません。\n";
}

if ($word =~ /[0-9]/) {
	print "数字が含まれています。\n";
} else {
	print "数字は含まれていません。\n";
}
>  英小文字が含まれています。
>  数字は含まれていません。 
文字クラスで多用されるものとして、次のような表現があります。
文字クラス 内容
[a-z] 英小文字のいずれか1文字
[A-Z] 英大文字のいずれか1文字
[0-9] 数字のいずれか1文字
[a-zA-Z0-9] 英数字のいずれか1文字
[^a-zA-Z] 英字以外にマッチ
[^0-9] 数字以外にマッチ
5. 量指定子
メタ文字 正規表現上の意味
* 直前の文字を0回以上にマッチ
+ 直前の文字を1回以上にマッチ
? 直前の文字を0回又は1回にマッチ
{n} 直前の文字をn回にマッチ
{n,} 直前の文字をn回以上にマッチ
{n,m} 直前の文字をn回以上、m回以下にマッチ
  { } の使用例
$word = 'apple';

if ($word =~ /[a-z]{5}/) {
	print "マッチします。\n";
} else {
	print "マッチしません。\n";
}
> マッチします。
量指定子の正規表現を行う場合、マッチする範囲は最大マッチであることに注意しましょう。
まず以下の例を見てみます。
  コード例1
$word = 'apple:1 banana:3 orange:2';
if ($word =~ /(.+[0-9])/) { print "$1\n"; }
>  apple:1 banana:3 orange:2
上記は「数字の前に1個以上の何らかの文字列」がある場合にマッチさせるパターンです。
この場合、変数$wordの文字列はすべてマッチしてしまいます。 要は、末尾の数字の2の前に1個以上の文字列があるためです。
「数字の前に1個以上の何らかの文字列」という条件であれば、「apple:1」や「apple:1 banana:3」という文字列でもあてはまりますが、このように最大の範囲内で条件にあてはまる部分がマッチするという取り決めになっています。
そこで、最短マッチにする場合のオプションとして、量指定子の直後に ? (クエスチョン) をつけることでそれが可能となります。
  コード例2
$word = 'apple:1 banana:3 orange:2';
if ($word =~ /(.+?[0-9])/) { print "$1\n"; }
>  apple:1 
6. その他のメタ文字及び特殊変数
これまで説明したもの以外で、正規表現上の特殊な意味として用いられるものには次のようなものがあります。
表現 正規表現上の意味
\w 英字、数字、アンダースコア。[a-zA-Z0-9_] に同じ。
\W 英字、数字、アンダースコア以外の文字。[^a-zA-Z0-9_] に同じ。
\d 数字。[0-9] に同じ。
\D 数字以外の文字。[^0-9] に同じ。
\t タブ
\r リターン(復帰文字)
\n 改行
\f ラインフィード(改ページ)
\s スペース。[\r\t\n\f] に同じ。
\S スペース以外の文字。[^\r\t\n\f] に同じ。
\a アラーム(ベル)
\b バックスペース
\e エスケープ
\0 + 数字 8進法で表すASCII文字。( ex. \033, \040 など )
\x + 英数字 16進法で表すASCII文字。( ex. \x1b, \x00 など )
\c[ コントロール文字
\l 次の1文字を小文字にする
\u 次の1文字を大文字にする
\L \Eまでの文字列を小文字にする
\U \Eまでの文字列を大文字にする
\E 変更の終わり
\Q \Eまでの文字列で正規表現のメタ文字を文字にみなす
\b 単語の境界にマッチする
\B 単語の境界以外にマッチする
\A 文字列の最初にマッチする。メタ文字 ^ に同じ。
\Z 文字列の最後にマッチする。メタ文字 $ に同じ。
$ + 数字 グループ化したパターンを後で参照する。( ex. $1, $2, $3, ... )
\ + 数字 上記に同じ。( ex. \1, \2, \3, ... )
$& マッチした文字列全体
$` マッチした文字列の前にあるすべての文字列
$' マッチした文字列の後にあるすべての文字列
pagetop

7-3. 置換演算子

正規表現を使用し、マッチした文字列を置き換える置換演算子があります。
構文 内容
s/パターン/置換文字列/ 「パターン」にマッチする文字列を「置換文字列」に置き換える
$word = "This is a pen.";

$word =~ s/pen/book/;
print "$word\n";
>  This is a book. 
置換演算子は省略形として、左辺を省略することもできます。この場合、左辺は 特殊変数 $_ が充てられます。
$_ = "This is a pen.";

s/pen/book/;
print "$_\n";
>  This is a book. 
パターンを囲む区切り文字のスラッシュを、任意の区切り文字 (ex. @, #, *, | など)に変更することができます。
この場合、パターンマッチ演算子とは異なり、先頭の mオプションを付けることはありません。
$url = 'http://www.kent-web.com/bbs/file/';

$url =~ s|bbs/file|chat/data|;
print "$url\n";
>  http://www.kent-web.com/chat/data/
置換演算子には、置換えを行う際のオプションとして、次の修飾子が用意されています。
修飾子 内容
g 繰り返しマッチする (global)
i 大文字と小文字の区別をしない (case-insensitive)
m 文字列を複数行として扱う (multi-line)
o 変数展開を1度だけ行う (only once)
s 文字列を単一行として扱う (single line)
x 拡張正規表現を行う (extended)
e 置換文字列を「式」と見なす (evaluation)
コード例-1(g修飾子の例)
$word = 'aaaa';
$word =~ s/a/A/;
print "$word\n"; 
>  Aaaa
$word = 'aaaa';
$word =~ s/a/A/g;
print "$word\n"; 
>  AAAA
コード例-2(e修飾子の例)
$word = "this is 100 yen.";
$word =~ s/(\d+)/$1 * 2/e;
print "$word\n";
>  this is 200 yen.
pagetop

7-4. 変換演算子

変換演算子は文字列を1文字ずつ順にスキャンして、対象文字列に含まれるすべての文字を変換して、変換した文字数を返す演算子です。
変換演算子は正規表現を使わないので、厳密には正規表現の対象にはならない演算子ではあります。
構文 内容
tr/変換対象の文字/変換後の文字/ 「変換対象の文字」をスキャンして「変換後の文字」にすべて変換する
y/変換対象の文字/変換後の文字/ 上に同じ。
コード例-1 (大文字に変換する)
$word = 'apple';
$word =~ tr/a-z/A-Z/;
print "$word\n";
>  APPLE 
コード例-2 (文字の個数を数える)
$word = 'apple';
$count = $word =~ tr/a-z/A-Z/;
print "$count\n";
>  5(変換した文字数が5個のため) 
変換演算子にも修飾子が次のとおり用意されています。
修飾子 内容
c 対象の文字に含まれないものを変換する
d 変換する文字に含まれないものを削除する
s 変換の結果、同じ文字が連続したものを1文字とする
コード例-1 (c修飾子)
$word = 'American Coffee';
$word =~ tr/A-Z/_/c; # 大文字以外を _ に変換
print "$word\n";
>  A________C_____
コード例-2 (d修飾子)
$word = 'American Coffee';
$word =~ tr/a-z//d; # 小文字を削除
print "$word\n";
>  A C
コード例-3 (s修飾子)
$word = 'apple';
$word =~ tr/a-z/A-Z/s; # 大文字に変換し、連続した同一文字を1文字に変換
print "$word\n";
>  APLE
pagetop

7-5. 正規表現を利用する関数

正規表現を利用する関数として、split関数 があります。
また、split関数の逆変換を行うものとして、join関数 がありますので、併せて使い方を認識しておきましょう(join関数は正規表現は使用しません)。
コード例-1 (split関数)
$word = "apple:banana:orange";
@word = split(/:/, $word); # : を区切り文字として「文字列」を「配列」にする

print "@word\n";
>  apple banana orange 
コード例-2 (join関数)
@word = ("apple", "banana", "orange");
$word = join(":", @word); # : を区切り文字として「配列」を「文字列」にする

print "$word\n";
>  apple:banana:orange 
pagetop