1. Perl
  2. 正規表現

Perlの正規表現をマスターしよう

Perlの正規表現の解説です。この記事を読めば、日常で利用する正規表現のすべてを短時間でマスターすることができます。正規表現を使えば、文字列の集合を表現することができ、正規表現にマッチする文字列を検索したり、置換したりすることができます。  

正規表現を使って文字列の集合を表現できます。たとえば「a」「aa」「aaa」という三つの文字列を正規表現で表現してみましょう。連続する文字の個数を表現する正規表現「{}」を使って次のように書くことができます。

a{1,3}

「a」「aa」「aaa」という文字列の集合をひとつの正規表現「a{1,3}」で表しています。{}は量指定子と呼ばれるもので、連続した文字を表現することができます。

a
aa     ->    a{1,3}
aaa

正規表現の例をもう一つみてみましょう。「p1」「q1」「r1」という文字列の集合をひとつの正規表現「[pqr]1」で表すことができます。[]は文字クラスと呼ばれるもので、複数の文字の集合を表現することができます。

p1
q1    ->    [pqr]1
r1

このように複数の文字列をひとつの正規表現で書くことができます。

パターンマッチ

パターンマッチとは正規表現で表した文字の集合が対象の文字列に含まれているかを調べる操作のことをいいます。パターンマッチを行うにはパターンマッチ演算子を使用します。

# パターンマッチ
$str =~ /正規表現/

パターンマッチ演算子は「=~」です。正規表現はスラッシュ「/」で囲む必要があります。マッチした場合は真を、マッチしなかった場合は偽を返します。パターンマッチはif文と組み合わせて利用されることがほとんどです。

if ($str =~ /正規表現/) {
  # マッチした場合の処理
}

「a{1,3}」という正規表現が「This string is aa.」という文字列にパターンマッチするかを確認してみましょう。

文字列: 「This string is aa.」

正規表現:  「a{1,3}」

スクリプトは以下になります。

my $str = 'This string is aa.';

if ($str =~ /a{1,3}/) {
  print "Match\n";
}

「Match!」と出力されます。

反対にパターンマッチしないということを表す演算子「!~」もあります。

$str !~ /正規表現/

マッチした文字列を取得する

パターンマッチを使って、マッチした文字列を取得することができます。マッチした文字列を取得するには取得したい部分を「()」で囲みます。「This string is aar1」という文字列の中の「aa」と「r1」を取得してみましょう。

文字列: 「This string is aar1」

正規表現:  (a{1,3})([pqr]1)

スクリプトは次のようになります。特殊変数「$1」と「$2」には括弧で囲んだ部分が順番に代入されます。

my $str = 'This string is aar1';
if ($str =~ /(a{1,3})([pqr]1)/) {
  print "Get $1 and $2";
}

次のように出力されます。

Get aa and r1

また文字列を「This string is aaap1」に変更してみましょう。次のように出力されます。

Get aaa and p1

置換

正規表現を使ってマッチした文字列を置換することができます。置換の構文は次のようになります。

対象文字列 =~ s/正規表現/置換後の文字列/;

では「This string is aa」という文字列を「This string is bb」という文字列に置換してみましょう。このサンプルではaaにマッチする正規表現「a{1,3}」を使用します。

正規表現                      置換後の文字列
a{1,3}                --->    bb

対象文字列                    
This string is aa     --->    This string is bb

スクリプトは次のようになります。

my $str = 'This string is aa';
$str =~ s/a{1,3}/bb/;

置換されるのはひとつだけです。たとえば「This string is aa aaa」という文字列があれば、上記の置換では「This string is bb aaa」となり、マッチした最初の文字列だけが置換されます。

すべてのマッチ文字列を置換するには正規表現オプション「g」を使用します。正規表現のオプションは末尾に指定します。

# マッチしたすべての文字列を置換
対象文字列 =~ s/正規表現/置換後の文字列/g;

すべてのマッチした文字列を置換するスクリプトは次のようになります。

my $str = 'This string is aa aaa';
$str =~ s/a{1,3}/bb/g;

置換後の文字列を出力すると次のようになります。

