ソースコードで学ぶWebプログラミング

Perlプログラミングの基礎

Perlプログラムは、CGIプログラムとしてWebサイトでよく利用されています。CGIとは、Webサーバでプログラムを稼働させるための仕組みです。Perlは、ほとんどのレンタルサーバで利用可能です。

Perlの変数について

Perlでは、代入するデータの型に応じて変数の先頭の文字が変わります。またリファレンスと呼ばれる、変数や関数のアドレスを扱うことが出来ます。配列や連想配列(ハッシュ)には、スカラのみを入れておくことが出来ます。多次元配列を作成する場合は、リファレンスで代入します。

Perlには、変数の有効範囲が存在します。通常は my $s のように、ブロック内でのみ有効なローカル変数(レキシカル変数)として宣言を行ってから代入します。他のブロックでも利用したい変数は、our 宣言を使用します。

# コメントは#以降 # スカラ 文字列、数値、リファレンス $s = 'テキスト'; $s = 123; # 配列 @a = (1,2); # 連想配列(ハッシュ) %h = (a=>'A',b=>'B'); # リファレンス 変数や関数(サブルーチン)のアドレス $r1 = \$s; $r2 = \@a; $r2 = [1,2]; $r3 = \%h; $r3 = {a=>'A',b=>'B'}; $r4 = \&func; # リファレンスから変数の取出や関数(サブルーチン)の実行 $$r1; @$r2; %$r3; $r4->(); # ローカル変数(レキシカル変数) my $my = ''; # グローバル変数(パッケージ変数) our $G = ''; # デフォルト変数 $_ foreach(@a){ # @aの各値が $_ に入る print $_; }

Perlで文字列を扱う方法

文字列の置換や判定では、正規表現を使用できます。正規表現は、文字列内の文字の出現パターンを、文字の集合で特定できる表現方法です。

# 文字列の結合 my $s = $s . $s1; my $s .= $s1; # 文字列の一部取り出し my $s = 'abcdef'; my $s1 = substr($s, 1, 3);#bcd # 文字列の置換 my $s = 'ababab'; my $s =~ s/a/b/;# 1文字置換 bbabab my $s =~ s/ab/a/g;# gで全置換 aaa # リストで変換 my $s = 'ABabab'; my $s =~ tr/ab/12/;#aを1に、bを2に my $s =~ tr/[A-Z]/[a-z]/;#大文字を小文字に # 文字列の比較 if ($s eq '') {}#等しい文字列 if ($s ne '') {}#等しくない文字列 # 文字列の判定 数値のみ if ($s =~ /^\d+$/) {} # 英数字のみ if ($s =~ /^[a-zA-Z0-9]$/) {} # 文字列を区切り文字で配列に my @a = split /,/, $s; # 配列を区切り文字で文字列に my $s = join "\t", @a; # 小文字に my $s = lc 'ABC'; # 大文字に my $s = uc 'abc'; # 文字列を逆順に my $r = reverse $s; # 末尾の改行を除去 $s =~ s/[\r\n]+$//;

Perlで配列、連想配列(ハッシュ)を扱う方法

Perlでは、配列と連想配列(ハッシュ)の変数の先頭文字が異なります。また要素にアクセスする場合は、変数の先頭文字が変わってくるので、注意が必要です。また配列の定義は通常 () を用いますが、リファレンスで定義する時は [] を使用します。

# 配列の作成 my @a = ('a','b','c','d'); my @a = qw/a b c d/; # 配列の要素の取り出し my $s = $a[1]; # 配列の要素数 my $len = $#a; # 配列のリファレンスから取り出し my $r = \$a; my $s = @$r[1]; # 配列の一部取り出し my @b = @a[1,2]; # 配列の結合 push @a, @b; my @c = (@a, @b); # 配列の先頭から取り出す my $s = shift @a; # 配列の最後から取り出す my $s = pop @a; # 配列の先頭に追加 unshift @a, 'scalar'; # 配列の最後に追加 push @a, 'scalar'; # 条件で一部要素を取り出す 例では数値の要素のみ my @m = grep { /^\d+$/ } @a; # 繰り返し処理 foreach(@a){ # $_ に入る } # 変数を指定 foreach my $s (@a) {} # 配列の要素数でループ for (my $i=0;$i<$#a;$i++) {} # すべての要素を変換 例では2倍に my @r = map { $_ * 2 } @a; # 昇順に並び替え @a = sort {$a <=> $b} @a; # 降順に並び替え @a = sort {$b <=> $a} @a; # 配列を逆順に my @r = reverse @a; # 多次元配列 要素はリファレンスに my @a = ( [1,2,3], [4,5,6] );

連想配列(ハッシュ)の定義は通常 () を用いますが、リファレンスで定義する時は {} を使用します。注意点としてPerlでは、連想配列(ハッシュ)の順序は保持されないため、順番に処理する場合は、キーなどで並び替える必要があります。

