C を知っている人のための perl の覚え書き

最終更新  2016.11.02


◆◆ 役に立つサイト ◆◆

Perl表技集
Perl基礎入門

◆◆ 変数の宣言の強制 ◆◆

ファイルの先頭に

use strict;

と書くと、そのファイル中のメイン関数(関数宣言なしに
書き始める部分)、サブルーチン全てに変数宣言が必要となる。

変数宣言は

my $a;
my ($a,$b,$c,@array);

のように行う。配列変数は @ をつける。


◆◆ 変数のスコープ ◆◆

メイン関数の中で宣言した変数はグローバル変数となる。

◆◆ ループからの脱出 ◆◆

break     : last
continue  : next

◆◆ ファイル入出力 ◆◆

open(FP,"> file");
print  FP  "$moji\n";
printf FP ("%4d  \n",$i);
close(FP);

open(FP,"<&STDIN");

open(FP,"< file");
$moji = <FP>;
close(FP);

open(FP,">&STDERR");

$iret = open(FP,"< file");
if ( $iret == FALSE ){
    print STDERR "cannot open file\n";
    exit 1;
}

成功したとき : 1
失敗したとき : 空白

open(FP,"< $fname") || die "cannot open file $fname\n";

ファイルのロック

open(FP,"> filename");
flock(FP,2);       # ロック
print FP,"....";
flock(FP,8);       # 解除
close(FP);

読み書きモードの使い方

open(FP,"+< filename");
$dat = <FP>;  読み込み

truncate(FP,0);   # ファイルサイズを 0 にする
                  # これを怠ると seek の後に書き込んだサイズが元のファイルより
                  # 小さいとき、元のファイルの後半が残る
seek(FP,0,0);     # 読み取りポインタをファイル先頭に
print FP "data\n";
close(FP);


◆◆ ファイルの削除 ◆◆

unlink ("fname","fname2","fname3");


◆◆ print 文関係 ◆◆

print "i = ",$i,"\n";                 basic と同じ構文
print "i = $i \n";                    $i は "  " の内側にも書ける
print "\"i\" = 1\n";                  エスケープシーケンス
printf("a = %10.5lf \n",$a);          C と同じように書いても良い
$moji = sprintf("a = %10.5lf ",$a);   sprintf は引数の書き方が少し違う
$moji = sprintf("%20s  ",$str);       20 文字右つめ
$moji = sprintf("%-20s  ",$str);      20 文字左つめ

print  FP "i = $i \n";                ファイルへの入出力
printf FP ("a = %10.5lf  ",$a);

print @list;         隙間なく並べる
print "@list";       要素ごとの間に空白を 1 つ入れる


◆◆ 引数の取得 ◆◆

$argc = @ARGV;  引数の個数を取得
$ARGV[0]        第一引数


◆◆ 標準入出力 ◆◆

<STDIN>   <STDOUT>  <STDERR>


◆◆ 環境変数の取得 ◆◆

$host = $ENV{'HOST'};    環境変数はハッシュ変数 %ENV に入っている


◆◆ サブルーチンとの引数のやりとり ◆◆

値渡し

&sub1($a,$b);

sub sub1{
    my($a2,$b2) = @_;  <---- 引数は配列 @_ に格納される
    my($c);            <---- $c はローカル変数
    $b2 = 10;
}

ポインタ渡し ( ver.5.8 ではエラーになる。現在は使えないようだ)

&sub2(*a,*b);

sub sub2{
    my(*c,*d) = @_;
    $c = 10;
    $d[0] = 1;
    $d[1] = 2;
}

ファイルハンドルの引数渡し

&sub3(FP);

sub sub3{
    my($FP) = @_;
    $_ = <FP>;
    print "file : $_\n";
}

サブルーチンからの戻り値

return 式;

複数の値を返すとき

($a,$b) = &sub4($a,$b)

sub sub4{
    my($c,$d) = @_;
    $c++;
    $d++;
    return ($c,$d);
}


◆◆ foreach 文 ◆◆

普通の配列

$array[0] = 'abc';
$array[1] = 'def';
foreach $item ( @array ){

}

連想配列

$a{'aa'} = 'xx';
$a{'ab'} = 'yy';
forcach $item ( keys %a ){

}

