|
|
||
コードリファレンス(orコードレフ)の基本的な使い方を学びました。とりあえずハッシュに格納して呼び出す感じです。
#!/usr/bin/perl
use strict;
use warnings;
my %printer = (
with_nl => \&print_with_nl,
without_nl => \&print_without_nl,
all_capital => \&print_all_capital,
);
sub print_with_nl {
local $_ = shift;
print /\n$/ ? $_ : $_."\n";
}
sub print_without_nl {
local $_ = shift;
s/\n$//;
print;
}
sub print_all_capital {
local $_ = shift;
s/(\w+)/\U$1/g;
print;
}
my $str = "I am learning Perl!\n";
$printer{with_nl} ->($str);
$printer{without_nl} ->($str);
$printer{all_capital}->($str);
実行結果
I am learning Perl!
I am learning Perl!I AM LEARNING PERL!
-e : ファイルが存在する -z : 0バイトファイル -s : 0バイトファイルじゃない -f : 通常ファイル -d : ディレクトリ -l : シンボリックリンク -T : テキストファイル -B : バイナリファイル -M : 最終更新日時からの日数 -A : 最終アクセス日時からの日数 -C : iノードが変更されてからの日数
用例は以下のように。
for my $file (glob '*') {
print "$file\n" if -T $file;
}
#!/usr/bin/perl
use strict;
use warnings;
chomp (my @list = `ls *.txt`);
# 元の要素から抽出したものだけのリスト
my @grepped = grep { /(.*)\.txt$/ } @list;
print join ",", @grepped, "\n";
# 元の要素のコピーから加工した別のリスト
my @mapped = map { /(.*)\.txt$/ } @list;
print join ",", @mapped, "\n";
# 元の要素自体を編集してしまう場合はfor/foreachを使うべき
s/\.txt/\.log/g for @list;
print join ",", @list, "\n";
実行結果
foo.txt,hoge.txt,var.txt,
foo,hoge,var,
foo.log,hoge.log,var.log,
置換演算子(substitution operator)を使うと、正規表現にマッチした部分を他の文字列に置換することができます。
s/正規表現(パターン)/ダブルクォート風の文字列/
置換演算子s///は、ブール値を返却します。
オプション修飾子にgをつけるとグローバル置換(該当する全てを置換)になります。この場合、置換はオーバーラップしません。つまり、一度置き換えられた結果が再度正規表現にマッチしたとしても、それ以上置換はされません。
また、前回出てきた/iや/sといったオプション修飾子も使用することができます。
$_ = "I am studying Perl.\n"; s/I am/You are/; print; $_ = "putty, poderosa, winscp\n"; s/p/P/g; print; 実行結果 You are studying Perl. Putty, Poderosa, winscP
デリミタにはスラッシュ以外を使用することができますが、左右で一対になるデリミタの場合、以下のように二つの組が別のデリミタでもOKです。
s<perl>#Perl#;
s[perl]{Perl};
前回、m//演算子での=~について「マッチした値を左辺に代入するのではない」と書きましたが、s///演算子で=~を使う場合は異なります。
まず、m//とs///の違いを明確に理解する必要があります。
m// : 正規表現にマッチするかどうかを判定し、ブール値を返却する s/// : 正規表現にマッチした場合、対象文字列のマッチする箇所を右辺値に置換し、ブール値を返却する
結合演算子=~は、これらの処理の対象として左辺値をとるものです。
つまり、s///で結合演算子=~を使用した場合、対象となる左辺にある文字列(もしくは変数が指す文字列)が置換されます。
しかし、あくまで結合演算子=~は左辺を対象として右辺の処理を行っているという点に注意が必要です。
my $str = "I like Perl."; $str =~ s/like/love/; print $str, "\n"; 実行結果 I love Perl.
アルファベットの大文字・小文字への置換は逆スラッシュエスケープを使用します。s///の右辺(置換後)において、逆スラッシュ以降に続くものを強制的に変換します。
\U : 以降全てを大文字へ \L : 以降全てを小文字へ \E : 変換の終了ポイント \u : 続く一文字だけを大文字へ \l : 続く一文字だけを小文字へ
これを応用するとワードの頭だけ大文字で以降は小文字というよくあるパターンは以下のようになります。
$target =~ s/\w+/\u\L$1/g;
以上が置換の基本的な使用方法となります。
Perl正規表現には、今まで見てきたパターンマッチ・置換処理の他に、文字列分割時のセパレータとしての利用方法があります。
splitは正規表現によるセパレータ(区切り)の間に挟まれた文字列のリストを返却します。
my @array = split /\t/, $target;
splitを使用する際の注意点としては、「先頭の空フィールドは必ず返されるが、末尾の空フィールドは破棄される」という点です。フィールドとはセパレータで区切られた部分文字列のことです。
my @array = split /:/, "::1:2:3::::"; print $_,"," for(@array); 実行結果 ,,1,2,3,
splitによって分割された文字列、または文字列の配列をひとつの文字列に結合したい場合は、join関数を使用します。
join関数は第一引数に「のり」のように挟み込む文字列(≠パターン)を指定し、第二引数以降に結合したい部分文字列を指定します。
my $joined_ex1 = join ":", "hoge", "foo", "var"; print $joined_ex1, "\n"; @array = ( "hoge", "foo", "var"); my $joined_ex2 = join ":", @array; print $joined_ex2, "\n"; 実行結果 hoge:foo:var hoge:foo:var
「/パターン/」は実際には「m/パターン/」のmを省略したショートカットです。ここで使っている「/」デリミタは他のものでも代用可能です。よく使用されるのは「{}(ブレース)」です。
動作をデフォルトから変えるもので閉じデリミタの直後に記述します。オプション修飾子はフラグ(flag)とも呼ばれます。
「//i」はアルファベットの大文字・小文字不問、「//s」はマッチする対象に改行文字を含みます(=全ての文字を対象とする)。
/\bhoge\b/i # ex. hoge, HOGE, Hoge,・・・ /Do.*self/s # ex. Don't \n Repeat \n Yourself
結合演算子(binding operator)は右辺のパターンで左辺の文字列がマッチするかを判定し、ブール値を返却します。マッチした値を左辺に代入するのではありません。実用ではif文の条件式などでよく使用されます。
my $target = "I love Perl!"; print $target =~ /\bperl\b/i, "\n"; print $target =~ /\bruby\b/i, "\n"; 出力結果 [user 20071011]$ ./binding_operator.pl 1 [user 20071011]$
正規表現でもダブルクォート文字列と同様に変数の展開が行われます。先ほどのサンプルは以下のように書き換えることが可能です。
my $target = "I love Perl!"; my ( $perl, $ruby ) = ( "perl", "\\bruby\\b" ); print $target =~ /\b$perl\b/i, "\n"; print $target =~ /$ruby/i, "\n";
もしかするとマッチ変数を理解してからの方が後方参照は理解しやすいのかもしれません。
後方参照、マッチ変数ともに「正規表現メモリに記憶されている値」という点は同じなのですが、以下のような違いがあります。
端的な例を示せば以下のようになります。
my $target="dummy"; print $1,"\n" if $target =~ /(.)\1/; 実行結果 m
また複数のマッチ変数に格納されるのは以下のような例です。
my $line = "My favorite is Perl!";
if ($line =~ /(\w+) (\w+) is (\w+)!/) {
print "\$1:$1,\$2:$2,\$3:$3\n";
}
実行結果
$1:My,$2:favorite,$3:Perl
マッチ変数は次にパターンマッチは、次のパターンマッチが成功するとリセットされ新しい値がセットされます。逆に次のパターンマッチが失敗した場合は、前回のパターンマッチの値が保持されたままなので注意が必要です。
#そのため、パターンマッチは多くの場合ifやwhileの条件式に使われます
$` : マッチするまでスキップされた部分
$& : 正規表現でマッチした部分全体
$' : マッチしなかった残りの部分
my $line = "My favorite is Perl!";
if ( $line =~ / is (\w+)/) {
print "\$`:$`,\$&:$&,\$':$'\n";
}
実行結果
$`:My favorite,$&: is Perl,$':!
とりあえず、今回はここまでです。次回は置換から。
#前回「前半」としたけど、二回では纏まりません。。
文字列に対してマッチさせられるテンプレートのこと。パターンとも呼びます。
「*」、「+」、「?」のように直前のものが現れる回数(=量)を指定するメタ文字を「量指定子(quantifier)」といいます。
「^」、「$」のようにパターンを文字列の特定の場所に固定するものを「アンカー」といいます。
「\b」のようにワードの両端にマッチするものを特に「ワードアンカー」といいます。Perl正規表現において「ワード」とは「英数字、アンダースコアで構成されるもの」を指します。
. : 改行文字以外の任意の一文字にマッチ * : 直前のものに0回以上マッチ + : 直前のものに1回以上マッチ ? : 直前のものに0or1回マッチ ^ : 文字列の先頭をあらわす $ : 文字列の末尾をあらわす \b : ワードの先頭または末尾をあらわす(片方のみで使用可)
正規表現のパターンを一つのグループに纏める場合はカッコ()でくくります。また後述しますが、()には正規表現メモリに記憶するという役割もあります。
/fred+/ # ex. fred,freddddddddd・・・ /(fred)+/ # ex. fred,fredfredfred・・・ /(fred)*/ # 任意の文字列
ブラケットの中の文字のいずれか一文字にマッチします。
キャレット(^)はアンカーとして使用される場合は「文字列の先頭」をあらわしますが、文字クラス[]内では「否定」を意味します。
/[0-9]/ # ex. 0,1,2,3,4,・・,8,9 /[^0-9]/ # ex. a,b,c,A,B,C,_,・・・
よく使用されるパターンにはショートカットが用意されています。
\d # [0-9] \w # [a-zA-Z0-9_] \s # [\f\t\n\r ]
\sは単体で使われることはまれで、人間から見て空白になっている部分にマッチするために\s*もしくは\s+の形で使用されます。
\s* # 空白がない場合も含む \s+ # 一個以上の空白文字がある
\D, \W, \S
これまでの量指定子は0回以上(*)、1回以上(+)、0/1回(?)しか指定できませんでしたが、これら以外の繰り返し回数を指定したい場合は、以下のように記述します。下限は省略できません。
/a{2,4}/ # aa, aaa, aaaaがマッチ
/a{2,}/ # aa, aaa, aaaa, aaaaa,・・・(カンマ省略不可)
/a{1,3}/ # a, aa, aaaがマッチ
/a{0,3}/ # 任意の文字列
/a{2}/ # aaのみマッチ
キャレットアンカー(^)は文字列の先頭、ドル文字アンカー($)は文字列の末尾をあらわします。
パターンが文字列全体にマッチすることを保証するためにこれらを併用することもあります。よく使われる例として空行をあらわす以下の記述法があります。
/^\s*$/
ワードアンカーは\wでマッチするもの(英数字、アンダースコアのみ)先頭もしくは末尾をあらわします。
/w.rd\b/ # ex. word, sword, ward, award,・・・ /\bw.rd/ # ex. word, wordpress, ward, warded, ・・・ /\bw.rd\b/ # ex. word, ward, ・・・ /\Bword\B/ # word以外のワード
また、ワードには、'(シングルクォート)、"(ダブルクォート)、!(エクスクラメーション)などは含まれませんので「That's "Mr.Larry"!」という文字列に含まれるワードは「That」「s」「Mr」「Larry」になります。
現在のパターンの処理内で正規表現メモリ内に保存された値を参照します。(後述するマッチ変数と対比すると理解しやすいかもしれません)
正規表現メモリにパターンがマッチした部分を格納するためには保存したいパターンをカッコ()でくくります。つまり、カッコにはパターンの一部分をグループにするだけでなく、そのマッチした値を正規表現メモリに格納するという機能があります。
正規表現メモリに格納された値は\1(メモリ1)、\2(メモリ2)、\3(メモリ3)・・\nで呼び出します。
以下の例は、同じ文字が二つ連続で続く文字列にマッチします。
まず、(.)でマッチした任意の一文字を正規表現メモリに記憶し、次に続く一文字とそのメモリ1がマッチするパターンです。
マッチしない場合は判定された一文字がメモリ1に記憶され、その次の一文字がメモリ1の文字とマッチするか判定します。
/(.)\1/ # ex. dummy, putty, livedoor, ・・・
複数のカッコが入れ子になったパターンも可能です。
my $backreference = "2ndsecond2ndsecond"; print "true\n" if $backreference =~ /((2nd)(second))\2/;
正規表現において、優先順位は以下になります。ここでいう優先順位とは「より強く結びつく」という意味です。数学で「+」や「-」よりも「()」が優先されるのと同じイメージです。
カッコ>量指定子>アンカー、並び(順に並べたもの)>|(選択肢を表す縦棒/or)
優先順位の例
/^start|end$/
これは「start」で始まる文字列、もしくはendで終わる文字列を意味します。
/^(start|end)$/
これは「start」だけを含む文字列、もしくは「end」だけを含む文字列を意味します。
/^(\w+)\s+(\w+)$/
これは「ワード」「空白」「ワード」の構成でそれ以外を含まない文字列を意味します。たとえば「Learning Perl」のような文字列です。
最後の例でみるとまずカッコが優先され、次にその中の量指定子が優先されて「ワード」のかたまりと間に挟まれた一個以上の空白が確定し、最後にその並びと先頭、末尾のアンカーが同列で確定するといった感じになります。