# ハッシュの作成 %h = (a=>'A',b=>'B'); # ハッシュの要素の取り出し my $s = $h{a}; # ハッシュのリファレンスから取り出し my $r = \%h; my $s = %$r{a}; my $s = $r->{a}; # ハッシュのキーリスト my @keys = keys %h; # ハッシュの値リスト my @vals = values %h; # 繰り返し処理 foreach (my($key, $val) = each %h) {} # キーの存在判定 if (exists $h{$key}) {} # 要素を削除する delete $h{$key}; # キーと値を入れ替える my %r = reverse %h; #多次元ハッシュ 値をリファレンスに my %h = ( a => {k=>'123'}, b => {k=>'456'} );

Perlの制御構文

Perlのif、else文では、elseifをelsifと記述します。また比較判定時は、数値の場合は==、!=を使用し、文字列の場合はeq、neを使用します。

# 条件分岐 if (A) { # Aの時、処理 } elsif(B) { # Bの時、処理 } else { #A,B以外 } # ifのみでは後置できる print $n if $n==1; # 数値を比較 if (1==1) {} #等しい時 if (1!=2) {} #等しくない時 if (1<2) {} # 大小を比較 # 文字列を比較 if ($s eq '') {} #等しい時 if ($s ne '') {} #等しくない時 # 複数の条件 if (A && B) {} #AかつB if (A || B) {} #AまたはB # 条件が偽の時、真 unless (A) {} #Aでない時 # 後置 print $n unless $n==1; # 指定回数繰り返す for (my $i=0;$i<10;$i++) {} # 配列の要素を順番に処理 foreach (@a) {} # ハッシュの要素を処理 foreach (my($key,$val) = keys %h) {} # 繰り返し処理を途中で抜ける foreach(@a){ # 条件Aの時、次へ飛ばす if (A) { next; } # 条件Bの時、終わりにする if (B) { last;} }

Perlでデータ取得を行なう方法

Perlで、ファイルや標準関数からデータを取得する方法です。CGIなどのWebプログラムでは、環境変数や標準入力からユーザーの入力を受け取ることが出来ます。

# ファイル名を定数に use constant FILE_NAME => 'data.txt'; # データ用変数 my $lines = ''; # ファイルから1行ずつ読み込む open FH, '<'.FILE_NAME; while (<FH>) { $lines .= $_; } close FH; # ファイルから一気に読み込む open FH, '<'.FILE_NAME; my $lines = do { local $/;<FH>; }; close FH; # ファイルから配列に読み込む open FH, '<'.FILE_NAME; my @lines = <FH>; close FH; # ファイル情報を取得する my @info = stat FILE_NAME; # ファイルの最終更新日時 my $mtime = $info[9]; # ディレクトリ内のファイル一覧 opendir DIR, 'dir/'; my @files = readdir DIR; closedir DIR; # ファイルを検索する my @files = glob '*.txt'; # 標準入力から読み込む my $buf = do { local $/;<STDIN>; }; # 標準入力から一行ずつ読み込む my $buf; while(<STDIN>){ $buf .= $_; } # 環境変数から取り出す my $ua = $ENV{HTTP_USER_AGENT}; # Unixタイムスタンプを取得する my $time = time; # 日時を取得する 秒,分,時間,日,月,年,曜日 my($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime; # 年を西暦に $year += 1900; # 月0-11 $mon += 1; # 曜日 my @week = qw/日 月 火 水 木 金 土/; $wday = $week[ $wday ]; # ランダムな数値を取得する my $i = rand 10; #10未満の小数 my $i = int rand 10;#10未満の整数 # 関数(サブルーチン)の引数 sub func { my($a,$b) = @_; } # 引数の先頭から取り出す sub func { my $a = shift; my $b = shfit; } # コマンドから返り値を取得する my @r = qx{ ls -l }; # コマンドラインの引数 @ARGV # プログラム名 $0 # OS名 $^0 # プロセスID $$ # プロセスの開始時刻 $^T # ファイルハンドルの現在の行番号 $. # スクリプト上の行番号 __LINE__ # スクリプトのファイル名 __FILE__ # パッケージ名 __PACKAGE__

Perlでデータ変換を行なう方法

入力されたデータを出力するために、適切なフォーマットに変換する処理です。初期状態で使うことが出来る標準関数以外にも、標準モジュールが利用できます。標準モジュールは、インストール作業を行なうことなく利用できます。