This string is bb bb

ではここからさまざまな正規表現を見ていきましょう。

正規表現文字

文字の集合を表す正規表現には次のようなものがあります。

. 改行を除くすべての文字
\d 数字
\D 数字以外の文字
\w ワード文字(「a~z」「A~Z」「0~9」 アンダーバー「_」)
\W ワード文字以外の文字
\s 空白文字( スペース「 」、タブ文字「\t」、改行文字「\n, \r」など)
\S 空白文字以外の文字
^ 文字列の先頭
$ 文字列の末尾
\b ワード文字の境界

改行を除く任意の一文字「.」

改行を除く任意の一文字を表現するには「.」を使用します。

a
b
c    ->    .
)
@

「.」を改行を含む任意の文字にマッチさせることもできます。「.」を改行にマッチさせたい場合は、正規表現オプション「s」を使用します。

# .を改行を含む任意の文字にマッチさせる
$str =~ /./s;

数字「\d」

数字を表現するには「\d」を使用します。0~9にマッチします。

0
1
2    ->    \d
8
9

数字以外「\D」

数字以外の文字を表現するには「\D」を使用します。これは「\d」と対になるものです。

数字「\d」以外    ->    \D

空白文字「\s」

空白文字とはスペース、タブ、改ページ、キャリッジリターン、ラインフィードのことです。空白文字を表現するには「\s」を使用します。

スペース 「 」
タブ 「\t」
改ページ 「\f」           ->    \s
キャリッジリターン「\n」
ラインフィード「\r」

空白文字以外「\S」

空白文字以外の文字を表現するには「\S」を使用します。これは「\s」と対になるものです。

空白文字「\s」以外    ->    \S

ワード文字「\w」

ワード文字とは「アルファベットと数字とアンダーバー」の集合のことを指します。ワード文字を表現するには「\w」を使用します。

A-Z
a-z
       ->    \w
0-9
_

ワード文字以外「\W」

ワード文字以外の文字を表現するには「\W」を使用します。

ワード文字「\w」以外    ->    \W

文字列の先頭「^」

正規表現文字には文字の先頭を表現できるものがあります。文字の先頭を表現するには「^」を使用します。

文字列の先頭    ->    ^

この正規表現文字は文字の長さを持ちません。他の文字と組み合わせて利用します。たとえば「^abc」という正規表現は先頭が「abc」で始まる文字列にマッチします。「abc」が含まれていても先頭が「abc」でなければマッチしません。

abcppp
abcqqq    ->    ^abc
abcrrr

^はmオプションと組み合わせた場合は行の先頭という意味に変わります。mオプションを使用した場合に文字列の先頭を表現したい場合は「\A」を使用します。「\A」はオプションの存在にかかわらず常に文字列の先頭を表現します。

文字列の末尾「$」

文字列の末尾を表現するには「$」を使用します。たとえば「abc$」という正規表現は末尾が「abc」で終わる文字列にマッチします。

pppabc
qqqabc    ->    abc$
rrrabc

$はmオプションと組み合わせた場合は行の末尾という意味に変わります。mオプションを使用した場合に文字列の末尾を表現したい場合は「\z」を使用します。「\z」はオプションの存在にかかわらず常に文字列の末尾を表現します。

ワード文字の境界

ワード文字の境界を表現するには「\b」を使用します。ワード文字の境界とはワード文字からそれ以外の文字へと変わっている部分のことを指します。たとえば「abc\b」という正規表現は「abcd」にはマッチしませんが「abc/」や「abc@」や「abc 」にはマッチします。

abc/
abc@    ->    abc\b
abc 

文字クラス

複数の文字の集合を表現するには文字クラスを使用します。

[文字の集合]

たとえば「a」か「b」か「c」という文字を表現したい場合は[abc]と書きます。

a
b    ->    [abc]
c

アルファベットの範囲や数値の範囲を指定するのにハイフン「-」記号を使用することができます。

a
b
c    ->    [a-e]
d
e
0
1
2    ->    [0-4]
3
4

アルファベットと数値のいずれかを表現したい場合は[a-zA-Z0-9]と書きます。

