Smart -Web Magazine

WEB開発者に嬉しいステキ講座

MENU

ファイルのコピー

2009年8月21日 コメントの追加

Perlにはmkdir()やrmdir()、unlink()といったディレクトリの作成や削除関連の関数は用意されているのですが、ファイルコピーに関する関数は用意されていません。そのため、system()を使ってシステムのコマンドを呼び出すか、モジュールを使う必要があります。

system()からシステムのcpコマンドを呼び出した場合
system("cp ./a.csv ./b.csv");

モジュールを使う場合は、デフォルトでインストールされているFile::Copyが便利です。File::Copyにはcopy()、move()、syscopy()、rmscopy()が用意されています。

File::Copyモジュールを使った場合
use File::Copy;
copy("./a.csv","./b.csv") or die "error: $!";

copy()の実行でエラーが発生すると、$! にエラー内容が格納されます。

任意のディレクトリにコピーしたい際は、第2引数にディレクトリを指定します。

use File::Copy;
copy("./a.csv","/var/www/html/bk");
参考
このエントリーをはてなブックマークに追加

PerlでCSVを扱う - Text::CSV_XS

2009年8月19日 コメントの追加

PerlでCSVを扱う場合、Text::CSV_XSモジュールが便利です。
インストールされていない場合、CPANを使ってインストールしましょう。

Text::CSV_XSのインストール
perl -MCPAN -e shell
install Text::CSV_XS

Text::CSV_XSを使ってCSVを読み込み、ファイルに出力する場合は以下の通りです。

#!/usr/bin/perl -w
use strict;
use Text::CSV_XS;
my $csv = Text::CSV_XS->new ({ binary => 1 });
open my $fh, "<", "t.csv";
open my $fh_out, ">", "t.txt";
while (my $columns = $csv->getline ($fh)) {
print $fh_out map({ "[$_]\t" } @$columns), "\n";
}
$csv->eof;
close $fh_out;
close $fh;

データに日本語が含まれる場合は、new()の際に{binary=>1}オプションを設定します。

Text::CSV_XS->new({binary=>1});

ファイルをロックして書き込みたい場合は、Fcntlモジュールのflock()を使います。書き込みの前後ロックとロック解除のコードを追加するだけです。
ついでにIO::Fileを使って、ファイルの入出力もオブジェクト指向スタイルにしてみましょう。

#!/usr/bin/perl -w
use strict;
use Text::CSV_XS;
use IO::File;
use Fcntl qw(:flock);
# データに日本語が含まれる場合は{binary=>1}オプションを忘れずに
my $csv		= Text::CSV_XS->new({binary=>1});
my $io		= IO::File->new('./t.csv', "r");
my $io_out	= IO::File->new('./t.txt', "w");
flock($io_out, LOCK_EX);
while (not $io->eof and my $columns = $csv->getline($io)) {
$io_out->print(map({ "[$_]\t" } @$columns), "\n");
}
flock($io_out, LOCK_UN);
$io->close;
$io_out->close;

getline()はCSVから1カラムを読み出し、そのカラムをパースして配列リファレンスを返します。

$columns = $csv->getline($io)

getline()で取得した配列リファレンスの要素は下記のようにしてアクセスすることができますが、インデックスを指定する方法は可読性が悪いのが難です。

$columns = $csv->getline($io)
print $$columns[0];

下記のように、フィール名を用意しておいて、配列リファレンスの要素に対応させると、必要な要素に簡単にアクセスできるようになります。

# フィールド名の用意
my @f_name= qw/ id date category title text /;
while (not $io->eof and my $columns = $csv->getline($io)) {
my %field;
for ( my $i=0; $i<=$#f_name; $i++ ){
$field{$f_name[$i]}	= $$columns[$i];
}
# titleフィールドのみ出力
print $field{'title'},"\n";
}
参考
このエントリーをはてなブックマークに追加

ディレクトリ・ファイル一覧の取得方法

2009年8月19日 コメントの追加

Perlで任意のディレクトリにあるファイル一覧を取得する際は、いくつかの方法があります。今回はglob()とopendir()で取得する方法をご紹介します。

glob()でファイルの一覧を取得

簡単なのはglob()を使ったファイルの取得方法です。
glog()は カレントディレクトリにあるファイル名やディレクトリ名を取得することができます。

# 引数にアスタリスク( * )を指定すると、. で始まらないすべてのファイル一覧を取得
my @file	= glob "*";
print join( "\t", @file ), "\n";
"* .*"	. で始まるファイルも対象にする場合は引数に"* .*"を指定
my @file2	= glob "* .*";
print join( "\t", @file2 ), "\n";

glob()の引数に拡張子を指定すると、指定した拡張子のファイル一覧のみを取得することができます。

my @file = glob "*.txt *.pl";
print join( "\t", @file ), "\n";

別ディレクトリのファイル一覧を取得したときは、 chdir()でカレントディレクトリを移動するか、相対パスあるいは、絶対パスで指定します。

