まえがき
台風による悪天候で暇なので、気まぐれで記事を書いてみます。
世の中の流れは「とにかく 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 のほうがたくさん見られてるみたいです)