アルファベットと数字    ->    [a-zA-Z0-9]

文字クラスを使って特定の文字以外を表現することもできます。特定の文字以外を表現するには「^」を使用します。文字クラス[]の先頭で「^」が使用されると「それ以外」という意味になり、「文字の先頭」という意味にはならないので注意してください。

たとえば「a」「b」「c」以外の文字を表現するには次のようにします。

「a」「b」「c」以外の文字    ->    [^abc]

量指定子

ある文字が連続していくつ続くかを量指定子を使って指定することができます。量指定子には次のものがあります。

? 直前の文字が0個か1個
* 直前の文字が0個以上
+ 直前の文字が1個以上
{m,n} 直前の文字がm個以上n個以下
{m,} 直前の文字がm個以上
{0,n} 直前の文字がn個以下

? 直前の文字が0個か1個

?は直前の文字が0個か1個ということを表現する量指定子です。わかりやすく言い換えれば「直前の文字があってもなくてもよい」ということです。

aaap
        ->    aaap?
aaa

量指定子はもちろん正規表現文字と組み合わせて使用することもできます。

aaa
aaap
        ->    aaa[pqr]?
aaaq
aaar

* 直前の文字が0個以上

*は直前の文字が0個以上ということを表現する量指定子です。

aaa
aaap     ->    aaap*
aaapp

+ 直前の文字が1個以上

+は直前の文字が1個以上ということを表現する量指定子です。

aaap
aaapp     ->    aaap+
aaappp

{m,n} 直前の文字がm個以上n個以下

{m,n}は直前の文字がm個以上n個以下ということを表現する量指定子です。

aaap
aaapp     ->    aaap{1,3}
aaappp

{m,}

{m,}は直前の文字がm個以上ということを表現する量指定子です。

aaapp
aaappp     ->    aaap{2,}
aaapppp

{0,n}

{0,n}は直前の文字がn個以下ということを表現する量指定子です。

aaappp
aaapppp     ->    aaap{0,5}
aaappppp

文字列の集合

文字列の集合を表現するには「|」を使用します。通常は括弧「()」と組み合わせて使用します。

a123b
a456b    ->    a(123|456|789)b
a789b

正規表現文字のエスケープ

正規表現文字を普通の文字として意味させるには直前に\をつける必要があります。たとえば「.」という文字自体を表現するには「\.」とする必要があります。

.txt    ->    \.txt

quotemeta関数を使用すると文字列全体に対してエスケープを自動で行ってくれます。

my $regex = quotemeta('.txt');

エスケープのための\Qという特殊な正規表現文字を使うこともできます。\Qから\Eまでがエスケープされる文字になります。\Eがない場合は正規表現の終わりまでが対象になります。

/\Q.txt\E/

正規表現のテクニック

(?:) キャプチャしない括弧

正規表現の括弧「()」にはキャプチャを行うためと「(A|B)」といういうように「または」ということを意味したい場合に利用されます。()を単なる「または」のために使うためには「()」の代わりに「(?:)」を使うことができます。

(?:A|B)

正規表現の囲み文字の変更

正規表現の中でスラッシュがたくさん出てくる場合は、\でエスケープしなければいけないのでとても面倒です。そのような場合はmを直前に置いて正規表現の囲み文字を変更することができます。

/\/aaa\/bbb/

# 上記と意味
m#/aaa/bbb#
m{/aaa/bbb}

置換の場合も囲み文字を変更することができます。

s#aaa#bbb#
s|aaa|bbb|

正規表現のリファレンス

正規表現はqr演算子を使ってリファレンスにすることができます。正規表現のリファレンスにすれば、正規表現オプションを含めて変数に代入することができます。

my $regex = qr/(\d+)/sm;

これは通常のパタンマッチでも利用することができます。

my $num = 34;
if ($num =~ /$regex/) {
   
}

正規表現のオプション

正規表現には必要に応じてオプションを指定することができます。

g パターンマッチを繰り返す
s 「.」を改行にマッチさせる
m 「^」と「$」を行の先頭と末尾にマッチさせる
i 大文字と小文字を区別しないでマッチさせる
e 置換に式を利用する
x 正規表現内のスペースを無視する
o 変数展開を一度だけ行う

