特徴と実装
特徴として、強力な文字列演算機能(拡張された正規表現構文のサポート)。組込みの連想配列(ハッシュ)。柔軟な拡張性 - CPAN参照。リファレンス - C言語のポインタのようなもので、ポインタより安全に複雑なデータを扱える。オブジェクト指向 - リファレンスをベースにした汎用オブジェクト指向システム。リファレンスカウントによるリソースマネージメント。クロージャのサポート。スレッド - Perlのスレッドはデータが丸ごとコピーされるという点で概念的にUNIXやLinuxのforkに近い。PerlIOレイヤ - 入出力操作に手を加えることが出来る。各レイヤはC言語で実装できるため高速である。Encodeモジュール - 汎用エンコーディング操作モジュール。許容度の高い文法「TMTOWTDI ... There's More Than One Way To Do It(やり方はいくらでもある)」という言葉にあらわされるように、様々なスタイルで記述することができる。
実装として、Perl4とPerl5のバージョンがあり、Perl4は、現行バージョンが5であることから、一世代前のPerlといえます。少数ながら、Perl4の環境を持つプロバイダも存在します。Perl5は、Perl4との互換性を保ちながら、ソースコードを一から書き換えたと言われ、オブジェクト指向の要素を取り入れたものとなっています。現在 Perl5は、安定版2種 (Stable, Latest) と開発版 (Devel) に分けて開発が行われています。実行はランタイムコンパイル形式を取っており、スクリプトは実行前に仮想マシン向けにコンパイルされ、コンパイルされたバイトコードが実行されます(Perlをインタプリタと呼ぶ人がいるが、この点では厳密には異なる)。ただし、Pythonのように一旦生成したバイトコードを保存して再利用することは少なく、これは現在のPerlのランタイムコンパイルが高速で、バイトコードから実行するメリットが少ないことが理由の一つである。コンパイル済みコードの再利用ではむしろmod_perl のような形式が好まれています(開発中のPerl 6はParrotという仮想マシンによって動作する。現在、ParrotCodeへのコンパイルを行うHaskell言語で書かれたPugsという実装が開発されている。Perl 6により、長らく実装が一つしかないという状況がようやく変わろうとしている)
インストールは、ActiveState社により提供されているWindows環境で動作するPerl(ActivePerl)。Perlを日本語対応させるためのパッチ(追加・補助ソフト)。外国生まれのPerlは、日本語1文字を2バイト(2文字)として扱うため、Perl の関数や正規表現で期待する効果が得られませんでしたが、このJPerlのパッチを当てることによって、日本語も1文字として扱うことが可能となり、日本語処理を有効に行うことができます(JPerl)などを参照してください。
Hello World
「#」以降は行末までがコメントである。
# バックスラッシュ"\"は環境により半角円記号"¥"となる場合がある。 print "Hello, world!\n"; # シェルからは以下のとおり。 perl -e 'print "Hello, world!\n"' perl -le 'print "Hello,world!\n"'TOP
データ型
主なものとして、スカラー(Scalar)、スカラー配列(Array)、そしてハッシュ(Hash)があります。$@%といった変数の型を示す記号をシジル(sigil)と呼び、変数名は、シジルと英字1文字から始まり、それ以降は数字、英字およびアンダースコア ( _ ) を用いることができます。また大文字と小文字は区別されます。配列およびハッシュの各要素はスカラーであり、スカラーには数値、文字列、バイト列、そして#リファレンス(参照;Reference)を格納できます。
# データ型 my $str = "Hello, world"; my @array = (0, 1, 2); my %hash = ('one' => 1, 'two' => 2); print '$str : '.$str."\n"; # Hello, world print '@array : '.$array[1]."\n"; # 1 print '%hash : '.$hash{'two'}."\n"; # 2 my $int = 42; my $float = 3.14159265358979; my $string = "\x{99f1}\x{99dd}"; # UTF-8で「駱駝」 my $bytes = "\xe9\a7\b1\e9\a7\9d"; # バイト列で「駱駝」 my $sref = \$int; # スカラーへのリファレンス my $aref = \@array; # 配列へのリファレンス my $href = \%hash; # ハッシュへのリファレンス print '$int : '.$int."\n"; # 42 print '$float : '.$float."\n"; # 3.14159265358979 print '$string : '.$string."\n"; # 駱駝 print '$bytes : '.$bytes."\n"; # 1 979d print '$sref : '.$sref."\n"; # SCALAR(0x8064308) print '$aref : '.$aref."\n"; # ARRAY(0x80641b8) print '$href : '.$href."\n"; # HASH(0x8064200)TOP
リストと配列
リストとは、スカラーに順番をつけて並べたもので、そのリストの中でのスカラーの位置によって自由にアクセスすることができます。そして、このリストを変数にしたものを、配列といいます。 配列の名前は、@ (アットマーク) + 英字1文字から始まり、それ以降は数字、英字およびアンダースコア ( _ ) を用いることができます。また大文字と小文字が区別されます。
# スカラー型の代入 ($x,$y,$z) = (1,2,3); # $x,$y,$zにそれぞれ1,2,3を代入 ($a,$b) = ("banana","melon"); # $aにbananaを、$bにmelonを代入 ($a,$b) = ($b,$a); # $aと$bの値を入れ換え # 配列型の代入 @fruit = ("apple","orange","banana"); @hoge = (@fruit,"hoge"); # @fruit + hoge を@hogeに代入 $num = $#fruit + 1; # 配列末尾の要素の添字は、$#変数名という変数で表すことができます。 print "この配列の要素は$num個あります。\n";TOP
配列関数
配列を操作するための関数として、次のような関数が用意されています。
関数 | 意味 |
push | 配列の末尾へ要素を追加 |
pop | 配列の末尾の要素を削除 |
unshift | 配列の先頭へ要素を追加 |
shift | 配列の先頭の要素を削除 |
reverse | 配列の順序を逆順にする |
sort | 配列の順序をソートする |
配列の末尾へ要素を追加します。
@char = ("A","B","C"); push(@char,"D"); print "@char\n"; # A B C D・pop
配列の末尾の要素を削除します。
@char = ("A","B","C"); pop(@char); print "@char\n"; # A B・unshift
配列の先頭へ要素を追加します。
@char = ("A","B","C"); unshift(@char,"D"); print "@char\n"; # D A B C・shift
配列の先頭の要素を削除します。
@char = ("A","B","C"); shift(@char); print "@char\n"; # B C・reverse
配列の順序を逆順にします。
@char = ("A","B","C"); @char = reverse(@char); print "@char\n"; # C B A・sort
文字列、数値および昇順降順によって4パターンのソートが存在します。
# 文字列としてソート(昇順) @char = ("C", "A", "D", "B"); @char = sort(@char); print "@char\n"; # A B C D # 文字列として逆ソート(降順) @char = ("C", "A", "D", "B"); @char = sort { $b cmp $a } (@char); print "@char\n"; # D C B A # 数値としてソート(昇順) @char = (3, 1, 4, 2); @char = sort { $a <=> $b } (@char); print "@char\n"; # 1 2 3 4 # 数値としてソート(降順) @char = (3, 1, 4, 2); @char = sort { $b <=> $a } (@char); print "@char\n"; # 4 3 2 1TOP
ハッシュ変数
ハッシュとは、連想配列とも呼ばれ、「キー」と「値」を1組のペアとして関連付けされた配列です。ただし、配列自体は順序付けされていないところに特徴があります。ハッシュの変数のことを、ハッシュ変数といい、% (パーセント) + 英字1文字から始まり、それ以降は数字、英字およびアンダースコア ( _ ) を用いることができます。また大文字と小文字が区別されます。
・ハッシュ変数の操作
ハッシュ変数は、キーと値をペアにして、数値や文字列を代入します。 また、このように定義したハッシュは読みづらいことがあるので、Perl5に限ってコンマの代わりに、=> (等号、大なり) という区切り記号が用意されています。これを使うことで、キーと値の違いを明確にあらわすことができます。
# Perl4互換 %fruit = ("red","apple","yellow","banana"); # Perl5互換 %fruit = ( "red" => "apple", "yellow" => "banana" );TOP
ハッシュ関数
ハッシュを操作するための関数として、次のような関数が用意されています。
関数 | 意味 |
keys | すべてのキーを取り出す |
values | すべての値を取り出す |
each | 1組のキーと値を取り出す |
delete | 特定の要素を取り除く |
すべてのキーを取り出します。取り出された配列の順序は一定でないことに注意します。これはハッシュが順序付けられた配列でないためです。
%fruit = ("red", "apple", "yellow", "banana", "purple", "grape"); @file = keys (%fruit); print "@file\n"; # purple yellow red・values
すべての値を取り出します。keys関数同様、取り出された配列の順序は一定ではありません。
%fruit = ("red", "apple", "yellow", "banana", "purple", "grape"); @file = values (%fruit); print "@file\n"; # grape banana apple・each
1組のキーと値を取り出します。
%fruit = ("red", "apple", "yellow", "banana", "purple", "grape"); ($key, $val) = each (%fruit); print "キー $key 値 $val\n"; # キー purple 値 grape
しかしながら、上記の場合、どのペアが取り出されるかが不定であり、しかも1組だけが抽出されるので、通常 each関数の場合は次のようにwhile構文と併用した形で使用されます。
%fruit = ("red", "apple", "yellow", "banana", "purple", "grape"); while ( ($key, $val) = each (%fruit) ) { print "キー $key 値 $val\n"; }・delete
特定の要素を取り除きます。変数によるキーを指定することで、そのキーと値のペアをハッシュから削除します。
%fruit = ("red", "apple", "yellow", "banana", "purple", "grape"); delete $fruit{"yellow"}; while (($key, $val) = each (%fruit)) { print "キー $key 値 $val\n"; }TOP
真と偽
制御構造としての条件式やループ文を学習する前に、真 (true) と偽 (false) について理解をしておきましょう。Perlにおける、真と偽の定義については次のとおりです。
偽 | 数値の 0 、文字列の "0"、空きの文字列 ""、未定義値 |
真 | 上記以外のもの |
0 | 数値の 0 なので偽 |
1 | 数値の 1 なので真 |
3 - 3 | 数値として結果が 0 なので偽 |
0.0 | 数値の 0 なので偽 |
"0" | 文字列の 0 なので偽 |
"0.0" | 文字列の 0.0 は 0 とは異なるため真 |
A | 文字列の A は真 |
"" | 空き文字列なので偽 |
undef() | 未定義値を返す関数なので偽 |
条件式
条件式とは、「もし〜ならば…」という条件構文を定義するもので、if文と unless文の2つが用意されています。また、通常 比較演算子 を使用して条件を定義します。 unless文は if文の逆の意味を持ちます。
構文 | if ( 条件A ) { 実行A } elsif ( 条件B ) { 実行B } else { 実行C } |
意味 | もし、「条件A」が真であるならば、「実行A」を実行する。 もし、「条件A」が偽で、「条件B」が真ならば、「実行B」を実行する。 もし、「条件A」「条件B」が偽であるならば、「実行C」を実行する。 |
if ($size eq "big") { print "大きい\n"; } elsif ($size eq "middle") { print "中くらい\n"; } else { print "小さい\n"; }TOP
ループ文
ループ文とは、ある条件のもとで、処理を繰り返し実行するもので、主に次の4つの構文が用意されています。
構文名 | 構文 | 意味 |
while文 | while ( 条件 ) { 実行 } | 「条件」が真である間は「実行」を実行する |
until文 | until ( 条件 ) { 実行 } | 「条件」が偽である間は「実行」を実行する |
for文 | for ( 初期化 条件式 増減式 ) { 実行 } | 「条件式」が真であれば「実行」を実行し、次に「増減式」を実行する |
foreach文 | foreach 制御変数 ( リスト値 ) { 実行 } | 「リスト値」の要素を順に「制御変数」にセットして、「実行」を実行する |
・while文
$i = 3; while ($i > 0) { print "カウントダウン : $i \n"; $i--; } <出力結果> カウントダウン : 3 カウントダウン : 2 カウントダウン : 1
・until文
$i = 3; until ($i <= 0) { print "カウントダウン : $i \n"; $i--; } <出力結果> カウントダウン : 3 カウントダウン : 2 カウントダウン : 1
・for文
for ($i=3; $i>0; $i--) { print "カウントダウン : $i \n"; } <出力結果> カウントダウン : 3 カウントダウン : 2 カウントダウン : 1
・foreach文
foreach $i (3, 2, 1) { print "カウントダウン : $i \n"; } <出力結果> カウントダウン : 3 カウントダウン : 2 カウントダウン : 1TOP
ループ制御
ループ文で、ループの流れを変えるために、次の2つの演算子が用意されています。
next演算子 | ループをスキップする |
last演算子 | ループを終了する |
・next演算子
foreach $i (5, 4, 3, 2, 1) { if ($i == 3) { next; } print "カウントダウン : $i \n"; } <出力結果> カウントダウン : 5 カウントダウン : 4 カウントダウン : 2 カウントダウン : 1
・last演算子
foreach $i (5, 4, 3, 2, 1) { if ($i == 3) { last; } print "カウントダウン : $i \n"; } <出力結果> カウントダウン : 5 カウントダウン : 4TOP
標準入出力
Perlでは、ファイルの入出力にファイルハンドルというものを使います。ファイルハンドルとは、今どのファイルを処理しているかを管理するための名前のことです。ファイルの読み込みや書き込みをするには、まずファイルを開いてファイルハンドルに関連付けし、そのファイルハンドルを操作するのが基本手順になります。Perlでは、特別なファイルハンドルとして、以下の3つが用意されています。
ファイルハンドル | 意味 | 内容 |
STDIN | 標準入力 | パイプ ( *1 ) やリダイレクト ( *2 ) 時にデータを取り込む。コマンドライン時にはキーボード。 |
STDOUT | 標準出力 | パイプやリダイレクト時にデータを出力する。コマンドライン時にはモニター。 |
STDERR | 標準エラー | エラー時に出力される。 |
*2 : 標準出力をそのままのテキストファイルなどに書き出す方法をいいます。
たとえばコマンドラインから操作する場合には、以下のようなスクリプトを実行することができます。
#スクリプト例 #!/usr/local/bin/perl print "お名前は? \n"; $name = <STDIN>; print "こんにちは、$nameさん\n"; <出力結果> お名前は? > TARO (← キーボードから入力) こんにちは、TAROさん
このように、キーボード入力を標準入力であるファイルハンドルSTDINから変数$nameにセットし、文字列を操作・表示させることができます。 TOP
open関数とファイルハンドル
前節ではあらかじめ用意された標準のファイルハンドルを説明しましたが、それ以外のファイルハンドル以外を使う場合には、open関数を使用します。open関数を使ってファイルを読み込む場合の構文は次のようになります。
ファイルを読み込みモードでオープンする場合 (open命令) |
open ファイルハンドル, "ファイル名"; |
ファイルを読み込む処理 (read命令) |
配列 = <ファイルハンドル> |
ファイルのオープンを閉じる処理 (close命令) |
close ファイルハンドル |
たとえばこの一連の処理として、掲示板などで記録ファイルを配列として読み込むには、次のように記述します。(読み込むファイル名をここでは log.txt と仮定します)
記述例 | 1| open(IN,"log.txt"); 2| @file = <IN>; 3| close(IN); |
内容 | 1| 記録ファイルを、INというファイルハンドルで読み込む。 2| 読み込んだ記録ファイルを配列@fileにセットする。 3| 読み込み処理を閉じる。 |
記述例の場合、ファイルハンドル名は「IN」という名前を使っていますが、これは自由に名前を指定して構いません。ただし、一般的には英字大文字で指定するようになっています。なお、記述例の 1 の部分、ファイルの読み込み処理で、
open(IN,"< log.txt");
というように、< を付ける場合もあります。意味は同じです。 TOP
ファイルの読み書き
ファイルに書き込み操作を行う場合にも、open関数とファイルハンドルを使います。open関数によるファイルの読み書き操作としては、次のような構文が用意されています。
意味 | 構文 |
読み込みモードでオープン | open (FH, "file") open (FH, "< file") |
上書きモードでオープン | open (FH, "> file") |
追加書き込みモードでオープン | open (FH, ">> file") |
読み書き両用モードでオープン | open (FH, "+> file") open (FH, "+< file") |
パイプ出力用でオープン | open (FH, "| コマンド") |
パイプ入力用でオープン | open (FH, "コマンド |") |
・ファイルの上書き
open(OUT,"> log.txt"); print OUT "DEF"; close(OUT); <解説> log.txtに「DEF」という文字列を上書きします。 上書きですから、log.txtの中身を消して新たに書き込むことになります。 仮に、当初 log.txt の中に「ABC」という文字列があった場合、上記の処理後 中身は「DEF」となります。
・追加書き込み
open(OUT,">> log.txt"); print OUT "DEF"; close(OUT); <解説> log.txtに「DEF」という文字列を追加書き込みします。 追加書き込みですから、log.txtの中身のに追加連結することになります。 仮に、当初 log.txt の中に「ABC」という文字列があった場合、上記の処理後 中身は「ABCDEF」となります。
・コマンドからパイプ入力用にオープン
open(IN,"ls -l |"); @cmd = <IN>; close(IN); foreach $cmd (@cmd) { print "$cmd \n"; } <解説> ls -l (カレントディレクトリ内のファイル名を取得) というコマンド を実行し、その結果を配列@cmdに格納し、表示する。
・コマンドへパイプ出力用にオープン
open(OUT,"| /usr/sbin/sendmail"); print OUT "HELO \n"; close(OUT); <解説> sendmailへ HELOコマンドを出力。TOP
その他のファイル操作
その他にファイル操作のための関数として、次のようなものがあります。
ファイルを削除 | unlink ファイル名 |
ファイル名を変更 | rename "変更前のファイル名", "変更後のファイル名" |
パターンマッチ演算子
正規表現とは、ある特定の文字列ではなく、文字列の一部をパターン化して表現する手法をいいます。複数行ある文章の中から文字列を検索する時に、検索したい文字列すべてを指定するのではなく、文字列の任意の一部を置き換え可能な状態にして検索をする場合に正規表現を用います。たとえば、UNIXやWindowsでは、文字列を検索するときに使用される正規表現として、「*」を任意の文字列(空き文字も含む)に、「?」を任意の1文字として使用することができます。パターンを、app* と表現すると、 apple application appeal などの文字列がマッチすることになります。この正規表現の方法は、当然ながらプログラム言語ごとに異なりますが、Perlの場合は他の言語に比べて非常に強力な正規表現によるパターンマッチ機能を備えており、より詳細にかつ容易にパターンマッチを行うことができるとされています。Perlでは、パターンマッチ演算子として、スラッシュ ( / ) で囲んだ文字列が正規表現であるとされます。性格上、正規表現は条件式と併用して用いられることが多いようです。この場合次のような構文があります。
構文 | 意味 |
if ( 文字列 =~ /パターン/) | もし「文字列」の中に「パターン」が含まれていれば (パターンマッチすれば真) |
if ( 文字列 !~ /パターン/) | もし「文字列」の中に「パターン」が含まれていなければ (パターンマッチすれば偽) |
$word = 'apple'; if ($word =~ /a/) { print "含まれています。\n"; } else { print "含まれていません。\n"; } <出力結果> 含まれています。
また、上記構文(パターンマッチすれば真)の省略形の位置づけとして、次の構文があります。この場合、対象となる「文字列」は特殊変数 $_ が充てられます。
構文 | 意味 |
if (/パターン/) | もし変数 $_ の中に「パターン」が含まれていれば(パターンマッチすれば真) |
$_ = 'apple'; if (/a/) { print "含まれています。\n"; } else { print "含まれていません。\n"; } <出力結果> 含まれています。
正規表現は通常スラッシュ ( / ) で囲んだ部分がパターンマッチの対象となりますが、このスラッシュを他の任意の記号に置き換えることもできます。パターンの中に「スラッシュ」が多く含まれる場合に、正規表現を書きやすくするための措置と思われます。
構文 | 意味 |
m 区切り文字 パターン 区切り文字 | /パターン/ に同じ |
※「区切り文字」とは、空白文字以外の記号を使用することができます。 (ex. @, #, *, | など)
$url = 'http://www.kent-web.com/'; if ($url =~ /http:\/\//) { print "含まれています。\n"; } ↓ 次のように記述することが可能。(エスケープしない分、書きやすくなる) $url = 'http://www.kent-web.com/'; if ($url =~ m|http://|) { print "含まれています。\n"; }
パターンマッチ演算子には、正規表現を行う際のオプションとして、いくつかの修飾子が用意されています。
修飾子 | 意味 |
g | 繰り返しマッチする |
i | 大文字と小文字の区別をしない |
o | 変数展開を1度だけ行う |
m | 文字列を複数行として扱う |
s | 文字列を単一行として扱う |
x | 拡張正規表現を行う |
・g修飾子の例
$word = 'ABCDEF'; while ($word =~ /(.)/g){ push(@word, $1); } print "@word\n"; <出力結果> A B C D E F (変数$wordから1文字づつマッチさせて配列@wordに格納する)
・i修飾子の例
▽例文(1) $word = 'apple'; if ($word =~ /A/) { print "マッチします。\n"; } else { print "マッチしません。\n"; } <出力結果(1)> マッチしません。 ▽例文(2) : i修飾子を使う場合 $word = 'apple'; if ($word =~ /A/i) { print "マッチします。\n"; } else { print "マッチしません。\n"; } <出力結果(2)> マッチします。
・m修飾子の例
▽例文(3) $word = "AAA\nBBB\n"; if ($word =~ /^B/) { print "マッチします。\n"; } else { print "マッチしません。\n"; } <出力結果(3)> マッチしません。 ▽例文(4) : m修飾子を使う場合 $word = "AAA\nBBB\n"; if ($word =~ /^B/m) { print "マッチします。\n"; } else { print "マッチしません。\n"; } <出力結果(4)> マッチします。TOP
メタ文字
正規表現を行ううえで、次のようなメタ文字と呼ばれる特殊な意味を持つ文字には、そのままではマッチできないため、必ず直前に \ を付加してエスケープする必要があります。
\ ^ . $ * ? | ( ) [ ] { }
▼メタ文字を「文字」としてマッチさせるためには、直前に \ を付ける
$price = 'This is $100'; if ($price =~ /\$/) { print "含まれています。\n"; } else { print "含まれていません。\n"; } <出力結果> 含まれています。これらのメタ文字を正規表現の中で使用する場合については、次に解説していくことにします。
1. 任意の1文字と位置指定
メタ文字 | 正規表現上の意味 |
\ | 直後の文字をエスケープ |
^ | 先頭にマッチ |
. | 改行を除く任意の1文字 |
$ | 末尾にマッチ |
▼「 ^ 」(キャレット)の使用例
$word = 'apple'; if ($word =~ /^a/) { print "マッチします。\n"; } else { print "マッチしません。\n"; } <出力結果> マッチします。 (apple という文字列は a で始まるため)
▼「 . 」(ドット)の使用例
$word = 'apple'; if ($word =~ /app.e/) { print "マッチします。\n"; } else { print "マッチしません。\n"; } <出力結果> マッチします。 (apple という文字列の l は改行以外の任意の1文字のため)
▼「 $ 」(ダラー)の使用例
$word = 'apple'; if ($word =~ /e$/) { print "マッチします。\n"; } else { print "マッチしません。\n"; } <出力結果> マッチします。 (apple という文字列の e は末尾にあるため)
2. パターン論理和
メタ文字 | 正規表現上の意味 |
| | 選択 |
▼「 | 」(パイプ)の使用例
$word = 'apple'; if ($word =~ /a|b|c/) { print "マッチします。\n"; } else { print "マッチしません。\n"; } <出力結果> マッチします。 (apple という文字列に a 又は b 又は c の文字があるため)
3. パターングループ
メタ文字 | 正規表現上の意味 |
( ) | グループ化 |
▼「 ( ) 」(バーレン)の使用例
$word = 'apple'; if ($word =~ /^(...)(.)(.)/) { print "先頭の3文字 $1 \n"; print "次の1文字 $2 \n"; print "次の1文字 $3 \n"; } <出力結果> 先頭の3文字 app 次の1文字 l 次の1文字 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"; } <出力結果> マッチします。 (apple という文字列に、英小文字が5回繰り返されているため)
量指定子の正規表現を行う場合、マッチする範囲は最大マッチであることに注意しましょう。 まず以下の例を見てみましょう。
$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」という文字列でもあてはまりますが、このように最大の範囲内で条件にあてはまる部分がマッチするという取り決めになっています。 そこで、最短マッチにする場合のオプションとして、量指定子の直後に ? (クエスチョン) をつけることでそれが可能となります。
$word = 'apple:1 banana:3 orange:2'; if ($word =~ /(.+?[0-9])/) { print "$1\n"; } <出力結果> apple:1
量指定子での最短マッチを行うパターンをまとめると次のようになります。
*? | 直前の文字を0回以上に最短マッチ |
+? | 直前の文字を1回以上に最短マッチ |
?? | 直前の文字を0又は1回に最短マッチ |
{n}? | 直前の文字をn回に最短マッチ |
{n,}? | 直前の文字をn回以上に最短マッチ |
{n,m}? | 直前の文字をn回以上、m回以下に最短マッチ |
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 | アラーム(ベル) |
\d | バックスペース |
\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, ... ) |
$& | マッチした文字列全体 |
$` | マッチした文字列の前にあるすべての文字列 |
$' | マッチした文字列の後にあるすべての文字列 |
置換演算子
正規表現を使用し、マッチした文字列を置き換える置換演算子があります。
構文 | 意味 |
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\//; ↓ 次のように記述することが可能。(エスケープしない分、書きやすくなる) $url = 'http://www.kent-web.com/bbs/file/'; $url =~ s|/bbs/file/|/chat/data/|;
・置換演算子には、置換えを行う際のオプションとして、次の修飾子が用意されています。
修飾子 | 意味 |
g | 繰り返しマッチして置き換える |
i | 大文字と小文字の区別をしない |
o | 変数展開を1度だけ行う |
m | 文字列を複数行として扱う |
s | 文字列を単一行として扱う |
x | 拡張正規表現を行う |
e | 置換文字列を「式」と見なす |
▽g修飾子の例
▽例文(1) $word = 'aaaaaa'; $word =~ s/a/A/; print "$word\n"; <出力結果(1)> Aaaaaa ▽例文(2) : g修飾子を使う場合 $word = 'aaaaaa'; $word =~ s/a/A/g; print "$word\n"; <出力結果(2)> AAAAAA
▽e修飾子の例
$word = "this is 100 yen."; $word =~ s/(\d+)/$price = $1*2/e; print "this is $price yen.\n"; <出力結果> this is 200 yen.TOP
変換演算子
変換演算子は文字列を1文字づつ順にスキャンして、対象文字列に含まれるすべての文字を変換して、変換した文字数を返す演算子です。変換演算子は正規表現を使わないので、厳密には正規表現の対象にはならない演算子ではあります。
構文 | 意味 |
tr/変換対象の文字/変換後の文字/ | 「変換対象の文字」をスキャンして「変換後の文字」にすべて変換する |
y/変換対象の文字/変換後の文字/ | 上に同じ。 |
$word = 'apple'; $word =~ tr/a-z/A-Z/; print "$word\n"; <出力結果> APPLE
$word = 'apple'; $count = $word =~ tr/a-z/A-Z/; print "$count\n"; <出力結果> 5 (変換した文字数が5個のため)
・変換演算子にも修飾子が次のとおり用意されています。
修飾子 | 意味 |
c | 「変換対象の文字」を反転させる。(「変換対象の文字」に含まれない文字すべてをスキャンする) |
d | 「変換対象の文字」に含まれるが、対応するものが「変換後の文字」にない文字を削除する |
s | 変換の結果、同じ文字が連続したものを1文字とする |
▽s修飾子の例
$word = 'apple'; $word =~ tr/a-z/A-Z/s; print "$word\n"; <出力結果> APLETOP
正規表現を利用する関数
正規表現を利用する関数として、split関数 があります。また、split関数の逆変換を行うものとして、join関数 がありますので、併せて使い方を認識しておきましょう。(join関数は正規表現は使用しません)
split関数 | split /パターン/, 文字列, 最大分割数 split /パターン/, 文字列 split /パターン/ split |
「文字列」を「パターン」でマッチするものに「最大分割数」まで分割する。 「最大分割数」省略時は、末尾の空フィードは切り捨てられる。 「文字列」省略時は、変数 $_ が充てられる。 「パターン」省略時は、先頭の空白文字を飛ばした後、空白文字をパターンとする。( /\s+/ ) |
join関数 | join 区切文字, 配列 | 区切り文字として間に「区切文字」をはさみ込み、「配列」内の各文字列を連結する。 |
サブルーチンの定義と呼び出し
サブルーチンとは、スクリプトの内部をモジュール化(部品化)する手法をいいます。いわば自分で関数を作成・定義するための手法です。このため、サブルーチンは「ユーザ関数」とも呼ばれることがあります。スクリプトを記述する際に、サブルーチンを作成するメリットとして、次の2点が挙げられます。
1. CGIスクリプトの中で、ある処理を何度も反復使用したりする場合に、その処理部分をモジュール化して、呼び出すことで効率的なプログラムを記述することができる。
2. CGIスクリプトの記述が長くなると、一般的にスクリプト全体の流れが理解しづらくなるが、このときに、全体的な処理を記述するレベル(サブルーチンの呼び出し)と、個々に細かく処理するレベル(サブルーチンの作成)を分けて、全体の見通しをよくする工夫ができる。
サブルーチンを作成するには、次のような構文で定義します。
サブルーチンの定義 |
sub サブルーチン名 { 処理 } (sub + 半角スペース + サブルーチン名 + ブロック) |
このサブルーチン名の付け方の制約は変数名と同じです。また、サブルーチンの定義部分はスクリプトのどこに置いても構いませんが、一般的には最後に置かれます。サブルーチンを呼び出すには次のようにします。呼び出されたところで、定義されたサブルーチンが呼び出され実行されます。
サブルーチンの呼び出し |
& サブルーチン名 (アンパサンド + サブルーチン名) |
▽サブルーチンの使用例
$x = 80; $y = 55; $z = 75; &answer; sub answer { $total = $x + $y + $z; $mean = $total / 3; print "合計点数は$total点で、平均点数は$mean点です。\n"; } <出力結果> 合計点数は210点で、平均点数は70点です。TOP
引数と戻り値
サブルーチンとは、ユーザが自由に定義できる関数ですから、サブルーチンに引数を渡したり、またサブルーチン実行後に戻り値を返すこともできます。引数は配列 @_ に格納されて渡されます。
▽サブルーチン abc に、引数 @list を渡すとき
引数の渡し方 | &abc(@list); |
引数の受取り | サブルーチンabcは、@list を、配列 @_ で受け取る |
&answer(80, 55, 75); sub answer { ($x, $y, $z) = @_; $total = $x + $y + $z; $mean = $total / 3; print "合計点数は$total点で、平均点数は$mean点です。\n"; } <出力結果> 合計点数は210点で、平均点数は70点です。
サブルーチンの戻り値は return を使って戻すことができます。
▽サブルーチン abc の戻り値を @back とし、それを @list で受け取るとき
戻り値の戻し方 | sub abc { 実行文 return @back; } |
戻り値の受取り | @list = &abc; (サブルーチンを右辺に定義する) |
($total, $mean) = &answer(80, 55, 75); print "合計点数は$total点で、平均点数は$mean点です。\n"; sub answer { ($x, $y, $z) = @_; $total = $x + $y + $z; $mean = $total / 3; return ($total, $mean); } <出力結果> 合計点数は210点で、平均点数は70点です。TOP
局所化
プログラムが数千行、数万行と膨大になってくると、どうしても使用する変数が重複するようになってきます。また、重複を回避させるために毎回異なる変数を充てるのにも限度があり、混乱や誤りを招いてしまいますし、膨大なプログラムである程、変数の数を絞ったほうがメモリの節約になります。そこで、変数をサブルーチンなどのブロック内だけに通用させるようにする必要がでてきます。これを局所化といいます。局所化という観点に基づいたとき、変数の特性は2つに分けられます。プログラム(スクリプト)全体で参照可能な変数をグローバル変数といい、サブルーチンなどのブロックの中だけで利用可能な変数をローカル変数といいます。変数は、初期値としてはグローバル変数となりますので、特に何も定義をしない変数はすべてグローバル変数とみなされます。変数をローカル変数として定義する場合には、local または my を使用します(local と my の違いについては次節で説明します)。まずは以下の例を見てみましょう。
$word = "晴れ"; &yesterday; print "今日は$wordです。\n"; sub yesterday { $word = "雨"; print "昨日は$wordでした。\n"; } <実行結果> 昨日は雨でした。 今日は雨です。
1行目で変数 $word に「晴れ」が代入されていますが、次の yesterdayサブルーチンが実行されて、$word に「雨」が代入されたため、3行目の $word は「雨」で実行されています。 これは変数が重複したために期待しない結果になってしまう一例です。そこで、次にサブルーチン内の変数をローカル変数で定義して局所化してみましょう。
$word = "晴れ"; &yesterday; print "今日は$wordです。\n"; sub yesterday { my $word = "雨"; print "昨日は$wordでした。\n"; } <実行結果> 昨日は雨でした。 今日は晴れです。
サブルーチン内で my を使って変数 $word を局所化したため、6行目で代入された「雨」はサブルーチンを抜けた瞬間消滅してしまいます。したがって、3行目の変数 $word は期待どおりに「晴れ」が実行されることになります。ちなみに、今回の例文では my の代わりに local を使用しても構いません。(同じ結果になります) TOP
local と my の違い
ローカル変数を宣言する関数として、local と my の2つがありますが、この2つの関数には次のような特性とそれぞれの相違が存在します。
local | my | |
Perlバージョン | Perl4, Perl5 | Perl5のみ |
局所化の特性 | 宣言されたサブルーチンから呼び出されたサブルーチンからは参照が可能。 | 完全に局所化されるため、localのように、サブルーチンから呼び出されたサブルーチンからも参照することはできない。 |
$word = "晴れ"; &today; sub today { local $word = "雨"; &yesterday; print "今日は$wordです。\n"; } sub yesterday { print "昨日は$wordでした。\n"; } <実行結果> 昨日は雨でした。 今日は雨です。
$word = "晴れ"; &today; sub today { my $word = "雨"; &yesterday; print "今日は$wordです。\n"; } sub yesterday { print "昨日は$wordでした。\n"; } <実行結果> 昨日は晴れでした。 今日は雨です。TOP
文字列の検索
文字列を検索する関数として、index関数 と rindex関数 があります。
構文 | 意味 |
index ( 全体文字列, 検索文字列, 開始位置 ) index ( 全体文字列, 検索文字列 ) |
「全体文字列」の中で、「開始位置」の位置から「検索文字列」が最初に出現する位置を返す。 「開始位置」が省略されると、先頭から検索する。(先頭の位置は 0 とする) 検索して見つからなければ -1 を返す。 |
rindex ( 全体文字列, 検索文字列, 開始位置 ) rindex ( 全体文字列, 検索文字列 ) |
「全体文字列」の中で、「検索文字列」が最後に出現する位置を返す。 「開始位置」を付加することで、値として返すことが許される、最も右よりの位置を指定することができる。 検索して見つからなければ -1 を返す。 |
▽例1
$str = "ABCDEF"; $sub = "CD"; $find = index ($str, $sub); if ($find >= 0) { $find++; print "先頭から$find番目に見つかりました。\n"; } else { print "見つかりませんでした。\n"; } <出力結果> 先頭から3番目に見つかりました。
「例1」では、最初に見つかった部分を表示させていますが、文字列の中で見つかるものすべてを表示させたい場合には、次の「例2」のように記述するとよいでしょう。
▽例2
$str = "apple orange banana"; $sub = "a"; while ( ( $find = index ($str, $sub, $find ) ) >= 0 ) { $find++; print "$find番目に発見。\n"; } <出力結果> 1番目に発見。 9番目に発見。 15番目に発見。 17番目に発見。 19番目に発見。TOP
文字列の操作
文字列を操作する関数として、substr関数があります。文字列の取り出し、置き換え、削除、追加などを行うことができます。
構文 | 意味 |
substr ( 全体文字列, 開始位置, 文字長 ) substr ( 全体文字列, 開始位置 ) |
「全体文字列」から部分文字列を取り出して返す。 部分文字列は、文字列の先頭から数えて「開始位置」番目から始まる(先頭は 0 から数える)。 「開始位置」が負の場合は、文字列の末尾からマイナス「開始位置」文字だけ戻ったところが部分文字列の先頭になる。 「文字長」を省略すると、文字列の末尾までがすべて返される。 |
▽文字列の取り出し
$word = "milktea"; $str = substr($word, 0, 4); print "$str\n"; <出力結果> milk
substr関数を左辺に置き、代入の対象とすることで、部分文字列の置き換えや追加などが可能となります。
▽文字列の追加
$word = "tea"; substr($word, 0, 0) = "milk"; print "$word\n"; <出力結果> milktea
▽文字列の置き換え
$word = "milktea"; substr($word, 0, 4) = "lemon"; print "$word\n"; <出力結果> lemontea
▽文字列の削除(先頭から削除)
$word = "milktea"; substr($word, 0, 4) = ""; print "$word\n"; <出力結果> tea
▽文字列の削除(末尾から削除)
$word = "milktea"; substr($word, -3, 3) = ""; print "$word\n"; <出力結果> milkTOP
文字列の整形
文字列を整形する関数として、printf関数 と sprintf関数 があります。文字列や数値を指定の書式に変換を行います。printf関数 は整形した値をファイルハンドルに出力し、sprintf関数 は整形した値を返します。
関数 | 構文 | 意味 |
printf関数 | printf ( 書式, リスト ) | 「リスト」の値を「書式」に整形して、ファイルハンドルに出力する。 |
sprintf関数 | sprintf ( 書式, リスト ) | 「リスト」の値を「書式」に整形して、その値を返す。 |
構文における書式のコードは次のとおりです。
コード | 意味 |
%c | 文字 |
%s | 文字列 |
%d | 10進整数 |
%e | 浮動小数点数(指数形式) |
%f | 浮動小数点数(固定小数点形式) |
%e | 浮動小数点数(コンパクト形式) |
%o | 8進整数 |
%x | 16進整数 |
%X | 16進整数(大文字使用) |
$hour = 5; $min = 12; $sec = 6; $time = sprintf("%02d:%02d:%02d", $hour,$min,$sec); print "$time\n"; <実行結果> 05:12:06TOP
ディレクトリの読み取り
ディレクトリの中身を読み取るための関数として、opendir関数、readdir関数、closedir関数があります。ファイルをオープンするときに、open関数でファイルハンドルを関連付けますが、それと同じように、ディレクトリをオープンするときには、ディレクトリハンドルを指定して関連付けを行います。opendir関数でディレクトリをオープンする場合は、読み出し専用であることに注意します。open関数のように、ファイルの追加や書き込みなどを行うことはできません。
関数 | 構文 | 意味 |
opendir関数 | opendir ( ディレクトリハンドル, ディレクトリ ) | 「ディレクトリ」で与えられたディレクトリ名をオープンする。 成功すれば真を返す。 |
readdir関数 | readdir ( ディレクトリハンドル ) | opendirによってオープンされた「ディレクトリハンドル」から、ディレクトリエントリを読む。 |
closedir関数 | closedir ( ディレクトリハンドル, ディレクトリ ) | opendirによってオープンされたディレクトリを閉じる。 |
▽ディレクトリ内のファイルとディレクトリをすべて読み出して表示させる例
opendir(DIR, "/home/kent/public_html"); @file = readdir(DIR); closedir(DIR); foreach (@file) { print "$_\n"; }TOP
ディレクトリの生成と削除
ディレクトリを生成したり、削除するための関数として、mkdir関数 と rmdir関数 があります。
関数 | 構文 | 意味 |
mkdir関数 | mkdir ( ディレクトリ名, モード ) | ディレクトリ「ディレクトリ名」を作成し、そのパーミッションを数値「モード」にする。 成功すれば 1 を返し、失敗すると 0 を返す($!にエラーコードをセットする)。 |
rmdir関数 | rmdir ( ディレクトリ名 ) | 「ディレクトリ名」に指定したディレクトリが空きならば、これを削除する。 成功すれば 1 を返し、失敗すると 0 を返す($!にエラーコードをセットする)。 「ディレクトリ名」を省略すると、$_ が使われる。 |
▽ディレクトリを生成する例
mkdir ("./lockdir", 0755) || die "ディレクトリ生成失敗 : $!";
▽ディレクトリを削除する例
rmdir ("./lockdir") || die "ディレクトリ削除失敗 : $!";TOP