2014年9月29日月曜日

行指向ファイル処理フレームワーク

業務でファイルを一行ずつ読み込んで処理するプログラムをコーディングしているのですが、提供された設計書のフローチャートに従って実装を進めていたら、脳みそがぐちゃぐちゃにかき回されるようないらだちを覚えました。
"ファイルを一行ずつ読み込んで処理する"という要件は、あちこちで需要がありそうなので、モデルを考えてみました。 Process、FileFinder、FileHandler、LineHandlerの4つのクラスで構成します。


Process
処理全体の流れを制御します。FileFinder、FileHandler、LineHandlerを注入することで、動作をカスタマイズできるようにします。
class Process(object):
    def __init__(self, finder, file_handler, line_handler):
        if not isinstance(finder, FileFinder):
            raise Exception('finder is not FileFinder')
        if not isinstance(file_handler, FileHandler):
            raise Exception('file_handler is not FileHandler')
        if not isinstance(line_handler, LineHandler):
            raise Exception('line_handler is not LineHandler')
        self._finder = finder
        self._file_handler = file_handler
        self._line_handler = line_handler

    def exec(self):
        files = self._finder.find_files()
        for f in files:
            logging.debug('processing...' + f)
            if not os.path.exists(f):
                logging.warn('file does not exist: ' + f)
                continue
            self._file_handler.before_file_open(f)
            with open(f, 'r') as h:
                line_no = 1
                while True:
                    line = self._file_handler.next_line(h)
                    if line is None:
                        break
                    self._line_handler.handle_line(line, line_no, f)
                    line_no += 1
            self._file_handler.after_file_close(f)

FileFinder
処理の対象になるファイルを探します。
class FileFinder(object):
    def find_files(self):
        return []

class PyFileFinder(FileFinder):
    def find_files(self):
        return filter(lambda x: x.endswith('py'), os.listdir('.'))

FileHandler
ファイルを読み込んで"行"を返します。
class FileHandler(object):
    def before_file_open(self, file_path):
        pass

    def after_file_close(self, file_path):
        pass

    def next_line(self, f):
        l = f.readline()
        if not l:
            return None
        return l.rstrip()

LineHandler
FileHandlerが返した"行"を処理します。
class LineHandler(object):
    def handle_line(self, line, lineno, file_path):
        print('{}:{}:{}'.format(file_path, lineno, line))

まだまだプロトタイプレベルなので、これから育てていけたらいいと思う。

0 件のコメント:

コメントを投稿