Perl
正規表現
ファイル操作
備忘録

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

まえがき

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