forcach $item ( sort keys %a ){

}


◆◆ 文字列の連結 ◆◆

$a = $b . $c;

あるいは

$a = "$b$c";


◆◆ 文字列のマッチング、比較 ◆◆

if ( $str =~ m/pattern/ )     m は省略可
if ( $str !~ m/pattern/ )     マッチングしない
if ( $str =~ m/pattern/i )    i : 大文字小文字を無視

if ( $str eq $str2 )

(注)if ( 'aaa' == 'bbb' ) は真となるので注意。文字列の比較は eq を使う

$str = "abc-def";
$str =~ m/(.*)-(.*)/;   . は任意の1文字  * は 0 回以上の繰り返し
                        () はグループを表す  $1 $2 $3 ... の順番に入る
print "$1  $2 \n";      $1 には abc   $2 には def が入る


$_ = "abc-def";         上の例の省略形
m/(.*)-(.*)/;
print "$1  $2 \n";

if ( $str =~ m/^\s*$/ )  空行とのマッチング

$str の最後が "\n" があっても上の式はマッチングする。
文字列の最後に "\n" があることが確実な場合は、

if ( $str =~ m/^\s*\n/ )

の方が分かりやすいかもしれない。



◆◆ 文字列の置換 ◆◆

$str =~ s/previous/after/;    文字変数 $str を置換した結果が $str に入る
s#previous#after#;            / でなくてもよい。
s/previous/after/g;           グローバルの略  マッチングするもの全て
                              g を付けない時、最初にマッチしたものだけ置き換える
s/previous/after/i;           大文字小文字区別せず


◆◆ 文字列の分解 ◆◆

@list = split( ' ' , $str );
print "$list[0]\n";


◆◆ 文字列の結合 ◆◆

$out = join('  ',@list);
print "out\n";


◆◆ 文字列の処理 ◆◆

行末に改行記号がある場合、切り落とす

if ( $moji =~ m/\n$/ ){ chop($moji); }

長さの取得

$leng = length($a);

Basic の MID$

$in = '123456789';
$a = substr($in,0,4);   最初から 4 文字取る
$b = substr($in,4,5);   5 文字目から 5 文字取る
$c = substr($in,3);     4 文字目から最後まで

結果 :     $a = "1234"    $b = "56789"    $c = "456789"


◆◆ 配列 ◆◆

$list[0] = "yabu";     普通の配列
@list = ( 1 , 2 , 3 );
@list                  配列全体
$size = @list          配列のサイズを得る
$#list                 配列の最後の要素番号 ( 要素は 0 から始まるので
                       サイズ-1 が得られる )
push(@list,element)    配列の最後に要素を一つ加える
$a = pop(@list)        配列の最後の要素を取り除き、その要素を返す
unshift(@list,element) 配列の最初に要素を一つ加える
$a = shift(@list)      配列の最初の要素を取り除き、その要素を返す。一つ前に詰められる

$list{yabu} = "tetsuro";   連想配列
%list                      配列全体

引数処理の例

while($arg = shift(@ARGV)){
    if ( $arg =~ m/^-/  ){
        push(@options,$arg);
    } else {
    $fname = $arg;
    }
}


◆◆ 2 次元配列 ◆◆

perl には 2 次元配列はないが、$a[$i] までを変数名と
見なすことにより、以下のように C と全く同じ構文で、
2 次元配列を実現することが出来る。

for($i = 0; $i < 3; $i++){
    for($j = 0; $j < 4; $j++){
        $a[$i][$j] = $i * 10 + $j;
    }
}

for($i = 0; $i < 3; $i++){
    for($j = 0; $j < 4; $j++){
        printf("a[%d][%d] = %3d\n",$i,$j,$a[$i][$j]);
    }
}


◆◆ 正規表現 ◆◆

.   任意の1文字
x?  0 個あるいは 1 個の x
x*  0 個以上の x
x+  1 個以上の x
^   行の先頭
$   行末
\s  空白文字 ( スペース、タブ、改行 )
\S  空白以外の文字


