Perl Hackers Hub

第58回 正規表現の勘所―わかりづらい記法の覚え方,先読みや後読みの実践(1)

この記事を読むのに必要な時間:およそ 3.5 分

正規表現にまつわる演算子

Perlには正規表現にまつわるさまざまな演算子があります。お馴染みかもしれませんが,軽く見ていきましょう。

m//演算子─⁠─検索を行う

まずは,検索を行うm//です。m//mはマッチmatchの頭文字で,m//はマッチ演算子とも呼ばれます。

文字列が代入された変数$strに対し,与えられた正規表現にマッチするパターンがあるかを検索したい場合,マッチ演算子m//と二項演算子=~を使って$str=~ m/●/と書きます。マッチするパターンがある場合に$str =~ m/●/は真,ない場合に偽となるので,if文の条件部分で使うことが多いです。

my $str = "ミーティングの日は木曜日です";
if ( $str =~ m/木曜日/ ) { # マッチする
    print "Thursday\n"; # 表示される
}

m//ではなくm||などのスラッシュ以外の区切り記号の選択については後述しますが,区切り記号がスラッシュの場合に限ってm記号を省略できるというルールがあります。上記の$str =~ m/木曜日/の場合,$str=~ /木曜日/mを省略できます。

本稿でも,省略できる場合はmを省略します(前項のコードでも省略していました⁠⁠。区切り記号が変更されたり,mが省略されている場合でも,本稿の解説ではマッチ演算子自体を指してm//と書いています。

s///演算子─⁠─置換を行う

マッチした箇所を別の文字列に置換する場合,s///を使います。s///sは代用を意味するsubstitutionの頭文字です。

文字列変数$strと正規表現があり,マッチ箇所を文字列で置換する場合,次の(1)のようにs///=~を使って$str =~ s/●/■/と書きます。

my $str = "ミーティングの日は木曜日です";
$str =~ s/木曜日/火曜日/;  ――(1)
print "$str\n"; # => "ミーティングの日は火曜日です"

通常,置換は最初にマッチした箇所のみ行われますが,マッチするすべての箇所を置換する場合,次の(1)のようにs///gと右側にgを付けます。gは大域を意味するglobalの頭文字です。

my $str1 = "アリスさんとボブくん";
$str1 =~ s/君|くん|さん/さま/;
print "$str1\n"; # => アリスさまとボブくん
my $str2 = "太郎君と花子さん";
$str2 =~ s/君|くん|さん/さま/g;  ――(1)
print "$str2\n"; # => "太郎さまと花子さま";

このgに代表される,正規表現のマッチや置換の方法を変える指示のことを修飾子modifierと呼びます。正規表現の解説では修飾子であることを明示するため,スラッシュ記号を伴って/gのように書きます。

よく使われる修飾子については後述します。

特殊変数$_─⁠─正規表現演算子のデフォルトの処理対象

m//s///が二項演算子=~を伴わない場合,特殊変数$_とのマッチを試みます。つまり,m//s///の左側に$_ =~が省略されたとみなされます。

特殊変数$_は,標準入力から1行ずつ受け取るwhile(<>)構文で1行の文字列が格納される変数など,多くの組込み機能がデフォルトの処理対象とします注3⁠。

この直接書かれない$_を活用して簡潔なプログラムを書く方針には賛否両論ありますが,書き捨てプログラムなどをすばやく書きたい場面で効果的です。

while(<>) {  ――(1)
    next if /^\s*#/ || /^\s*$/;  ――(2)
    print;  ――(3)
}

上記の例は,標準入力で与えられたソースコードから,空行や#開始のコメント行を除いて表示します。

(1)while(<>)によって,$_に標準入力の1行が順番に代入されます。標準入力の末尾に到達すると,<>は偽と評価されてwhile文から抜けます。

(2)/\s*#/はコメント行,/^\s*$/は空行を表していて,これらのm//=~を伴っていないので,(1)で用意された標準入力からの1行である$_とマッチを試みます。

また,(3)は,出力文字列を引数で与えられていないprint$_を出力文字列とする性質を利用しています。

注3)
perldoc perlvarに詳細が書かれています。

区切り記号の変更

m//の解説時に少し触れましたが,m//s///は区切り記号をスラッシュ/以外から選ぶことができます。

while(<>) {
    if ( m|(https?://\S+)| ) {  ――(1)
        print "$1\n";
    }
}

(1)では,m//の区切り記号として縦線|を選んでいます。

(1)でスラッシュが区切り記号の場合,正規表現を構成する文字にスラッシュを使うには,それが正規表現の終端文字ではないことを示すため,m/(https?:\/\/\S+)/のようにバックスラッシュを使ってエスケープする必要があります。正規表現で特定の記号群を多用する場合,それら以外を区切り記号に選ぶことで可読性が上がります。

s///の場合も,s|||のように変更できます。

対となる記号がある括弧類を区切り記号にすることも可能で,その場合はm{}m[]s{}{}s[][]と直感的に書くことができます。波括弧{}は正規表現のメタ文字注4としての使用頻度がそれほど高くないため,括弧類の区切り記号として選ばれやすいです。

注4)
波括弧{}の正規表現のメタ文字としての用法は,正の整数mnにおいて,正規表現がm回以上n回以下登場することを●{m,n}と表現します。

<続きの(2)こちら。>

WEB+DB PRESS

本誌最新号をチェック!
WEB+DB PRESS Vol.122

2021年4月24日発売
B5判/168ページ
定価1,628円
(本体1,480円+税10%)
ISBN978-4-297-12119-8

  • 特集1
    上から下まで全レイヤ解説! 複雑化した世界を体系的に学ぶ
    Web技術総整理
  • 特集2
    新バージョン登場! PythonによるWeb開発の基本
    はじめてのDjango
  • 特集3
    Rustで実装!
    作って学ぶRDBMSのしくみ

著者プロフィール

尾形鉄次(おがたてつじ)

大学院卒業後の2003年,Webメール開発会社に入社。以後10年以上,Perlを使ったWeb開発に携わる。

2015年,株式会社ガイアックスに入社。オンプレやクラウドの構築や保守運用を担当するインフラチームに所属しつつ,2018年頃から社外イベント運営やプログラミング教育などにも携わっている。

教育に関しては大学でのTAの経験などから課題感を持っていたが,2013年以降にPerl入学式などのコミュニティで教える側に立つことにつながり,そこで得られた知見を社内外で活かしている。

2019年よりJapan Perl Association理事,およびPerl入学式2代目代表を務める。