#author("2023-05-20T14:34:29+09:00","","")
#topicpath

#contents();
#ls2(Lang/Perl/);

////////////////////////////////////////////////////////////////////////////////
* perldoc [#cefb1795]
- [[perldoc.jp>https://perldoc.jp/]]

////////////////////////////////////////////////////////////////////////////////
* システムにおける各データ長を調べる [#e6f41a10]
- 以下のコマンドを実行することで、各データ型のデータ長を取得出来る。
 $ perl -V:{short,int,long{,long}}size
- 実行結果例
 shortsize='2';
 intsize='4';
 longsize='8';
 longlongsize='8';


////////////////////////////////////////////////////////////////////////////////
* 1sec 未満の sleep [#s1414a2e]
- 1sec未満の sleep をするには、sleep ではなく、select を使用する。
 select undef, undef, undef, 0.5;   # 0.5sec の sleep

////////////////////////////////////////////////////////////////////////////////
* Perlでハッシュ [#b6565f40]
//==============================================================================
** hashを宣言(?)する [#o111d4f1]
 my %aaa = ( 'ip' => '192.168.0.1',
             'host' => 'foo' );
//==============================================================================
** hashの中身を参照する [#r9e8457e]
 $aaa{'ip'}

//==============================================================================
** hashを配列にする [#k47bc7e1]
- hashを配列にするには、配列の要素となるhashを1個、ループ内において my で宣言しておき、これに値を入れ、配列に入れる。
 for ($i = 0; $i <= $#A_ARRAY; $i++) {
     my %aaa = ( 'ip' => "192.168.0.$i", 'host' => "host_$i" );    # <--- ループ内で my で宣言するのがミソ
     $aaa{'ip'} = $new_ip;
     # $HOSTS[$#HOSTS+1] = \%aaa;
     push (@HOSTS, \%aaa);
 }

//==============================================================================
** hash の配列を宣言時に初期化 [#y4b37e34]
- hash の配列を宣言する。
- 例:
 my (@HOSTS) = (
     { ip => '192.168.1.34' , host => 'ikura.example.com' },
     { ip => '192.168.1.35' , host => 'tai.example.com'   },
     { ip => '192.168.1.36' , host => 'shake.example.com' },
     );


//==============================================================================
** 配列の中身を見る [#xc01f65a]
- 上で作ったhashの配列の中身を見るには、以下のようにすればOK。
 foreach $h (@HOSTS) {
     print "ip: $h->{'ip'}\n";
 }

//==============================================================================
** 配列に入れたhashの中身を更新する [#daf96c11]
- たとえば、
 foreach $x (@XXX) {
     my (%host) = ('IP' => "0.0.0.0", 'host' => "host_$x" );
     # $HOSTS[$#HOSTS+1] = \%host;
     push (@HOSTS, \%host);
 }
のようにして作ったhash配列 @HOSTS に対して、その$i番目の中身を更新するには
 (1) :
   $HOSTS[$i]->{'IP'} = "192.168.0.1"   # これでは更新出来ない。
 
 (2) :
   my (%h) = $HOSTS[$i];
   $h{'IP'} = "192.168.0.1";
   $HOSTS[$i] = \%h   # これでなら更新出来る。
   my (%tmp) = $HOSTS[$i];
   $tmp{'IP'} = "192.168.0.1";
   $HOSTS[$i] = \%tmp   # これでなら更新出来る。


//==============================================================================
** hashを別のhashにコピー [#t1bc7fcc]
- 丸ごとコピー出来る。
 %aaa = ( 'val1' => 10, 'val2' => 100 );
 %bbb = %aaa;


//==============================================================================
** hash の中身を全て取り出す [#z11a7aa3]
- key() 関数を使うと、引数に渡した hash の中身の key を取り出すことが出来る。これを使って hash に格納されている値を全て取り出すことが出来る。
 my (@key) =keys (%ENV);
 foreach $k (@key) {
     print "$key=[$ENV{$k}]\n";
 }


//==============================================================================
** 参考URI [#c21d8467]
- [[Perlについて構造体配列を宣言&使いたい。>http://www.rescue.ne.jp/CGI-BBS/cgi/perl/20030507180401.shtml]]
- [[cの構造体vsPerlのハッシュ>http://www.geocities.co.jp/SiliconValley-Sunnyvale/6128/perl/structhash.html]]
- [[Class::Struct>http://www2u.biglobe.ne.jp/~MAS/perl/waza/struct.html]]

////////////////////////////////////////////////////////////////////////////////
* 配列要素の操作 [#v7d5d906]
* 配列要素 [#array]

////////////////////////////////////////////////////////////////////////////////
//==============================================================================
** 基本事項 [#h1e888f5]
- 配列の要素数
-- 配列をスカラに代入すると得られる。
 $num = @array;    # 配列 @array の要素数が $num に入る
-- &color(#ff0000){ ローカル変数へ格納する際の注意:};
--- NG例
 my ($num) = @array;  # 配列 @array の要素数ではなく先頭の要素が入ってしまう。
--- 予め my ($変数) とし、文を改めてから代入すれば正しく要素数が入る
 my ($num);
 $num = @array;       # 配列 @array の要素数が $num に入る

- 配列の最後のindex番号
-- &color(#ff0000){$#};配列名とすると、その配列の最後のindex番号が得られる。
 $#array   # 配列 @array の最後のindex番号を表す。

////////////////////////////////////////////////////////////////////////////////
//==============================================================================
** sort [#gcd11b25]
- 辞書順
 @dest = sort { $a cmp $b } @src;    # 順方向
 @dest = sort { $b cmp $a } @src;    # 逆方向
- 値順
 @dest = sort { $a <=> $b } @src;    # 順方向
 @dest = sort { $b <=> $a } @src;    # 逆方向

- hash配列も特定の値でsort出来る
 @dest = sort { $a->{'VarName'} <=> $b->{'VarName'} } @src;    # 順方向

////////////////////////////////////////////////////////////////////////////////
//==============================================================================
** 挿入・削除 [#u287eeb9]
- 配列の最後に要素を挿入
 push (@dst, $add)
  @dst: 挿入先配列, $add: 挿入する値

- 配列の後ろに別の配列を繋げる --- これも push() でOK。
 push (@dst, @add);
 @dst: 挿入先配列, @add: 挿入する配列

- 配列の最後から要素を削除
 $deleted = pop (@src)
  @src: 取り出し元配列。実行後は $deletedが取り除かれた配列になっている
  $deleted: @srcの最後から取り出した要素

- 配列の先頭に要素を挿入
 unshift (@src, $add)
  @dst: 挿入先配列, $add: 挿入する値

- 配列の先頭から要素を削除
 $deleted = shift (@src)
  @src: 取り出し元配列。実行後は $deletedが取り除かれた配列になっている
  $deleted: @srcの先頭から取り出した要素

※arrayの要素がhashであっても大丈夫。


//==============================================================================
** 配列の初期化 [#m8f2642e]
- 配列から要素を全て削除する
 @array = ();


//==============================================================================
** 多次元配列 [#s4495e15]
- 多次元配列としての初期化
 @ar = (
     [ "a", "b", "c", ],
     [ 1,   2,   3,   ],
     ["Q", "W", "E",  ],
     );




////////////////////////////////////////////////////////////////////////////////
* 時間関数 [#ef88b2b5]
//==============================================================================
** 経過日数の計算 [#jeaffa25]
- Time::Local を利用する。
- 例:現在から丁度10日と2時間後の日時を得る
 use Time::Local;	# timelocal()
 
 my($sec,$min,$hour,$mday,$mon,$year);
 # 普通は localtime(time) の返り値を代入するが、ここは例示なので具体的な日付けで
 ($year,$mon,$mday,$hour,$min,$sec) = (2009,01,01,12,00,23);  #2009年1月1日12時0分23秒
 
 # perlの常識ながら、$monは0オリジン、$yearは1900オリジンなので、人間が使う値が入っている場合には、当然引き算が必要
 my ($time_local) = timelocal ($sec,$min,$hour,$day,$mon-1,$year-1900);
 
 # 上で得られた $time_local は、基準日時(1970年01月01日で0時0分0秒)からの秒数になっている。
 # であるから、ここから更に「10日と2時間」を秒数に換算したものを加えると、
 $time_local += (10 * 24 * 60 * 60) + (2 * 60 * 60);
 
 # 以下で得られる $res_* に入る値が、求める日時である。 
 my ($res_sec,$res_min,$res_hour,$res_mday,$res_mon,$res_year,$res_wday,$res_yday,$res_isdst) = localtime($time_local);


//==============================================================================
** 秒未満の時間を取得する [#xd20294a]
- Time::HiRes の gettimeofday を使用する
 use strict;
 use warnings;
 use Time::HiRes "gettimeofday";
 
 my ($current_time) = &get_time_stamp ();
 
 sub get_time_stamp {
      my ($epoc_sec, $micro_sec) = gettimeofday();
      my ($sec, $min, $hour, $day, $mon, $year) = localtime($epoc_sec);
      $year += 1900;
      $mon++;
      my ($time_stamp) = sprintf("%04d-%02d-%02d %2d:%2d:%2d.%d", $year, $mon, $day, $hour, $min, $sec, $micro_sec);
      return $time_stamp;
 }


////////////////////////////////////////////////////////////////////////////////
* 外部プログラム呼出 [#s0f44447]
//==============================================================================
** 返り値を得たい場合 [#cef38dbb]
 $ret = system ("呼び出すコマンド");
- この $ret のように、&color(red){サブプロセスの返り値の場合、''"<< 8"'' された値になっている};ので、正しい値を得るには、以下のようにしてやる必要がある:
 $ret = $ret >> 8;;

//==============================================================================
** 呼び出したコマンドのプロセスに入力値を与えたい場合 [#j48e4f5c]
 if (!open (PROG, "| 呼び出すコマンド")) {
     print "open error\n";
     return;
 }
 print PROG "送りたいデータ";
 ・・・・
 close (PROG);    # 最後の後始末


//==============================================================================
** 呼び出したプログラムの出力を得たい場合 [#m821c904]
 if (!open (PROG, "呼び出すコマンド |")) {
     print "open error\n";
     return;
 }
 my (@out) = <PROG>;     # 複数行の出力も全て、配列 @out に格納する。
 close (PROG);



////////////////////////////////////////////////////////////////////////////////
* File I/O [#l78587ba]

//------------------------------------------------------------------------------
//==============================================================================
** ファイルの copy, rename [#x409af09]
- ファイルの copy rename には、 File::Copy を使う。
 use File::Copy;
 copy("コピー元のファイル名", "コピー先のファイル名");  # ファイルのcopy
 move("移動元", "移動先");   # ファイルのrename


//------------------------------------------------------------------------------
** ディレクトリの読み込み [#nc7671b7]
- opendir, readdir, closedir を使う。割と普通なやり方。
 my ($dir) = './data';
 if (!opendir (DIR, "$dir")) {
     die "Could not open dir: $dir\n";
 }
 
 my (@read_tmp);
 while (defined($ent = readdir(DIR))) {
     if (($ent =~ /^\.$/) || ($ent =~ /^\.\.$/)) { next; }
         # $read_tmp[$#read_tmp+1] = $ent;
         # $read_tmp[$#read_tmp+1] = "$dir/$ent";
         push (@read_tmp, "$dir/$ent");
 }
 closedir(DIR);
 # 思った通りの並びで読み取ってくるわけではないので、sort しておいた方がいい場合もある。
 my (file_entries) = sort { $a cmp $b } @read_tmp;


//------------------------------------------------------------------------------
//==============================================================================
** ファイルテスト [#c2a19583]
|~演算子  |~説明|
|-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 |	ファイルはキャラクタ型の特殊ファイル|
|-t |	ファイルハンドルが tty としてオープンされている|
|-u |	ファイルの setuid ビットがセットされている|
|-g |	ファイルの setgif ビットがセットされている|
|-k |	ファイルの sticky ビットがセットされている|
|-T |	ファイルがテキストファイル|
|-B |	ファイルがバイナリファイル|
|-M |	perl起動時における、ファイルの更新時刻からの日数|
|-A |	perl起動時における、ファイルの参照時刻からの日数|
|-C |	perl起動時における、ファイルの作成時刻からの日数|

- 使用例
 if (-e $file_name) {
     # ファイルが存在した場合の処理を記述
 }

//------------------------------------------------------------------------------
//==============================================================================
** カレントディレクトリの取得 [#r9809812]
- getcwdを使う。
 #!/usr/bin/perl
 use Cwd 'getcwd';              # for getcwd
 my ($cur_dir_name) = getcwd;   # current dirが Full Path で代入される。
 
 # もし、Full Pathではなく現在のディレクトリ名だけが欲しければ、さらに次のようにする。
 $cur_dir_name =~ s/.*\///g;


//==============================================================================
** カレントディレクトリの変更 [#q5724e3a]
- chdir を使う
 my ($ret) = chdir ($path);
-- return
 1: 成功
 0: 失敗 → $! に失敗原因のメッセージ文字列が格納される

- File::chdir を使う
 use File::chdir;
 
 $CWD = ~/work;
-- File::chdir を使う場合、環境によっては追加パッケージの導入が必要となる。
--- debian の場合は [[libfile-chdir-perl>https://packages.debian.org/search?lang=ja&searchon=names&keywords=libfile-chdir-perl]] をインストールする。

//------------------------------------------------------------------------------
//==============================================================================
** ファイル属性の取得 [#a1fcdaee]
- stat 関数を使う。
 @INFO = stat( $file );
|ファイルシステムのデバイス情報   |$INFO[0] |
|Iノード番号                 |$INFO[1] |
|ファイルモード(パーミッション等)   |$INFO[2] |
|対象ファイルのリンク数         |$INFO[3] |
|対象ファイルの所有書のユーザID |$INFO[4] |
|対象ファイルのグループのグループID |$INFO[5] |
|デバイス識別子              |$INFO[6] |
|ファイルサイズ(バイト数)       |$INFO[7] |
|最終アクセス時間            |$INFO[8] |
|最終変更時間              |$INFO[9] |
|最終 I ノード変更時間       |$INFO[10] |
|最適ブロックサイズ          |$INFO[11] |
|割当てられているブロック数    |$INFO[12] |


//==============================================================================
** Read/Write [#t087d466]
//------------------------------------------------------------------------------
** Read/Write [#t087d466]
*** 簡単なread [#u01bca45]
 if (!open (FILE_HANDLE, $file_name)) {
     die "file open error: $file_name\n";
 }
 
 my (@lines) = <FILE_HANDLE>;  # ファイルの全ての行を一気に @lines に読み出し
 close (FILE_HANDLE);          # 読みだしたので、close
 foreach $line (@lines) {
     # 処理を記述
 }
- 長所
-- 全ての行が一つの配列に格納されるため、読み出し後、行単位でランダムアクセスが出来る
-- すぐにclose出来る
- 短所
-- 読みだすファイルが巨大な場合、メモリ不足に陥り正常に処理終了出来ない場合がある。

//------------------------------------------------------------------------------
*** 大容量ファイルも読み込めるread [#dbb9cd54]
 if (!open (FILE_HANDLE, $file_name)) {
     die "file open error: $file_name\n";
 }
 
 while ($line = <FILE_HANDLE>) { # 1行ずつ読み出して逐次処理する。
     # 処理を記述
 }
 close (FILE_HANDLE);            # 読みだしたので、close
- 長所
-- 巨大なファイルも読み出せる
- 短所
-- 先頭からの逐次処理しか出来ない




////////////////////////////////////////////////////////////////////////////////
* Unicodeの使用 [#bb6eec79]
** 参考サイト [#b17497bd]
- [[PelleasのBlog>http://pelleas.cocolog-nifty.com/blog/cat7921558/index.html]]
- [[Perl 5.8.x Unicode関連>http://www.rwds.net/kuroita/program/Perl_unicode.html]]
- [[Perlによる日本語コード変換のメモ(第二版)>http://www.hikoboshi.org/perl/utf8.html]]
- [[perl5.8のUnicodeサポート>http://www.lr.pi.titech.ac.jp/~abekawa/perl/perl_unicode.html]]

//------------------------------------------------------------------------------
** ひとまず [#m4b559b3]
- スクリプトファイル自体の文字コードを utf-8 にしておく。
- Unicodeを使えるように宣言しておく。
 use utf8;
 use Encode;

- 入力/出力をutf-8にするには、この様に宣言しておく。
 binmode(STDIN,":utf8");
 binmode(STDOUT,":utf8");


//------------------------------------------------------------------------------
* 文字列処理 [#pf775480]
//==============================================================================
** split() : 区切り文字によるフィールド分割 [#k3a2b186]
- 書式:
 @dst = split (/$splitter/, $src);
 
 @dst : 区切り文字で分割された各フィールドが配列として格納される先。区切り文字は除去して格納される。
 $splitter : Tab区切りの場合はTab(\t)
             SPACE区切りの場合は SPACE( )
             コンマ区切りの場合はコンマ(,)
             ...といった具合に指定する
 $src : 区切り文字でフィールドが区切られた文字列。


//==============================================================================
** 文字列を1文字づつ処理する [#ke53ce9f]
- split() で分割する
 $string = "hogefuga";
 @chars = split (//, $string);
 foraech $ch (@chars) {
     // 1文字づつの処理
 }
あるいは
 $string = "hogefuga";
 foraech $ch (split (//, $string)) {
     // 1文字づつの処理
 }


//==============================================================================
** シングルクォートを含む文字列の処理 [#jcf8f9fc]
- シングルクォートは、単にエスケープしただけでは正しく解釈されない為、''&apos;\&apos;'' ではなく、それ自体を更にシングルクォートで括って ''&apos;\&apos;&apos;'' とする必要がある。
-- 例:シングルクォートを削除する
 $ perl -pe 's/\'//'   # 単にシングルクォートをエスケープ              :正しく動作しない
 $ perl -pe 's/'\''//' # エスケープしたシングルクォートをさらにシングルクォートで括る:正しく動作する

- 例:シングルクォートを含む文字列を処理する例として、lisp の require 文から、require されるモジュール名の抽出をしてみる。
-- lisp の require 文は以下の書式になっている:
 (require '<モジュール名>)
-- シングルクォート以前と、最後の小括弧閉じを削除する
 $ echo "(require 'magit)" | perl -pe 's/.*'\''//' | perl -pe 's/\)//'
 magit





 
////////////////////////////////////////////////////////////////////////////////
* 呼び出し元関数名を取得する [#bc8c6672]
- C/C++ の __func__ マクロの代用機能(perlには同様のマクロはない)
- その代わり、「呼び出し元関数の名前」を取得する機能があって、これを関数化して呼び出すと、 __func__ マクロ同様の使い方が出来る。
 sub get_func_name{
   # 呼び出し元の関数名を取得
   my ($parent_func_name) = (caller 1)[3];
   $parent_func_name =~ s/.*:://;
   return $parent_func_name; 
 }
-- (caller 2)[3] とすると更にもう1階層上の呼び出し元を表示する。これは例えば log 関数内から呼び出す場合に使える。


////////////////////////////////////////////////////////////////////////////////
* コメント [#dcfabb6f]
- 行単位のコメントは、半角 "#"
 print "Hello World.\n";    # これより右側はコメント

- 複数行をコメントアウトする
-- 開始記号: =pod
-- 終了記号: =cut
 =pod
 ここは、コメントになる
 =cut
 print "Hello World.\n";
-- &color(red){このコメントアウト記号は、行頭に記述しないと正しくコメント開始・終了記号として認識されない場合があるので注意!};

- 複数行をコメントアウトする(Acme::Comment)
-- Acme::Comment を使用すれば、 C/C++/Java 風のコメントアウトが使用出来る。
 Acme::Comment;
 /*
 ここは、コメントになる。
 */
-- CGI で使用する場合には注意が必要(サーバによっては使えない場合がある)。


////////////////////////////////////////////////////////////////////////////////
* pack / unpack [#w075669c]
** pack [#qee4c16f]
- [[pack>https://perldoc.jp/func/pack]]


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS