LoginSignup
4
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-07-30

まえがき

本記事は、Perl で書いたものの Python 移植版です。
(https://qiita.com/okateim/items/4df7ebca399901660072)

同じ内容で言語を変えたときにPV数にどれくらい差がでるのだろうと
気になって同じ内容2言語で書いてみた次第です。
(やっぱり Python のほうがたくさん見られてるみたいです)

Python での変数定義、ファイル読み込み、
および正規表現処理の復習と備忘録を兼ねて記事に残します。
Better な書き方があれば是非ご教授くださいませ。。

なお、今回用いた Python のバージョンは以下のとおりです。

[MBA ~/work/python]$ python --version
Python 3.6.3 :: Anaconda, Inc.
[MBA ~/work/python]$ 

概要

指定されたファイルを読み込み、
コメント行(# で始まる行)と空行を除いて
標準出力に出力するだけのプログラム remove_comments.py を書きました。

[MBA ~/work/python]$ cat test.txt 

hoge

# hogehoge
    # huga
    huga

### hugahuga

[MBA ~/work/python]$ 
[MBA ~/work/python]$ python remove_comments.py test.txt 
hoge
    huga
[MBA ~/work/python]$ 

引数がなかったり、2つ以上指定されていた場合はエラー終了します。
指定されたファイルが無い場合もエラー終了します。
(エラーメッセージの雑さは許してください)

[MBA ~/work/python]$ python remove_comments.py test.txt /etc/hosts
Invalid arguments
[MBA ~/work/python]$ python remove_comments.py
Invalid arguments
[MBA ~/work/python]$ python remove_comments.py hoge
hoge: No such file or directory
[MBA ~/work/python]$ 

コード

以下が実装です。

remove_comments.py
import sys
import os
import re

EXIT_SUCCESS = 0
EXIT_FAILURE = 1

ERR_MSG = {
    'INVALID_ARGS': "Invalid arguments\n",
    'FILE_NOT_FOUND': "No such file or directory\n",
}

def main():
    check_args()
    (_, filename) = sys.argv
    with open(filename, "r") as f:
        match_pattern = r'^\s*(#.*|)$'
        line = f.readline()
        while line:
            match = re.match(match_pattern, line)
            if not match:
                print(line, end="")
            line = f.readline()
    return EXIT_SUCCESS

def check_args():
    ARGS_NUM = 2;
    if len(sys.argv) != ARGS_NUM:
        sys.stderr.write(ERR_MSG['INVALID_ARGS'])
        sys.exit(EXIT_FAILURE);
    (_, filename) = sys.argv
    if not os.path.isfile(filename):
        sys.stderr.write(filename + ": " + ERR_MSG['FILE_NOT_FOUND'])
        sys.exit(EXIT_FAILURE)

if __name__ == '__main__': 
    main()

モジュールインポート部分・定数定義部分

import sys
import os
import re

EXIT_SUCCESS = 0
EXIT_FAILURE = 1

ERR_MSG = {
    'INVALID_ARGS': "Invalid arguments\n",
    'FILE_NOT_FOUND': "No such file or directory\n",
}

import ... で、プログラム内で使うモジュールをインポートします。
辞書変数 ERR_MSG で、エラーメッセージを定義しています。
実際は定数定義用のクラス config.py なんかを作って、それを import するのがよいと思います。
今回はコードの練習ということで...

引数チェック: check_args()

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

def check_args():
    ARGS_NUM = 2;
    if len(sys.argv) != ARGS_NUM:
        sys.stderr.write(ERR_MSG['INVALID_ARGS'])
        sys.exit(EXIT_FAILURE);
    (_, filename) = sys.argv
    if not os.path.isfile(filename):
        sys.stderr.write(filename + ": " + ERR_MSG['FILE_NOT_FOUND'])
        sys.exit(EXIT_FAILURE)

メインルーチン: main()

def main():
    check_args()
    (_, filename) = sys.argv
    with open(filename, "r") as f:
        match_pattern = r'^\s*(#.*|)$'
        line = f.readline()
        while line:
            match = re.match(match_pattern, line)
            if not match:
                print(line, end="")
            line = f.readline()
    return EXIT_SUCCESS

with open(filename, "r") as f: で、ファイルを読み出します。書き込みのときは "w"
with を利用することで、処理終了時に自動でファイルの閉じてくれます。
インデントは深くなりますが、便利です。ファイルを開くときは、必ず使うべきです。

f.readline() で、1行だけ読み出し、変数 line に格納します。
while line: line = f.readline() とすることで、1行ずつの読み出しを繰り返します。

match_pattern = r'^\s*(#.*|)$' で正規表現のパターンを定義し、
match = re.match(match_pattern, line) として、マッチオブジェクトを得ます。
このパターンにマッチしないものが欲しいので、
if not match: とします。
マッチしたものが欲しいときは if match: です。
マッチした部分の文字列が欲しい場合は、マッチオブジェクト match に対して、
match.group(0), match.group(1)などによって取得します。詳細はここには書きません。

ちなみに、re.match() は、先頭一致のみマッチします。
文字列のどこかでマッチすればよい場合は re.search() を使います。

おわりに

Python で、基本的なファイル操作および正規表現操作を実装しました。
久しぶりに Python でコードを書いたので文法やメソッドをかなり忘れていました。。
やはりコードは定期的に書くべきですね(反省)。

4
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2