/var/www/htmlの拡張子.htmlに該当するファイルのみを取得する
chdir("/var/www/html");
my @file	= glob "*.html";
print join( "\t", @file ), "\n";
参考

opendir()でファイルの一覧を取得

ファイルハンドルを扱いたい場合はopendir()が便利です。
opendir()で任意のディレクトリをオープンし、readdir()でファイル名やディレクトリ名を取得します。

# ディレクトリオープン
opendir(DIRHANDLE, "./");
# ディレクトリエントリの取得
foreach(readdir(DIRHANDLE)){
next if /^\.{1,2}$/;	# '.'や'..'をスキップ
print "$_\n";
}
# ディレクトリクローズ
closedir(DIRHANDLE);
参考
このエントリーをはてなブックマークに追加

経過日数の計算

2009年6月17日 コメントの追加

time関数の利用方法について説明します。
例えば、どのようにしたら20日前の日付を得られるか、特定の日付から今日までの経過日数を算出するのはどのようにすればいいのかを考えていきたいと思っています。

現在の日付を求める

my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)
= localtime(time);
my $yyyymmddJ = sprintf("%04d年%02d月%02d日", $year + 1900, $mon +1, $mday);
print "$yyyymmddJです";

現在の時刻を日本語で表示させています。

それでは20日後の日付を求めるには、どうのようにしたらよいのでしょうか。

X日前後の日付を求める

time は現在の日時・時刻を1970年1月1日0時0分0からの経過秒で現すのでしたね。

print time, "<br>";
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)
= localtime(time);
my $yyyymmddJ = sprintf("%04d年%02d月%02d日", $year + 1900, $mon +1, $mday);
print "$yyyymmddJです";

先ほどのスクリプトの前にprint timeを入れてみました。

1005634570
2001年11月13日です

上の行が1970年01月01日0時0分0秒からの経過秒です。
下の行は上の経過秒を元にして得られた日付となります。

20日後の日付を求めるなら
60秒×60分×24時間×20日=1728000秒前を求めることになります。そこで

my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)
= localtime(time - 1728000);
my $yyyymmddJ = sprintf("%04d年%02d月%02d日", $year + 1900, $mon +1, $mday);
print "$yyyymmddJです";

下のように20日前の日付が得られました。20日後ならtimeに1728000秒をプラスすればいいのです。

2001年10月24日です

経過日数を求める

特定の日付から現在までの経過日数を求めるにはどうしたらいいでしょうか。経過秒から86,400秒(60秒×60分×24時間)で割れば日数を得られます。しかし今回は日付から経過日数を割り出さなければなりません。そんな時に便利な機能があります。timelocalです。これはperlのモジュールにある特殊な関数です。先ずモジュールの使い方から説明をします。

use Time::Local;

use でモジュールを使用することを宣言します。ここではTime::Localがモジュール名です。Time::Localのモジュールを宣言したことによりtimelocal関数を使用することができるようになりました。

use Time::Local;
$year=2001;
$mon =10;
$mday=24;
$hours = 0;
$min   = 0;
$sec   = 0;
$time = timelocal($sec, $min, $hours, $mday, $mon - 1, $year);
print $time, "<br>";
1003849200

先ほど秒数を使って20日前の日付(2001年10月24日)を求めましたが、ここでは2001年10月24日を指定して現在までの経過秒を求めました。それがtimelocal( )、モジュールで使用できる関数です。残念ながら、この値が正しいのか分かりませんので、localtimeを使って検証してみましょう。

use Time::Local;
$year=2000;
$mon =10;
$mday=24;
$hours = 0;
$min   = 0;
$sec   = 0;
$time = timelocal($sec, $min, $hours, $mday, $mon - 1, $year);
print $time, "<br>";
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($time);
$yyyymmddJ = sprintf("%04d年%02d月%02d日", $year + 1900, $mon + 1, $mday);
$hhmmssJ = sprintf("%02d時%02d分%02d秒", $hour, $min, $sec);
print "$yyyymmddJで$hhmmssJです";

1003849200
2000年10月24日で00時00分00秒です

正しい経過秒を取得できたようです。経過秒から経過日数を算出できました。

ついでに1970年1月1日0時0分0秒から現在までの経過秒と、2000年10月24日0時0分0秒までの経過秒も表示しておきます。

use strict;
use Time::Local;
# 2011/7/24
my $y	= 2011;
my $m	= 7;
my $d	= 24;
my $time	= timelocal( 0, 0, 0, $d, $m-1, $y );
my $time1	= time;
my $lap		= int( abs(($time1 - $time) / 60 / 60 / 24) );
print "Content-Type:text/html\n\n";
print "$lap";

timelocalで求めた2001年10月24日0時0分0秒までの経過秒を $time に、現在までの経過秒を $time1 に代入しました。

my $lap		= int( abs(($time1 - $time) / 60 / 60 / 24) );

現在までの経過秒($time1)から指定の日付までの経過秒($time1)を引き、それを一日の秒数(60秒×60分×24時間)で割っています。abs で絶対値にし int で整数にしています。

このエントリーをはてなブックマークに追加

Jump to the top