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

# まえがき

台風による悪天候で暇なので、気まぐれで記事を書いてみます。
世の中の流れは「とにかく Python」って感じですが、
最近は個人的に Perl に触れる機会があったので
復習も兼ねて記事に残してみます。備忘録程度です。
Better な書き方があれば是非ご教授くださいませ。。

# 概要

指定されたファイルを読み込み、
コメント行（# で始まる行）と空行を除いて
標準出力に出力するだけのプログラム `remove_comments.pl` を書きました。
<pre>
[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]$ 
</pre>

引数がなかったり、2つ以上指定されていた場合はエラー終了します。
指定されたファイルが無い場合もエラー終了します。
（エラーメッセージの雑さは許してください）
<pre>
[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]$ 
</pre>

# コード

以下が実装です。

```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__
```

## お約束部分・定数定義部分

```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();
```

`use strict; use warnings;` はつけましょう。お約束。
`use constant` で定数を定義しています。TURE/FALSE なども、しばしば定義します。
`our %ERR_MSG ` でグローバル変数によりエラーメッセージを定義しています。
実際は定数定義用のモジュールを作って、それを `use` するのがよいと思います。
今回はコードの練習ということで...

## 引数チェック: check_args()

引数の数の確認および指定されたファイルの存在を確認します。

```perl

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()

```perl

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