Edited at

[Perl] ファイルを読み込みコメント行と空行以外を表示

More than 1 year has passed since last update.


まえがき

台風による悪天候で暇なので、気まぐれで記事を書いてみます。

世の中の流れは「とにかく 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]$


コード

以下が実装です。


remove_comments.pl


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 のほうがたくさん見られてるみたいです)