## とほほのperl入門（概要編）

トップ > とほほのperl入門 > 概要編

#### ■ perlとは

◆ 特徴

UNIX上で開発されたファイル処理コマンドですが、MS-DOSにも移植されています。C言語と似た構文をもち、フリーソフトとして配布されています。同様のコマンドにAWKなどがありますが、perlの方が多少記述力は高いようです。WWWにおいてCGIスクリプトを作成する際、CGIスクリプト内部でperlを使用することが多いようです。

◆ perlのバージョン

現在広まっているperlのバージョンには、Ver4と、Ver5があります。Ver5ではオブジェクト指向の概念が取り入れられています。ダブルクォーテーション( " )の中でアットマーク( @ )を使用できないなど、若干仕様の差異があるので注意が必要です。ここではVer4でもサポートされている機能について、説明を行います。

◆ 入手方法

「とほほの厳選ツール集(perl編)」を参照してください。

#### ■ 実行方法

◆ 引数指定

コマンドプロンプトから、次のように入力してリターンキーを押してください。-eオプションは次に続く'...'をperlスクリプトとして実行することを意味します。

    perl -e "print 'Hello world!!';"

◆ ファイル指定

次のような内容のテキストファイル(仮にhello.plとします)を作成してください。

    print "Hello world!!\n";

上記で作成したファイルの名前を引数にして、コマンドプロンプトから次のようにperlコマンドを実行してください。

    perl hello.pl

◆ 自己完結型

これはUNIXでのみ使用可能な方法です。次のような内容のテキストファイル(仮にhello.plとします)を作成してください。

    #!/usr/local/bin/perl
    print "Hello world!!\n";

1行目は、perlコマンドが置かれているディレクトリに応じて変更してください。#! というのはUNIXのファイル特有の記号で、その後ろに続くコマンド（上記の場合はperl）を起動するためのおまじないです。chmodコマンドを次のように使用してファイルのモードを変更してください。

    chmod 755 hello.pl

コマンドプロンプトから次のように入力して実行してください。

    hello.pl

CGIスクリプトでperlを使用する場合は、ほとんどこの３番目の方法になります。

◆ 標準入力読み込み型

perlコマンドの引数が指定されていない場合、標準入力から読み取った内容をperlスクリプトとして実行します。

    echo 'print "Hello World!!\n";' | perl      # UNIXの場合
    echo  print "Hello World!!\n";  | perl      # Windowsの場合

#### ■ 基礎知識

◆ 簡単な実行例

次の例では変数$xxに5を、変数$yyに2を代入し、その合計を変数$zzに代入した後、結果を表示します。

    $xx = 5;
    $yy = 2;
    $zz = $xx + $yy;
    print "答は $zz です。\n";

◆ これだけは覚えて

以後の説明を楽にするために、上記の実行例についてこれだけは覚えておいてください。

- このスクリプトを実行すると「答は 7 です。」と表示されます。
- それぞれの行を文(statement)と呼びます。
- 文の末尾にはセミコロン( ; )をつけます。
- 文は最初の文から順番に実行されていきます。
- $xx = 5; は「変数$xxに値5を代入する」という意味を持ちます。
- 通常の変数にはダラー( $ )を付けて表わします。
- print は「"....." を表示せよ」という命令です。
- \n は「改行せよ」という意味を持ちます。

#### ■ 定数・変数・値

◆ 数値

perlでは次のような表現の数値を使用することができます。

    12345          # 整数
    12_345         # 整数(アンダーバーは無視されます)
    123.45         # 小数
    1.23E45        # 指数
    0x12345        # 16進数
    012345         # 8進数

◆ 文字列

文字列はダブルクォーテーション( " )またはシングルクォーテーション( ' )で囲みます。

    "abc"
    'abc'

ダブルクォーテーションの中ではシングルクォーテーションを、シングルクォーテーションの中ではダブルクォーテーションを使用することができます。

    "abc'def'ghi"
    'abc"def"ghi'

どうしてもダブルクォーテーション中にダブルクォーテーションを使用したい時は、バックスラッシュ( \ )を用います。

    "ダブルクォーテーション(\")を使う"

ダブルクォーテーションの中では変数やバックスラッシュ( \ )が評価されますが、シングルクォーテーションの中では単なる文字として認識されます。

    $xx = 7;
    print "答は$xxです\n";     # 「答は7です」と表示されます
    print '答は$xxです\n';     # 「答は$xxです\n」と表示されます

◆ エスケープシーケンス(\)

ダブルクォーテーションで囲まれた文字列の中ではバックスラッシュ( \ )に続く文字は特別な意味を持ちます。

    \t        # タブ文字（\x09）
    \n        # 改行（\x0a）
    \r        # リターン（\x0d）
    \f        # フォームフィード（\x0c）
    \b        # バックスペース（\x08）
    \a        # アラーム（\x07）
    \e        # エスケープ文字（\x1b）
    \033      # 文字コード(8進数)
    \x1b      # 文字コード(16進数)
    \c[       # コントロール文字
    \l        # 次の１文字を小文字にする
    \u        # 次の１文字を大文字にする
    \L        # \Eまでの文字列を小文字にする
    \U        # \Eまでの文字列を大文字にする
    \E        # \Lや\Uを終了させます
    \" など   # ダブルクォーテーション( " )自身を表わします。

◆ スカラー変数($XXX)

ダラー( $ )で始まる変数はスカラー変数と呼ばれます。スカラー変数には数値や文字列をひとつだけ代入することができます。

    $xx = 5;          # スカラー変数$xxに数値5を代入します。
    $xx = "abc";      # スカラー変数$xxに文字列abcを代入します。
    print "$xx\n";    # スカラー変数$xxの値を参照して表示します。

通常、何も初期化していない変数はヌルまたは0として扱われますが、perlの処理系によっては(バグなのか？)ゴミの値になることがあるようですので、最初に $xx = 0; と初期化しておいた方が無難なようです。(1998.8.23追記)

◆ 配列(@XXX)

$変数名[添え字]という形式で配列を表現することができます。添え字には0,1,2,3...を指定します。

    $xx[0] = 5;        # 0番目の要素に数値5を代入します。
    $xx[1] = "abc";    # 1番目の要素に文字列abcを代入します。
    print "$xx[1]\n";  # 1番目の要素の内容abcを表示します。

次の形式のようにアットマーク( @ )を用いて配列全体や配列の範囲を表現します。

    @xx = ( "aa", "bb" );       # 配列xxにaa,bbを代入します。
    $xx[2] = 'cc';              # 2番目の要素にccを代入します。
    @xx[3..4] = ( "dd", "ee" ); # 3〜4番目の要素にdd,eeを代入します。
    print @xx;                  # aa bb cc dd eeを表示します。
    print $xx[0];               # 0番目の要素aaを表示します。
    print @xx[0..2];            # 0〜2番目の要素aa bb ccを表示します。

$#変数名 は、@変数名 の配列の最後の添え字（個数から１をひいたもの）を表わします。

    @xx = ( "aa", "bb", "cc", "dd" );
    print "$#xx\n";                     # 3と表示されます。

次のような配列同士の一括代入が可能です。

    ( $year, $mon, $day ) = ( 1997, 2, 3 );

()は空配列を示します。

    @xx = ();

配列を操作するためにいろいろな関数が用意されています。

    $x = shift(@xx);      # 最初の要素を取り除き、これを返す
    $x = pop(@xx);        # 最後の要素を取り除き、これを返す
    push(@xx1, $xx);      # 最後に要素を追加する
    push(@xx1, @xx2);     # @xx1 の後ろに @xx2 の要素を追加する
    splice(@xx, 3, 2);    # $xx[3..4](2個)を取り除き、これを返す
    reverse(@xx);         # @xxの要素を逆順にしたものを返す
    sort(@xx);            # @xxの要素をソートした結果を返す
    @x = split(/,/, $s);  # 文字列$sを","で分割してその配列を返す

すべての配列要素について処理を行うには次のようにします。

    foreach $i (@xx) {
        print "$i\n";
    }

◆ 連想配列(%XXX)

$変数名{文字列}という形式で、文字列を添え字とした配列を表現することができます。

これを連想配列と呼びます。

    $xx{"Tanaka"} = "man";
    $xx{"Suzuki"} = "female";

%変数名 という形式で、連想配列全体を表現することができます。次の例では、$xx{"tanaka"} という変数に "man" が、$xx{"suzuki"} という変数に "female" が代入されます。

    %xx = ( "tanaka", "man", "suzuki", "female" );
    print $xx{"tanaka"};
    print $xx{"suzuki"};

すべての連想配列要素について処理を行うには次のようにします。

    while (($name, $value) = each(%xx)) {
        print "$name = $value\n";
    }

◆ 変数名に使える文字

変数名にはアンダーバー( _ )を含む半角英数文字を使用することができます。最初の1文字は数値であってはなりません。

    $my-name       # × ハイフン( - )は使用できない
    $32xx          # × 数値で始まってはならない

$変数名, @変数名, %変数名 はそれぞれ個別の名前空間をもっているので、$xx と @xx はそれぞれ無関係の変数になります。

◆ 数値と文字列

perlでは、数値と文字列の扱いはあいまいで、その都度数値として評価されたり、文字列として評価されたりします。次の例では1行目では文字列の 12345 が代入されていますが、2行目では加算演算子により臨機応変に数値として解釈され、12350 が表示されます。

    $xx = "12345";
    print $xx + 5;

◆ 真(true)と偽(false)

if ( ) 文の中などの真偽判断では、空文字列(""), ゼロ(0), ゼロを示す文字列("0")の値は偽として、それ以外は真として扱われます。true や false という特殊な変数がある訳ではないので、return(false) は、"false" という文字列を返却することになります。真偽を返却する関数を評価する際は if (&func() == true) ではなく、if (&func()) と評価してください。

◆ 定義済みと未定義

変数には定義済みの状態と未定義の状態があります。最初は未定義ですが、最初に何か値が代入されると定義済みになります。定義済みかどうかを調べるためにdefined()を使用できます。

◆ 省略時の変数($_)

perlには省略の美学というものがあり、いろいろな箇所で変数名を省略することができます。省略した場合は、$_ を指定したものとみなされます。

    while (<IN>)  # while ($_ = <IN>) と同じ意味
    print;        # print $_; と同じ意味
    /^From:/      # $_ =~ /^From:/ と同じ意味

これを用いて、

    while ($xx = <IN>) {
        if ($xx =~ /^From:/) { print $xx; }
    }

というコードは、次のように書くことができます。

    while (<IN>) {
        if (/^From:/) { print; }
    }

◆ 環境変数($ENV{'XXX'})

perlでは%ENVという特別な連想配列変数を用いて環境変数の値を読み取ったり設定したりすることができます。

    print $ENV{'PATH'};        # 環境変数PATHの値を表示する
    $ENV{'TZ'} = "JST-9";      # 環境変数TZに値を設定する

◆ コマンド引数($ARGV)

コマンドラインからの引数を得るために、次の変数が使用できます。

    @ARGV          # 引数の配列
    $#ARGV         # 配列の個数−1
    $ARGV[0]       # 最初の引数
    $ARGV[$#ARGV]  # 最後の引数

◆ シグナル($SIG{'XXX'})

シグナルはプログラムに送信される非同期のメッセージです。例えば、強制終了を明示された時はSIGTERMシグナルが、プロセス間の通信が切断された時はSIGPIPEメッセージが、設定しておいた時刻になったらSIGALRMシグナルが、プログラムに対して送信されます。

    $SIG{'TERM'} = "sigexit";

と宣言することにより、そのプログラムに SIGTERMシグナルが送信された時に、&sigexit()関数が実行されるようになります。

◆ その他の特殊変数($X)

$_ の他にも、次のような特殊変数があります。

    $.       # ファイルから読み込み時の現在の行数
    $/       # 入力時のレコード区切り文字。通常は改行。
    $,       # 出力時の項目区切り文字。print @xx; の時に有効
    $"       # 出力時の項目区切り文字。print "@xx"; の時に有効
    $\       # 出力時の行末文字。print "$xx"; の後ろに付加される
    $#       # 出力時の数値形式。通常は"%.20g"。詳細はprintfを参照
    $%       # 出力時の現在のページ番号
    $=       # 出力時の現在のライン数
    $-       # 出力時の残り行数
    $~       # 出力時のフォーマット名(デフォルトはハンドル名と同じ)
    $^       # 出力時のヘッダフォーマット名
    $|       # 0以外が代入されると出力をバッファリングしなくなる。
    $$       # プロセスID
    $?       # 最後に実行されたコマンドのステータス
    $&       # パターンマッチにマッチした部分文字列
    $`       # パターンマッチにマッチした部分の前側の文字列
    $'       # パターンマッチにマッチした部分の後側の文字列
    $+       # パターンマッチの最後の()に対応する文字列
    $*
    $0       # perlスクリプトのコマンド名
    $1       # パターンマッチの際の1番目の()に対応する文字列
    $[       # 配列の最初の添え字。通常は0。
    $]       # perlのバージョン情報
    $;       # 高次元連想配列の添え字の区切り文字
    $!       # エラー番号、もしくはエラー文字列
    $@       # 直前のevalコマンドのエラーメッセージ
    $<       # このプロセスの実ユーザーID
    $>       # このプロセスの実効ユーザーID
    $(       # このプロセスの実グループID
    $)       # このプロセスの実効グループID
    $
    $^D      # デバッグフラグの値
    $^F      # システムファイルディスクリプタの最大値
    $^I      # -iオプションで指定した拡張子の名前
    $^L
    $^P      # デバッガが使用する内部フラグ
    $^T      # スクリプトを実行した時刻
    $^W      # 警告スイッチの現在値
    $^X      # perl自身の起動時の名前
    @_       # サブルーチンへの引数
    @INC     # perlライブラリ検索ディレクトリ
    %INC     # 読み込まれたライブラリファイルの配列
    __LINE__ # スクリプト中の現在の行数
    __FILE__ # スクリプトのファイル名

◆ バイナリデータ
pack と unpack はバイナリデータに対して、文字表現、数値表現の変換を行います。

次の例を実行すると [ABC] [65 66 67] [ABC] と表示されます。

    $xx = "ABC";                      # 文字列としての代入
    @yy = unpack("C*", $xx);          # 数値の配列に変換
    $zz = pack("C*", @yy);            # 数値の配列を文字列に変換
    print "[$xx] [@yy] [$zz]\n";

#### ■ 演算子

◆ 代入

次の例は「$xxは5である」という意味ではなく、「変数$xxに値5を代入する」という意味を持ちます。

    $xx = 5;

◆ 算術演算子

perlでは次のような演算子を用いることができます。

    $xx = 5 + 2;        # 足し算
    $xx = 5 - 2;        # 引き算
    $xx = 5 * 2;        # 掛け算
    $xx = 5 / 2;        # 割り算
    $xx = 5 % 2;        # 5を2で割った余り(=1)
    $xx = 5 ** 2;       # 5の2乗(=25)
    $xx++;              # $xxの値が1加算されます
    $xx--;              # $xxの値が1減算されます
    $xx **= 2;          # $xx = $xx ** 2; と同様

◆ ビット演算子

    $xx | $yy           # $xxと$yyのOR
    $xx & $yy           # $xxと$yyのAND
    ~$xx                # $xxのビットをすべて反転させたもの
    $xx << 2            # $xxを2ビット左シフト
    $xx >> 2            # $xxを2ビット右シフト(上位ビットには符号ビットを埋める)

◆ 数値比較演算子

if文やwhile文の中で数値に対する条件判断を行うには、次のような比較演算子を用います。

    if ($xx == $yy)            # $xxと$yyが等しければ
    if ($xx != $yy)            # $xxと$yyが異なっていれば
    if ($xx < $yy)             # $xxが$yyより小さければ
    if ($xx > $yy)             # $xxが$yyより大きければ
    if ($xx <= $yy)            # $xxが$yyより等しいか小さければ
    if ($xx >= $yy)            # $xxが$yyより等しいか大きければ
    if (($xx==2) && ($yy==3))  # $xxが2、かつ、$yyが3であれば
    if (($xx==2) || ($yy==3))  # $xxが2、もしくは、$yyが3であれば
    if ($xx <=> $yy)           # 比較結果を -1, 0, 1 のいずれかで返す

◆ 文字列比較演算子

文字列に対しては次のような演算子を用います。

    if ($xx eq $yy)            # $xxと$yyが等しければ
    if ($xx ne $yy)            # $xxと$yyが異なっていれば
    if ($xx lt $yy)            # $xxが$yyより小さければ
    if ($xx gt $yy)            # $xxが$yyより大きければ
    if ($xx le $yy)            # $xxが$yyより等しいか小さければ
    if ($xx ge $yy)            # $xxが$yyより等しいか大きければ
    if ($xx =~ /ABC/)          # $xxがABCという文字を含んでいれば
    if ($xx !~ /ABC/)          # $xxがABCという文字を含んでいなければ
    if ($xx cmp $yy)           # 比較し、-1, 0, 1 のいずれかで返す

◆ 条件演算子

A ? B : C は、Aが真であればBを、偽であればCを値とします。下記の例では$yyの値が5未満であれば"small"を、さもなくば"big"を$xxに代入します。

    $xx = ($yy < 5) ? "small" :  "big";

◆ マッチング

変数$xxと正規表現にマッチするか否かを調べます。

    if ($xx =~ /[0-9]+/)        # 正規表現にマッチすれば
    if ($xx !~ /[0-9]+/)        # 正規表現にマッチしなければ

置換文字列の中に括弧を書くと、その括弧に対応した文字列を $1, $2, ... で参照することができます。次の例は、12:34:56の秒数を求めます。

    $xx = "12:34:56";
    if ($xx =~ /([0-9][0-9]):([0-9][0-9]):([0-9][0-9])/) {
        print $1 * 3600 + $2 * 60 + $3;
    }

◆ 文字列連結

文字列を連結します。

    $xx = $yy . $zz;    # 文字列 $yy と 文字列 $zz を連結します
    $xx .= $yy;         # $xx = $xx . $yy と同様です。

◆ 文字列置換

次の例は、文字列$xxの中の abc という文字列をすべて大文字の ABC に変換します。最初の s は置換することを表わします。最後の g を省略した場合は、最初に現れたabcを1度だけ置換します。

    $xx =~ s/abc/ABC/g;

abc の部分には後で述べる正規表現を指定することができます。(...)を使用すると(...)に対応する文字列を $1, $2, $3,...で参照することができます。次の例は、"12:34:56" を "12時34分56秒" に置換します。

    $xx = "12:34:56";
    $xx =~ s/([0-9][0-9]):([0-9][0-9]):([0-9][0-9])/$1時$2分$3秒/g;

最後の g の部分をオプションと呼びます。g オプションは、見つかったすべてのパターンを置換します。

    $xx = "abcabcabc";
    $xx =~ s/abc/ABC/;   # ABCabcabc となります。
    $xx =~ s/abc/ABC/g;  # ABCABCABC となります。

eオプションは、ABCの部分を文字列ではなく、perlの実行文と解釈して、その結果に置換します。次の例は、12:34:56を秒数に置換します。

    $xx = "12:34:56";
    $xx =~ s/([0-9][0-9]):([0-9][0-9]):([0-9][0-9])/$1*3600+$2*60+$3/e;

区切り文字（/）はアルファベット、数字以外の任意記号を用いることができます。置換文字中の区切り文字はバックスラッシュ(\)を用いてエスケープ（区切り文字という意味を無効化する）必要がありますが、区切り文字を変更してやると、その煩わしさを解消できます。以下の2行は同じ処理を行います。

    $xx =~ s/http:\/\/www.yyy.zzz\//http:\/\/aaa.bbb.ccc\//;
    $xx =~ s|http://www.yyy.zzz/|http://aaa.bbb.ccc/|;

区切り文字としてシングルクォーテーション（'）を用いると、置換文字の変数解釈などを行わないようになります。

    $yy = "YY";
    $xx = "999"; $xx =~ s/[0-9]/$yy/g;    # YYYYYY となる
    $xx = "999"; $xx =~ s'[0-9]'$yy'g;    # $yy$yy$yy となる

次の例は、URLエンコードされた %20 のような文字列をデコードします。CGIなどではよく使用される構文です。

    $xx =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg;

◆ 繰り返し演算子

    print '-' x 80;     # '-'を80個表示します。
    $xx x= 80;          # $xx を80回繰り返したものを$xxに代入します。
    @ones = (1) x 80;   # 80個の(1, 1, 1, 1, ......, 1)と同様です。

◆ 範囲演算子

    @xx = (0 .. 5);     # @xx = (0, 1, 2, 3, 4, 5);と同様。
    for (0 .. 5)        # for ($_ = 0; $_ <= 5; $_++) と同様

◆ ファイル検査

次のようにして、ファイルが書き込み可能かどうかなどを検査することができます。

    # ファイル名で指定する
    if (-w "file.txt") { print "書き込み可能Write\n"; }

    # ファイルハンドルで指定する
    open(IN, "file.txt");
    if (-r IN) { print "読み込み可能\n"; }
    close(IN);

    # アンダーライン( _ )は最後に検査されたファイルを示す
    if (-r "file.txt") { print "読み込み可能\n"; }
    if (-w _) { print "書き込み可能\n"; }

    # stat()を用いた例
    stat("file.txt");
    if (-r _) { print "読み込み可能\n"; }

次のような検査が可能です。（UNIXでしか意味の無い物も多いです）

    -r      # 読み込み可能であれば
    -w      # 書き込み可能であれば
    -x      # 実行可能であれば
    -o      # 自分のものであれば
    -R      # 実uid/gidで読み込み可能であれば
    -W      # 実uid/gidで書き込み可能であれば
    -X      # 実uid/gidで実行可能であれば
    -O      # 実uidのものであれば
    -e      # 存在すれば
    -z      # サイズが0であれば
    -s      # サイズが0でなければ(ファイルサイズを返す)
    -f      # 通常ファイルであれば
    -d      # ディレクトリであれば
    -l      # シンボリックリンクであれば
    -p      # 名前付きパイプであれば
    -S      # ソケットであれば
    -b      # ブロック型スペシャルファイルであれば
    -c      # キャラクタ型スペシャルファイルであれば
    -u      # setuidビットが立っていれば
    -g      # setgifビットが立っていれば
    -k      # stickyビットが立っていれば
    -t      # ファイルハンドルが tty としてオープンされていれば
    -T      # ファイルがテキストファイルであれば
    -B      # ファイルがバイナリファイルであれば
    -M      # perl起動時における、ファイルの更新時刻からの日数
    -A      # perl起動時における、ファイルの参照時刻からの日数
    -C      # perl起動時における、ファイルの作成時刻からの日数

#### ■ 制御構文

◆ if elsif else文

if(...)は単純な条件判断をする際に用います。

    if ($xx > 5) {
        print "5より大きい\n";
    } elsif ($xx == 5) {
        print "5ちょうど\n";
    } else {
        print "5より小さい\n";
    }

◆ unless文

unless(...)はif(...)と逆の意味を持ちます。

    unless ($xx > 5) {
        print "5より大きくない\n";
    }

◆ while文

while(...)は、(...)が真のあいだ{...}を繰り返します。

    $xx = 1;
    while ($xx < 10) {
        print "xx = $xx\n";
        $xx++;
    }

◆ until文

until(...)は、(...)が真になるまで{...}を繰り返します。

    $xx = 1;
    until ($xx > 10) {
        print "xx = $xx\n";
        $xx++;
    }

◆ do文

do{...}を用いて、if(...), unless(...), while(...), until(...)の構文順序を変更することができます。

    do { print "xx = $xx\n"; } if ($xx == 5);
    do { print "xx = $xx\n"; } unless ($xx == 5);
    do { print "xx = $xx\n"; $xx++; } while ($xx < 5);
    do { print "xx = $xx\n"; $xx++; } until ($xx > 5);

do {...}の中身が単文の場合は、do { ; }を省略することができます。

    print "xx = $xx\n" if ($xx == 5);
    print "xx = $xx\n" unless ($xx == 5);
    print "xx = $xx\n" while ($xx++ < 5);
    print "xx = $xx\n" until ($xx++ > 5);

◆ for文

for(...)は指定回数処理を実行する際に用います。次の例は最初に$xxに1を代入して、$xxが10以下であるあいだ、$xxを1ずつ加算しながら、printを実行することを意味します。

    for ($xx = 1; $xx <= 10; $xx++) {
        print "xx = $xx\n";
    }

次の構文も上と同じ動作をします。

    @xx = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    for (@xx) {
        print "xx = $_\n";
    }

また、次の構文も同様です。

    for (1..10) {
        print "xx = $_\n";
    }

◆ foreach文

foreach(...)は配列の各要素に対して処理を行います。

    @xx = ( 11, 22, 33, 44 );
    foreach $i (@xx) {
        print "i = $i\n";
    }

◆ last文

C言語のbreakと同じ動きをします。最も内側のループを抜けます。

    while (<IN>) {
        if ($_ eq "END") {
            last;
        }
    }

◆ next文

C言語のcontinueと同じ動きをします。最も内側のループを繰り返します。

    while (<IN>) {
        if ($_ =~ /^#/) {
            next;
        }
    }

◆ continue文

例えば、次の2つのコードは同じ動作となります。

    for ($xx = 1; $xx < 10; $xx++) {
        print "$xx\n";
    }

および

    $xx = 1;
    while ($xx < 10) {
        print "$xx\n";
    } continue {
        $xx++;
    }

#### ■ 入出力

◆ ファイル読み込み

ファイルから各行を読み込むには次のようにします。ここで、INのような変数をファイルハンドルと呼びます。ファイルハンドルはINでもHEHEHEでもGUGOGOでも、自分の好きな名前で構いません。ファイルを読み書きする際は、ファイル名ではなく、このファイルハンドルを指定することになります。

    open(IN, "datafile.txt");
    while ($xx = <IN>) { print $xx; }
    close(IN);

変数 $xx を省略した場合は、省略時の暗黙の変数 $_ が使用されます。前記の例は次のように記述することもできます。

    open(IN, "datafile.txt");
    while (<IN>) { print; }
    close(IN);

これは、次の記述と同じ意味を持ちます。

    open(IN, "datafile.txt");
    while ($_ = <IN>) { print $_; }
    close(IN);

もっと簡単には、次のようにも記述することができます。

    open(IN, "datafile.txt");
    print while (<IN>);
    close(IN);

次のようにファイルの各行を配列に一括して読み込むこともできます。

    open(IN, "datafile.txt");
    @xx = <IN>;
    foreach $yy (@xx) {
        print $yy;
    }
    close(IN);

また、次のようにすると、コマンドラインで指定したファイルをひとつづつオープンし、それを順次読み込むことができます。

    while (<>) {
        print;
    }

これは、だいたい、次のような処理と同等になります。

    for ($i = 0; $i <= $#ARGV; $i++) {
        open(IN, $ARGV[$i]);
        while (<IN>) {
            print;
        }
        close(IN);
    }

◆ ファイル書き込み

ファイルに何かを書き込むには次のようにします。

    open(OUT, "> datafile.txt");
    print(OUT "こんにちわ\n");
    close(OUT);

ファイルに追加書き込みするには次のようにします。

    open(OUT, ">> datafile.txt");
    print(OUT "こんにちわ\n");
    close(OUT);

◆ 外部コマンド実行

出力結果を外部コマンドに渡すこともできます。次の例ではprint文の出力をnkfコマンドでJISコードに変換して出力しています。

    open(OUT, "| /usr/local/bin/nkf -j");
    print(OUT "こんにちわ\n");
    close(OUT);

また、外部コマンドの出力結果を読み取ることもできます。次の例では、datafile.txt の中身を nkf コマンドでEUCに変換したデータを読み込みます。

    open(IN, "/usr/local/bin/nkf -e datafile.txt |");
    while (<IN>) {
        print $_;
    }
    close(IN);

単に外部コマンドを実行するだけであれば、次のようにします。

    system("/usr/local/bin/nkf -e datafile.txt");

◆ ハンドル

前述の IN や OUT, STDOUT などをハンドルと呼びます。ハンドルは通常、大文字を用いて表わします。

◆ 標準入出力

ハンドルは open() を用いて作成する他に、次の3つのハンドルがあらかじめオープンされています。

    STDIN     標準入力
    STDOUT    標準出力
    STDERR    標準エラー出力

標準入力から各行を読み込むには次のようにします。

    while (<STDIN>) {
        print;
    }

< >の中のSTDINは省略することができます。以下のようにした場合、コマンドの引数で何も指定しなかった場合は標準入力から、コマンドの引数でファイルのリストを指定した場合は、そのファイルを順次読み取ることができます。

    while (<>) {
        print;
    }

◆ 改行コード

Windowsでは \r\n、Macintoshでは \r、UNIXでは \n のように、OSによって改行コードが異なります。入力ファイルの改行コードに関わらず、自分の環境に適した改行コードに変換するには以下のようにします。（UNIX、Windowsで確認。Macintoshでは未確認）

    while (<IN>) {
        s/[\r\n]*$//;
        print "$_\n";
    }

Windowsでは $_\n ではなく $_\r\n にすべきだと思われるかもしれませんが、Windows上で上記のスクリプトを実行した場合、STDOUT や OUT への出力は \n が \r\n に自動変換されて書き込まれます。これをテキストモードと呼びます。この自動変換を行わないようにしたい時は binmode()を用いてバイナリモードにしてください。UNIXでは常にバイナリモード（というか、テキストモードとバイナリモードの動作が同じ）となります。

任意の改行形式のファイルを読みとって、UNIXの改行形式のファイルに変換する例は次のようになります。

    open(IN, "any.txt");
    open(OUT, "> unix.txt");
    binmode(OUT);
    while (<IN>) {
        s/[\r\n]*$//;
        print OUT "$_\n";
    }
    close(IN);
    close(OUT);

◆ バイナリ読み込み

例えば次のようなコードを用いる事により、ファイルをバイナリデータとして読み込む事ができます。

    ($dev, $ino, $mode, $nlink, $uid, $gid,
     $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks)
            = stat("binary.dat");
    open(IN, "binary.dat");
    binmode(IN);
    sysread(IN, $buf, $size);
    close(IN);
    @data = unpack("C*", $buf);
    for $x (@data) {
        :
    }

binmode()はUNIXでは必要ありませんが、Windowsで \n が \r\n に変換されてしまうのを防ぐために必要です。

#### ■ サブルーチンとライブラリ

◆ サブルーチン(sub)

次のようにしてサブルーチンを宣言することができます。

    sub add {
        return($_[0] + $_[1]);
    }

サブルーチンは宣言しただけでは実行されませんが、次のように呼び出す事によって実行されます。

    print &add(3, 5);

サブルーチンに渡された引数は特殊配列変数 @_ に格納されるので、$_[0], $_[1], $_[2],...として参照することができます。return(...)は ... をサブルーチンの戻り値としてサブルーチンを終了することを意味します。return()を省略した場合は、サブルーチンの最後の文(statement)の結果がサブルーチンの戻り値となります。

C言語と違ってperlではほとんどの変数はグローバル変数なので、サブルーチンの外でも中でも同じ名前で参照・設定できます。サブルーチンの中でローカル変数(サブルーチンの中だけで有効な名前)を使用するには次のようにします。

    sub add {
        local(@a) = @_;
        local($i);
        local($ans) = 0;
        for ($i = 0; $i <= $#a; $i++) {
            $ans += $a[$i];
        }
        return($ans);
    }

特殊変数 @_ の代入部を次のようにすると、perl Ver4.X ではうまくいかない(値が渡されない)場合があるので注意しましょう。

    sub add {
        local(@a);
        local($i);
        @a = @_;
           :

◆ 参照渡し

サブルーチンに複数の配列を渡す際や、pop, push を用いてサブルーチン内で配列の個数を変更する際など、「*名前」という参照渡しを用いると都合が良い場合があります。

    sub listprint {
        local(*list1, *list2) = @_;
        print "[ "; print @list1; print " ]\n";
        print "[ "; print @list2; print " ]\n";
    }
    @xx = (1, 2, 3);
    @yy = (4, 5, 6);
    &listprint(*xx, *yy);

◆ ライブラリ

perlのライブラリは通常、calc.pl のように拡張子 .pl のファイル名で提供されます。これは環境変数PERLLIBで指定されたディレクトリやカレントディレクトリから検索されます。特殊変数@INCはperlライブラリを探す場所のリストを保持しています。

次は簡単なperlライブラリの例(calc.pl)です。

    ;#####################
    ;# perl Library calc
    ;#####################
    package calc;
    sub add {
        return($_[0] + $_[1]);
    }
    1;

コメントを記述する場合はシャープ( # )の前にセミコロン( ; )を追記する必要がある場合もあるそうです。

package は、これ以降(ファイルの終わりか次の package が現れるまで)のスクリプトを calc パッケージとして扱うことを宣言します。パッケージの内部では独自の変数名空間を持てるので、パッケージの内部と外部の間でたまたま変数名が重複する事による悲劇を解除することができます。パッケージ内のサブルーチンや変数は、「パッケージ名'名前」で指定します。

最後の1は特に何をするものでもありませんが、このライブラリを require で読み込む際のおまじないだと思ってください。

このライブラリを呼び出して実行するには次のようにします。

    require "calc.pl";
    print &calc'add(3, 5);

#### ■ その他

◆ コメント(#)

perlスクリプト中でシャープ( # )から行末まではコメントとして無視されます。

    $xx = 5;    # ここから行末までは無視されます

◆ 正規表現

次の例の/EFG/などのように、perlでは文字列の特徴を表現することができます。

    $xx = "ABCDEFG";
    if ($xx =~ /EFG/) {          # 文字列$xxが文字列EFGを含んでいたら
        print "含んでいる\n";
    }

上記のif文の( )内は「変数$xxの中に、EFGという文字列が含まれていたら」という意味になります。このように、ある文字列が指定した特徴に一致することを「マッチする」と言います。正規表現のEFGの箇所には次のような表記を用いることができます。^ABC, ABC$ 以外はすべて、「その文字列を含んでいたら」という意味になります）

      A           Aという文字
      ABC         ABCという文字列
      A+          1個以上連続したA(A, AA, AAA, ...)
      A*          0個以上連続したA(  , A, AA, AAA, ...)
      .           1つの任意文字(A, B, C, ...) (\nを除く)
      ?           0または1つの任意文字(  , A, B, C, ...)
      ^ABC        ABCで始まっていたら
      ABC$        ABCで終わっていたら
      [ABC]       A,B,Cのいずれか1文字
      [A-Z]       A〜Zまでのいずれか1文字
      [A-Za-z0-9] A〜Z, a〜z, 0-9までのいずれか1文字
      [^ABC]      A,B,C以外の文字
      [^A-Z]      A〜Z以外の文字
      A|B|C       AまたはBまたはC
      \w          英数文字かアンダーバー( _ )。[a-zA-Z0-9_]と同様
      \W          \w以外の文字
      \d          数値文字。[0-9]と同等
      \D          \d以外の文字
      \s          空白文字
      \S          \s以外の文字
      \b          単語の区切り？？？？？？
      \B          \b以外の文字
      \nなど      エスケープシーケンス文字の\nなど
      (ABC)       ABCという文字列。カッコ部分は後で参照可能です。

これらを組み合わせてたとえば、「perlの変数名に用いることができる名前」は次のように表わされます。

    ^[A-Za-z_][A-Za-z0-9_]*$

正規表現中では下記の文字は特殊な文字（メタ文字）として扱われますので、これらの文字を表わすには \+, \*, \?, ...のようにします。

    + * ? . ( ) [ ] { } | \

括弧(...)で囲んだ部分は、正規表現内では \1, \2, \3, ...として参照できます。1, 2, 3, ...は、正規表現中で括弧が現れる順番を示します。

       ^(...)\1    最初の3文字がもう一度繰り返す(ABCABCなど)

また、正規表現の外ではマッチの結果を次のように参照できます。

    $1, $2, $3, ...      (  )の部分にマッチした文字列
    $+                   最後にマッチした(  )部分
    $&                   マッチした部分の文字列
    $`                   マッチした部分から前側の文字列
    $'                   マッチした部分から後側の文字列

たとえば、次のように使用することができます。

    if ($time =~ /^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$/) {
        $hour = $1;
        $min = $2;
        $sec = $3;
    }

◆ <<によるデータ表記

次の例では、「Hello World!!」という文字列を3行表示します。END_OF_DATAの部分はどんな文字列でも使用できますが、(1)<<の後ろにはスペースを空けてはならない、(2)最初のEND_OF_DATAの後ろにはセミコロン(;)が必要、(3)最後のEND_OF_DATAの後ろには改行が必要、など注意点も多いので気を付けてください。

    print <<END_OF_DATA;
    Hello World!!
    Hello World!!
    Hello World!!
    END_OF_DATA

次の例では、変数 $msg に 3行のメッセージを代入しています。

    $msg = <<END_OF_DATA;
    This is Japan.
    That is America.
    That is England.
    END_OF_DATA

次の例でも同様に、「Hello World!!」を3行表示します。この場合、空行がデータの終わりを示します。）

    print << x 3;
    Hello World!!

◆ リファレンス

Perl5からはC言語のポインタに相当するリファレンスがサポートされました。\$xx, \@xx, \%xx はそれぞれの変数へのリファレンスを示します。

    $aa = 123;
    $xx = \$aa;
    print "$$xx\n";

    @bb = ( "AAA", "BBB" );
    $xx = \@bb;
    print "$xx->[0]\n";
    print "$xx->[1]\n";

    $cc{'name'} = "tanaka";
    $cc{'age'} = 24;
    $xx = \%cc;
    print "$xx->{'name'}\n";
    print "$xx->{'age'}\n";

連想配列とリファレンスを組み合わせることにより、C言語の構造体の配列のようなものを表現することができます。

    push @persons, { "name" => "tanaka", "age" => 24 };
    push @persons, { "name" => "suzuki", "age" => 28 };
    foreach $person (@persons) {
        print "$person->{'name'} $person->{'age'}\n";
    }

上記では名前無しの連想配列という機能を用いています。次のようにしてしまうと、$persons[0] と $persons[1] に同じリファレンス値が代入されてしまうので、期待通りの動作をしません。

    %person = ( "name", "tanaka", "age", 24 );
    push(@persons, \%person);
    %person = ( "name", "suzuki", "age", 28 );
    push(@persons, \%person);
    foreach $person (@persons) {
        print "$person->{'name'} $person->{'age'}\n";
    }

◆ 日本語の取り扱い

日本語には、シフトJIS、EUC、JISなどいくつかの漢字コードがあります。（→ 漢字コードについて）。Perlで漢字コードの変換を行うには、jcode.pl などのライブラリを使用します。

       ftp://ftp.iij.ad.jp/pub/IIJ/dist/utashiro/perl/

jcode.plは、以下のように使用します。

    require "jcode.pl";
    $xx = "日本語";
    &jcode'convert(*xx, "euc");  # EUCに変換
    &jcode'convert(*xx, "jis");  # JISに変換
    &jcode'convert(*xx, "sjis"); # シフトJISに変換
    &jcode'h2z_sjis(*xx);        # 半角カナを全角カナに変換

スクリプトをシフトJISで記述する場合、"表" や "予" などにように、シフトJIS漢字コードの2バイト目が 0x5c（バックスラッシュ（\））となる文字を使用すると、文字化けすることがあります。この場合、下記のような方法で対処します。

- スクリプトをEUCで記述する。
- "表示" ではなく、'表示' のようにシングルクォートを用いる。
- "表\示" や "予\約" のように、バックスラッシュを補完してやる。

Copyright (C) 1997-2002 杜甫々
初版：1997年2月3日、最終更新：2002年2月24日
http://www.tohoho-web.com/wwwperl1.htm