フィルタ系のプログラムの実装
すごーくひさしぶりに日記書いてみる。
cat, grepなど元のデータを加工して,データを出力するフィルタ系のプログラムを書く場合メモ。
1.入力が標準入力の場合とファイル入力の場合の切り替え
これらのプログラムで入力データを標準入力から得る場合と,ファイルから読み込む場合との両方を実装する場合を考えてみた。
filter < hoge.txt # 標準入力からの読み込み filter hoge.txt # ファイルからの読み込み
このように読み込み元が異なるが,処理自体は同じなのでコードを共通化したい。
C言語の場合(こちらのサイトから引用)
#include <stdio.h> #include <stdlib.h> .... char *filename; /* NULL ならファイル指定なしとします */ FILE *fp; .... if ( filename == NULL ) { /* ファイル指定なし */ fp = stdin; } else { /* ファイル指定あり */ fp = fopen( filename, "r" ); if ( fp == NULL ) { fprintf( stderr, "'%s'が読み込めません.\n", filename ); exit(1); /* 異常終了 */ } }
C++の場合その1:入力ストリームを切り替え得るwrapper関数を作成する
#include <iostream> #include <fstream> #include <string> // 読み込むべきストリームを切り替え得るwrapper // bool isStd : 入力ストリームが、std::cin かファイルか指定する // ifstream& ifs : 入力ストリームへの参照 std::istream& pickInput( bool isStd, std::ifstream& ifs) { if (isStd) { return std::cin; } else { return ifs; } } int main (void) { std::ifstream ifs("hoge.txt"); bool isStd; std::string line; isStd = true; // 標準入力から受け取る場合 while (std::getline( pickInput(isStd, ifs), line)) { if (isStd) { std::cout << "cin : " << line << std::endl; } else { std::cout << "ifs : " << line << std::endl; } } return 0; }
C++の場合その2:istream型のポインタ変数を利用
ifstream型変数もstd::cinもistreamクラスを継承していることに着目し,istream型のポインタ変数をもちいて,ifstream型変数,std::cinを切り替える。
#include <iostream> #include <fstream> #include <string> int main (void) { std::ifstream ifs("hoge.txt"); std::istream* ptrIs; bool isStd; std::string line; isStd = true; if (isStd) { ptrIs = &std::cin; } else { ptrIs = &ifs; } while (std::getline( *ptrIs, line)) { if (isStd) { std::cout << "cin : " << line << std::endl; } else { std::cout << "ifs : " << line << std::endl; } } return 0; }
ここで,ポインタ変数でなく参照変数を利用したくなるが,コンパイルエラーになる。*1
#include <iostream> #include <fstream> #include <string> int main (void) { std::ifstream ifs("hoge.txt"); std::istream& refIs = std::cin; bool isStd; std::string line; isStd = true; if (!isStd) { refIs = ifs; // compile error } while (std::getline( refIs1, line)) { if (isStd) { std::cout << "cin : " << line << std::endl; } else { std::cout << "ifs : " << line << std::endl; } } return 0; }
rubyの場合
ARGF.each_line do |line| filename = ARGF.filename lineno = ARGF.file.lineno puts "#{filename}:#{lineno}:#{line}" end