まえがき
台風による悪天候で暇なので、気まぐれで記事を書いてみます。
世の中の流れは「とにかく Python」って感じですが、
最近は個人的に Perl に触れる機会があったので
復習も兼ねて記事に残してみます。備忘録程度です。
Better な書き方があれば是非ご教授くださいませ。。
概要
指定されたファイルを読み込み、
コメント行(# で始まる行)と空行を除いて
標準出力に出力するだけのプログラム remove_comments.pl
を書きました。
[MBA ~/work/perl]$ cat test.txt
hoge
\# hogehoge
\# huga
huga
\#\#\# hugahuga
[MBA ~/work/perl]$
[MBA ~/work/perl]$ perl remove_comments.pl test.txt
hoge
huga
[MBA ~/work/perl]$
引数がなかったり、2つ以上指定されていた場合はエラー終了します。
指定されたファイルが無い場合もエラー終了します。
(エラーメッセージの雑さは許してください)
[MBA ~/work/perl]$ perl remove_comments.pl test.txt /etc/hosts
Invalid arguments
[MBA ~/work/perl]$ perl remove_comments.pl
Invalid arguments
[MBA ~/work/perl]$ perl remove_comments.pl hoge
hoge: No such file or directory
[MBA ~/work/perl]$
コード
以下が実装です。
use strict;
use warnings;
use constant {
EXIT_SUCCESS => 0,
EXIT_FAILURE => 1,
};
our %ERR_MSG = (
'INVALID_ARGS' => "Invalid arguments\n",
'FILE_NOT_FOUND' => "No such file or directory\n",
'CANNOT_OPEN_FILE' => "Cannot open file\n",
'CANNOT_CLOSE_FILE' => "Cannot close file\n",
);
main();
sub main {
check_args();
my ($filename) = @ARGV;
open my $fh, "<", $filename or die "$filename: $ERR_MSG{CANNOT_OPEN_FILE}";
my $match_pattern = '^\s*(#.*|)$';
while (my $line = <$fh>) {
chomp $line;
if ($line !~ $match_pattern) {
print "$line\n";
}
}
close $fh or die "$filename: $ERR_MSG{CANNOT_CLOSE_FILE}";
return EXIT_SUCCESS;
}
sub check_args {
my $ARGS_NUM = 1;
if (@ARGV != $ARGS_NUM) {
print STDERR "$ERR_MSG{INVALID_ARGS}";
exit EXIT_FAILURE;
}
my ($filename) = @ARGV;
if (!-f $filename) {
print STDERR "$filename: $ERR_MSG{FILE_NOT_FOUND}";
exit EXIT_FAILURE;
}
}
1;
__END__
お約束部分・定数定義部分
use strict;
use warnings;
use constant {
EXIT_SUCCESS => 0,
EXIT_FAILURE => 1,
};
our %ERR_MSG = (
'INVALID_ARGS' => "Invalid arguments\n",
'FILE_NOT_FOUND' => "No such file or directory\n",
'CANNOT_OPEN_FILE' => "Cannot open file\n",
'CANNOT_CLOSE_FILE' => "Cannot close file\n",
);
main();
use strict; use warnings;
はつけましょう。お約束。
use constant
で定数を定義しています。TURE/FALSE なども、しばしば定義します。
our %ERR_MSG
でグローバル変数によりエラーメッセージを定義しています。
実際は定数定義用のモジュールを作って、それを use
するのがよいと思います。
今回はコードの練習ということで...
引数チェック: check_args()
引数の数の確認および指定されたファイルの存在を確認します。
sub check_args {
my $ARGS_NUM = 1;
if (@ARGV != $ARGS_NUM) {
print STDERR "$ERR_MSG{INVALID_ARGS}";
exit EXIT_FAILURE;
}
my ($filename) = @ARGV;
if (!-f $filename) {
print STDERR "$filename: $ERR_MSG{FILE_NOT_FOUND}";
exit EXIT_FAILURE;
}
}
メインルーチン: main()
sub main {
check_args();
my ($filename) = @ARGV;
open my $fh, "<", $filename or die "$filename: $ERR_MSG{CANNOT_OPEN_FILE}";
my $match_pattern = '^\s*(#.*|)$';
while (my $line = <$fh>) {
chomp $line;
if ($line !~ $match_pattern) {
print "$line\n";
}
}
close $fh or die "$filename: $ERR_MSG{CANNOT_CLOSE_FILE}";
return EXIT_SUCCESS;
}
open my $fh, "<", $filename
で、ファイルを読み出します。書き込みのときは ">"
。
これもほぼお約束で、ファイルオープンに失敗した場合に異常終了するように、or die
をつけます。
while (my $line = <$fh>) {...}
で、ファイルハンドラから一行ずつ取り出して
それを $line
に格納します。単に while (<$fh>) {...}
だと $_
に格納されます。
デフォルト変数は便利ですが、可読性を損ねることがあるので私はあまり好まないです。
my $match_pattern = '^\s*(#.*|)$'
で正規表現のパターンを定義しています。
このパターンにマッチしないものが欲しいので、
if ($line !~ $match_pattern) {...}
とします。
マッチしたものが欲しいときは if ($line =~ $match_pattern) {...}
です。
マッチした部分の文字列が欲しい場合は、パターン文字列の対象箇所を()
で括った上で
$1
, $2
などの変数を用いて参照します。詳細はここには書きません。
close $fh
でファイルを閉じます。
学生時代、実家の冷蔵庫を開けたままでお茶を飲んでたらよく親から
「開けたら閉める!!」と叱られたものです(10秒くらいいいじゃん、って思います)。
ファイルも同じ、開けたら閉めましょう。
Python の with open(file) as f:
みたいなの、Perl にもないかな...
おわりに
Perl の基本的な記事を書いて読む人っているのでしょうか。
Qiita を眺めてても Python / DeepLearning の記事が多い印象です。
私もトレンドに乗っかって、Python を完全に理解したいものです。
なお、本記事の内容を Python で書いた記事もあります。
(https://qiita.com/okateim/items/6e0955eac1fb66a8981e)
同じ内容で言語を変えたときにPV数にどれくらい差がでるのだろうと
気になって同じ内容2言語で書いてみた次第です。
(やっぱり Python のほうがたくさん見られてるみたいです)