# 特定の文字列を除去する # カンマを除去 $txt =~ s/,//g; # タブ文字を除去 $txt =~ s/\t//g; # HTMLエスケープする $txt =~ s/&/&amp;/g; $txt =~ s/</&lt;/g; $txt =~ s/>/&gt;/g; $txt =~ s/"/&quot;/g; $txt =~ s/'/&#39;/g; # 改行コードの統一 $txt =~ s/\r\n/\n/g; $txt =~ s/\r/\n/g; # 末尾の改行を除去 $s =~ s/[\r\n]+$//; # URLデコードする $str =~ tr/+/ /; $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack('H2', $1)/eg; # URLエンコードする $str =~ s/([^a-zA-Z0-9 ])/'%'.unpack('H2', $1)/eg; $str =~ tr/ /+/; # 文字コードを変換する use Encode qw/encode decode/; # Shift_JISの文字列を内部文字列に $str = decode('Shift_JIS', $str); # 内部文字列をShift_JISの文字列に $str = encode('Shift_JIS', $str); # md5値を求める use Digest::MD5 'md5_hex'; my $md5 = md5_hex $str; # 変数を保存用に文字列にする use Storable qw/freeze thaw/; # シリアライズ my $se = freeze \%h; # 文字列を変数に復元する my $h = thaw $se; # JSONデータを変換する use JSON qw/encode_json decode_json/; # JSON文字列を変数に my $j = decode_json($str); # 変数をJSON文字列に my $str = encode_json($j); # ファイル名を変更、移動する rename 'old', 'new'; # ファイル名を変更、移動する use File::Copy qw/copy move/; move 'old', 'new'; copy 'old', 'new'; #コピー

Perlでデータ出力を行なう方法

Perlでファイルにデータを保存したり、出力表示する方法です。CGIなどのWebプログラムでは、ヘッダーを出力後、コンテンツ部分を出力します。コマンドラインのプログラムでは、printで標準出力に出力できます。

# ファイル名を定数に use constant FILE_NAME = 'data.txt'; # 書き込むデータ my $lines = ''; # ファイルに保存する 新規書込、上書き open FH, '>'.FILE_NAME; print FH $lines; close FH; # ファイルに追記する 末尾に追加 open FH, '>>'.FILE_NAME; print FH $lines; close FH; # 読み込んでから保存する ファイルロック open FH, '+<'.FILE_NAME; # ファイルロック flock FH, LOCK_EX; while (<FH>) { # 編集、削除処理 $lines .= $_; } # 書込位置を先頭に seek FH, 0, 0; print FH $lines; # バッファ出力 select((select(FH), $|=1)[0]); # ファイルサイズを整える truncate FH, tell FH; close FH; # 標準出力に表示 print 'Hello'; # ブラウザに出力 cgi print "Content-Type: text/plain\n\n"; print 'Hello'; # 変数の内容を表示する use Data::Dumper; # スカラ print Dumper $s; # 配列 print Dumper \@a; # ハッシュ print Dumper \%h;

Perlの関数(サブルーチン)、クラスの作り方

Perlでは、ユーザーが定義した関数をサブルーチンと言います。引数の受け取り方は、@_またはshiftで受け取ります。$_[0]のように受け取ることも出来ます。関数は、リファレンスとして変数で渡し、受け取り側での実行が可能です。

# 関数(サブルーチン)を作る sub plus { my($a1,$a2) = @_; return $a1 + $a2; } print plus(1, 2); #実行 # shift で取得する記述方法 sub plus { my $a1 = shift; my $a2 = shift; return $a1 + $a2; } print plus(1, 2); #実行 # リファレンス my $r = \&plus; print &$r(3,4);

Perlでは、オブジェクト指向のクラスを実装できます。具体的には、package で名前空間を宣言し、サブルーチン名の重複を防ぎます。コンストラクタは、sub new {} で作成します。デストラクタが必要な場合は、sub DESTROY {} で作成します。

Perlでは、一定の処理を再利用できるようにしたライブラリをモジュールと呼びます。モジュールでは一般的に、モジュール内で定義された関数(サブルーチン)がインポートされて利用できるようになります。クラスファイルもモジュールとして作成できますが、通常メソッドであるサブルーチンはインポートしません。

# クラスを作る package Base; # コンストラクタ sub new { my $class = shift; my %args = @_; return bless \%args, $class; } # メソッドA sub methodA { my $this = shift; my %args = @_; return $args{n} + 10; } # メソッドB sub methodB { my $this = shift; my %args = @_; return $args{n} * 10; } # デストラクタ sub DESTROY {} 1;

継承は、パッケージ変数の@ISAに継承したい基底クラスを代入するだけです。継承とは、オブジェクト指向の考え方で、継承元のプロパティやメソッドを引き継いで利用でき、機能の拡張も行えるものです。

# クラスを継承する package Extends; our @ISA = qw(Base); # メソッドの上書き sub methodA { my $this = shift; my %args = @_; return $args{n} - 20; } 1;

Perlのクラス(モジュール)は、useまたはrequireで利用できます。useは、コンパイル時に読み込まれ、サブルーチンのインポートが行われます。requireでは、実行時に読み込まれ、インポートは行われません。

# クラスの使い方 use Base; use Extends; my $b = Base->new; print $b->methodA( (n=>10) ); print $b->methodB( (n=>10) ); my $e = Extends->new; print $e->methodA( (n=>10) ); print $e->methodB( (n=>10) );