正規表現のオプションは正規表現の末尾で指定します。複数のオプションを組み合わせることも可能です。

/正規表現/sm

g マッチしたすべての文字列を置換する

gオプションを指定することで、マッチしたすべてのものを置換することができます。

$message2 =~ s/yah/yes/g;

mとsとgについてはすでに解説したので残りのオプションを解説します。

m「^」と「$」を行の先頭と末尾にマッチさせる

m」オプションを使用すれば、「^」と「$」を行の先頭と末尾にマッチさせることができます。

$message =~ /^i/m

s 「.」を改行にマッチさせる sオプション

s」オプションを使用すれば、「改行」を「.」にマッチさせることができます。

$message =~ /^i/s

i 大文字と小文字を区別しないでマッチさせる

iオプションを使用すると大文字と小文字を区別しないでマッチさせることができます。

abc
Abc    ->    /abc/i
ABC

e 置換に式を利用する

eオプションを使用すると置換の結果に式を使用することができます。

次のサンプルはマッチした数値を2倍するサンプルです。

s/(\d+)/$1 * 2/e;

x 正規表現内の空白を無視する

xオプションを使うと正規表現内の空白を無視することができます。またコメントを記述することができるようになります。スペースであることが重要でない場合は、xオプションを使うと正規表現を見やすく記述することができます。

my $time = '03:02:56';
my $regex = qr/
  (\d{2}) # 時
  :
  (\d{2}) # 分
  :
  (\d{2}) # 秒
/x;

if ($time =~ /$regex/) {
  my $hour   = $1;
  my $minute = $2;
  my $second = $3;
}

空白自体を使用したい場合は「\ 」のように空白をエスケープします。

o 変数展開を一度だけ行う

正規表現オプション「o」を使えば、変数展開を一度だけ行うようになります。これは、正規表現の再評価が行われないので、パフォーマンスが増しますが、バグの原因にもなりやすいので、必要に応じて利用しましょう。

my $regex = "a pen";
for (1 .. 10) {
  $message =~ /$regex/o;
}

o オプションを使うと、変数展開が、最初の一度だけ行われます。つまり、ループの2回目からは、 /$regex/ → /a pen/ という変数展開が行われずに、 /a pen/ が、そのまま使われるということです。

最短マッチ

Perlの正規表現にはひとつの癖があります。それは量指定子が一番長い位置でマッチするということです。たとえば次の正規表現と文字列が与えられたときどの部分にマッチするでしょうか。

# 正規表現
.+\s

# 文字列
aaa bbb ccc

.+\sという正規表現の意味は「改行以外の文字がひとつ以上で空白」という意味です。上のサンプルでは複数の候補があります。「aaa 」と「aaa bbb 」というふたつの候補です。Perlのデフォルトの動作では一番長い位置にマッチし「aaa bbb 」にマッチします。

# マッチする文字列
「aaa bbb 」

これを「aaa 」にマッチさせることは可能です。このテクニックを最短マッチといいます。最短マッチを行うには量指定子の後ろに?を付けます。正規表現を (.+?\s) にすれば、「aaa 」にマッチするようになります。

# 最短マッチを行う正規表現
.+?\s

これで「aaa 」に正規表現がマッチするようになります。

「aaa 」

日本語と正規表現

正規表現の中で日本語を使うためには正規表現も対象の文字列も内部文字列に変換しておく必要があります。内部文字列への変換はEncodeモジュールの使い方で解説していますので参考にしてください。

終わりに

この解説で学んだことさえ覚えておけば実務で困ることはないでしょう。正規表現を使えば必要な行だけを抜き出したり、簡単に置換することができるます。Perlの醍醐味である正規表現をぜひ楽しんでみてください。

業務に役立つPerl

Perlテキスト処理のエッセンス

PerlでポータブルなLinuxファイル管理入門

ITエンジニアの求人情報など

 ITエンジニアの求人情報・Webサービス・ソフトウェア・スクールなどの情報。

システム開発のお問い合わせ