[0-9\-]   0 〜 9 の文字あるいは '-' 記号の固まり
[^\"]+\"  " 以外の文字の 1 つ以上の連続の後、" 文字



◆◆ if 構文 ◆◆

if ( 条件 || 条件 ) {

} elsif ( 条件 && 条件 ) {

} else {

}

$a == $b    スカラ変数
$a != $b    スカラ変数
$a eq $b    文字列
$a ne $b    文字列


◆◆ 連想配列 (ハッシュ変数) ◆◆

$name{'yabu'} = "tetsuro";
$name{'sawa'} = "shinnosuke";

@list  = sort keys   %name;
@list2 = sort values %name;


◆◆ rewind ◆◆

seek(FP,0,0);   第 2 引数の 0 バイト目、第 3 引数の 0 はファイル先頭から


◆◆ ファイルポインタ操作関数 ◆◆

$loc = tell FP;     ファイルポインタの位置を得る
$line = <FP>;       読み込む
seek(FP,$loc,0);    先程の位置にもどす


◆◆ eof ◆◆

eof(FP) が 1 のときファイルエンドを表す

例  下の二つの例は同じ

while(1){
    if ( eof(FP) == 1 ){
        last;
    }
    $_ = <FP>;
    print;
}

while(<FP>){
    print;
}

( 注意 )

$_ = <FP> として最後の行を読み込んだ直後に
eof(FP) は 1 になるので、

    $_ = <FP>;
    if ( eof(FP) == 1 ){
        last;
    }
    print;

ではダメである。ここは C の fgets とは感覚が
異なるところであるから注意すること。



◆◆ find_header ◆◆

sub find_header
{
    my(*FP,$header) = @_;
    seek(FP,0,0);

    $flag = 0;
    while(<FP>){
        if ( m/^\s*$header/ ){
            $flag = 1;
            last;
        }
    }

    if ( $flag == 0 ){
        print "Cannot find header\n";
        print "Exit.\n";
        exit 1;
    }
}



◆◆ int と剰余 ◆◆

$hour = int( $min / 60 );
$min  = $min % 60;

また printf 文で実数型変数に対して %d を指定すると
小数部を切捨てて表示してくれる。



◆◆ goto 文 ◆◆

C 言語と同じ

goto LABEL;

LABEL:


◆◆ ディレクトリの操作 ◆◆

opendir(DIR);
@flist = readdir(DIR);
closedir(DIR);

chdir "/usr/local/bin";

◆◆ ファイルテスト演算子 ◆◆

if ( -d $fname )   $fname がディレクトリか否か
if ( -f $fname )   $fname がファイルか否か
if ( -l $fname )   $fname がシンボリックリンクか否か
if ( -e $fname )   $fname が存在するか否か


◆◆ 特殊変数 ◆◆

$$   プロセス番号
$0   スクリプトファイルの名前(コマンドラインから打った値)


◆◆ シグナル処理 ◆◆

ctrl-C は 'INT' で表される。
SIGTERM でないのに注意!

( 例 )

$SIG{'INT'} = 'interrupt';

while(1){}

sub interrupt {
    print "terminate\n";
    exit 1;
}


◆◆ ヒアドキュメント ◆◆

2種類の書き方がある。

---------------------
print <<END_MARK     << と END_MARK の間に空白を入れてはならない
.....
$a                   変数も使える
.....
END_MARK
;                    このセミコロンを忘れないように
---------------------

---------------------
print << "END_MARK";
.....
$a
.....
END_MARK
---------------------


◆◆ 配列全体に作用するコマンド ◆◆

@result = grep /文字/, @list
@result = sort @list


◆◆ ソート ◆◆

@result = sort @list                 普通のソート
@result = sort {$a cmp $b} @list     上の内容を省略せずに書いたもの
                                     sort コマンドは $a, $b という一時変数
                                     を使う。{  } の中が -1 なら $a を前に
                                     並べ、1 なら $b を前に並べる。
                                     $a cmp $b は文字変数 $a と $b の大小関係
                                     を調べ、$a < $b なら -1 、$a == $b なら 0
                                     $a > $b なら 1 を返す

                                     ただし、ここでの $a, $b はユーザーが
                                     定義して使う変数 $a, $b とは別物である。

@result = sort {$b cmp $a} @list     逆順に並べる

@result = sort {$a <=> $b} @list     数値を小さい順から並べる
                                     <=> は数値を比べる演算子

@result = sort {length($a) <=> length($b)}  文字長が短い順に並べる
@result = sort {length($b) <=> length($a)}  文字長が長い順に並べる


◆◆ 省略形 ◆◆

@all = <>;   入力を行で区切って配列 @all に入れる


◆◆ 入出力のバッファリングをしない ◆◆

$| = 1;


◆◆ 配列の要素のシフト ◆◆

shift @list;

$list[0] <-- $list[1]
$list[1] <-- $list[2]
$list[2] <-- $list[3]

unshift(@list,"top_item");

$list[0] <-- "top_item"
$list[1] <-- $list[0]
$list[2] <-- $list[1]


◆◆ 処理内容をファイルに落としてメールする ◆◆

$msgfile = "/tmp/$$.tmp";
open(FP,"> $msgfile");

print FP "xxx\n";
print FP "$0 is executed normally.\n";

close(FP);
system("/usr/bin/mail -s 'Report from $0' $ENV{USER} < $msgfile");


◆◆ system 文 ◆◆

C と全く同じ

正常終了のとき返り値は 0
Command not found などで異常終了のとき返り値は -1
C-C で Abort したときの返り値は 2 65280 など。よくわからない。0 以外であることは間違いない。

エラー処理を付加した system 文の例

sub ysystem
{
    my($command) = @_;
    if ( system($command) != 0 ){
        print STDERR "error occurs in executing : $command\n";
        exit 1;
    }
}


◆◆ ディレイ ◆◆

sleep 1      1 秒待つ

select(undef, undef, undef, 0.1)     0.1 秒待つ


◆◆ コメント ◆◆

# 以降はコメント

__END__ 以降は無視される



◆◆ host <----> ip address ◆◆

$host = "denki.nara-edu.ac.jp";
($hname,$aliases,$addrtyp,$length,@addrs) = gethostbyname($host);

$addr = "192.168.107.50";
$host = gethostbyaddr(pack("C4", split(/\./, $addr)), 2);


◆◆ 日付が新しいファイルのみコピー ◆◆

ディレクトリの新規作成の階層は 1 階層しかサポートしていない

sub command{
    my($command) = @_;
    print "$command\n";
    system($command);
}

sub copy{
    my($source_fname,$dist_fname) = @_;

    if ( ! -e $source_fname ){
        print "file $source_fname does not exist.\n";
        return;
    }

    @stat1 = stat($source_fname);   # $stat1[9] に更新時刻が入る
    @stat2 = stat($dist_fname);     # $stat2[9] に更新時刻が入る

    if ( $stat1[9] > $stat2[9] ){

        $dist_fname =~  m/(^\S+)\/(\S+)$/;  # ファイルが格納されるディレクトリ
        $dist_dir = $1;
        $dist_dir =~  m/(^\S+)\/(\S+)$/;
        $dist_dir1 = $1;                    # その親ディレクトリ
        $dist_dir2 = $2;                    # ディレクトリ名のみ

        if ( -e $dist_dir ){
        } else {
            command("cd $dist_dir1; mkdir $dist_dir2");
        }
        command("cp $source_fname $dist_fname");
    }
}

◆◆ Windows 版 perl の使い方 ◆◆

active perl 64bit 版 5.14 をインストールすると

  C:\Perl64

以下にインストールされる。path 変数の先頭に

  C:\perl64\site\bin; C:\perl64\bin

が加えられ、拡張子 pl に

  C:\perl64\bin\perl.exe

が結びつけられる。

pl ファイルをダブルクリックするとエラーが発生しても
エラーメッセージを見ることが出来ない
(DOS 窓らしきものが一瞬表示されるが視認できない)。

DOS 窓から

  ...>perl script-name.pl

として実行するのが良さそうだ。


◆◆ 日付と時刻の読み取り ◆◆

引用元

# ファイルの場合

@stat = stat($fname);
@time = localtime($stat[9]);

# 現在時刻

@time = localtime;

# @time 配列の処理

@youbi = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = @time;
$year += 1900;
$mon += 1;

$line = "$year/$mon/$mday $youbi[$wday] $hour:$min:$sec";
print "$line\n";