プログラミング入門第 12 回講義 リダイレクトとパイプ ファイルリダイレクションパイプ応用例 マークのあるサンプルプログラムは /home/course/prog0/public_html/2006/lec/source/ 下に置いてありますから 各自自分のディレクトリにコピーして コンパイル 実行してみてください Prog-0 2006 Lec 12-1
ファイル (p.326) ファイル : あらゆる入出力データ 装置を論理化する概念 ( 実際のデータ記録装置, 記録方式, 記録形式等に依存しない処理環境の実現 ) C プログラム ファイルに表示 ファイルに印刷 ファイルから入力 ファイルに書込み 標準手続 ファイル処理 表示表示 印刷印刷 入力入力 読書き 実際の装置 Prog-0 2006 Lec 12-2
ファイル もし ファイルの概念が無かったら!? 表示 読書き 読書き それぞれの入出力装置の違いを別々にプログラムで記述することが必要!! 印刷 入力 読書き Prog-0 2006 Lec 12-3
Prog-0 2006 Lec 12-4 C プログラムの流れ C プログラムの流れ scanf(),printf() はそれぞれ標準入力ファイル ( キーボード ) 標準出力ファイル ( ディスプレイ ) に対応 C プログラム標準入力ファイル標準出力ファイル標準エラー出力ファイルLec12-12 参照
標準入出力とリダイレクション (p.365) リダイレクションとは 標準入出力ファイルの切り替えをプログラムの外で行う機能 リダイレクションの例 キーボード入力 ディスク入力に変更 画面表示 ディスク出力に変更 Prog-0 2006 Lec 12-5
リダイレクション (p.20) C プログラム 装置の切り替え 標準入力操作命令 標準出力操作命令 Prog-0 2006 Lec 12-6
標準入出力のリダイレクションの書式 実行ファイル名 入力ファイル名 例 : プログラム./a.out の標準入力を test.dat から読み込む./a.out test.dat 実行ファイル名 > 出力ファイル名実行ファイル名 >> 出力ファイル名 Prog-0 2006 Lec 12-7 例 :./a.out の標準出力を test.dat に書き込む./a.out > test.dat./a.out >> test.dat 先頭から書込み 末尾に追加書き
リダイレクションの例 (1) 例 1. ファイル一覧をファイル list.txt に書込む ls > list.txt 例 2. list.txt の行, 単語, 文字数を表示する wc list.txt 例 3. list.txt から文字列 file を含む行だけ file.txt に書き込む grep file list.txt > file.txt Prog-0 2006 Lec 12-8
リダイレクションの例 (2) リダイレクションでファイル中のデータの平均を計算する std1dc1{s10000001: #include #include stdio.h>./a.out./a.out stdio.h> 1 #include #include stdlib.h> 2 3 4 5 stdlib.h> 2 3 4 5 6 Control+Dは入力 Control+D の終わりを示す main() Control+D main() Average { Average 3.500000 3.500000 std1dc1{s10000002: int int i, i, data, data, result, result, sum sum./a.out./a.out lec12-1.data lec12-1.data Average (i (i ; ; i++){ Average 3.500000 3.500000 i++){ std1dc1{s10000003: result result scanf("%d",&data); cat cat lec12-1.data lec12-1.data 1 if if (result (result EOF) EOF) break; 2 3 4 5 break; 2 if if (result (result!! 1) 1) exit(1); 3 4 5 6 exit(1); std1dc1{s10000004: sum sum + + data; data; printf("average printf("average %f\n",(float)sum/i); Prog-0 2006 Lec 12-9 /home/course/prog0/public_html/2006/lec/source/lec12-1.c
標準入出力とパイプ (p.21) パイプとは二つのプログラムの標準出力と標準入力を結合する機能コマンド間に を挟むパイプの例 cat ex1.c grep 'printf' cat の出力が grep の入力となる プログラム cat ファイル出力処理 標準出力 パイプ 標準入力 プログラム grep ファイル入力処理 Prog-0 2006 Lec 12-10
パイプの例 例 1. ディレクトリ内のファイルをファイル容量の大きい順に表示 ls -l sort -k 4,4 -nr 例 2. そのディレクトリ内のファイルの数を数える ls wc -l 例 3. result.txt(id と点数のペアデータ ) から得点の高い順に 10 人表示する cat result.txt sort -k 2,2 -nr head -10 Prog-0 2006 Lec 12-11
標準エラー出力 (p.47,349) プログラムの出力をリダイレクトやパイプする場合に エラーメッセージを標準出力に出力していると エラーメッセージも画面に表示されず リダイレクトやパイプの対象になってしまう このためエラー出力用の標準ファイルを別に設け ( 標準エラー出力と呼ぶ ) リダイレクト パイプの場合も画面にエラーメッセージを表示するようにしている 標準エラー出力への出力は以下のようにして行う fprintf(stderr," 書式 ", ", 変数リスト ); ); 例 :fprintf(stderr," 入力した値がおかしい \n"); Prog-0 2006 Lec 12-12
プログラム内の標準入出力と標準エラー (p.47) #include stdio.h> main() { scanf( ); ); printf( ); ); fprintf(stderr, ); この中で定義 標準入力から入力標準入力から入力 標準出力ヘ出力標準出力ヘ出力 標準エラーヘ出力標準エラーヘ出力 Prog-0 2006 Lec 12-13
リダイレクト パイプの例簡単な画像処理 簡単な画像処理画画像を標準入力から入力入力された画像を処理 ( 例えば左に90 度回転 ) 画像を標準出力から出力エラー情報は標準エラー出力により表示 標準入力 標準像出力処標準エラー出力理エラー Prog-0 2006 Lec 12-14
簡単な画像形式 Plain PBM(Portable Bitmap) 形式 必ず P1 (magic number) P1 P1 320 160 画像の大きさ : 横縦 ( 画素数 ) 0 0 0 0 1 0 1 0... 1 1 0 0 0 1 0 0 1 1 0 1 1 0 1... 0 0 1 0 1 0 1 0 0 0 0 1 0 1 0... 1 1 0 0 0 1 0...... 0 1 1 0 1 1 0 1... 0 0 1 0 1 0 1 0 0 0 0 1 0 1 0... 1 1 0 0 0 1 0 0 1 1 0 1 1 0 1... 0 0 1 0 1 0 1 横 縦の数の画素データ ( 左上から横に ) 1: 黒 0: 白 データ間は空白で区切られている ( 本当は無くても良い ) 横 (x) 軸縦(y) 軸原点 Prog-0 2006 Lec 12-15 1 行は 70 文字以下今回はあまり気にしない
データ入力部分 ( 共通部分 ) #include #include stdio.h> stdio.h> #include #include stdlib.h> stdlib.h> #define #define MAX_X MAX_X 800 800 #define #define MAX_Y MAX_Y 800 800 #define #define BLACK BLACK 1 1 #define #define WHITE WHITE 0 0 画素データ読み込み Prog-0 2006 Lec 12-16 取り扱える画像の最大の大きさ ( 縦 横 ) 最初に P1 と書いていないものは デ画像用二次元配列 main(){ ータ形式が違う main(){ int int img_data[max_y][max_x]; この部分はプログラミング入門では扱わ img_data[max_y][max_x]; int int i, i, j, j, x_size, x_size, y_size; ない文字型を使用しているので 今のと y_size; ころは呪文だと思っておいてください ( 詳しくはP67,p139 参照 ) if if (getchar() (getchar()!! 'P' 'P' getchar() getchar()!! '1'){ '1'){ fprintf(stderr, fprintf(stderr, " " データの形式が違いますデータの形式が違います \n"); \n"); exit(1); exit(1); x,yそれぞれの画素数を得る scanf("%d", scanf("%d", &x_size); &x_size); 画素数が多すぎる場合 scanf("%d", scanf("%d", &y_size); &y_size); if if (x_size (x_size > > MAX_X MAX_X y_size y_size > > MAX_Y){ MAX_Y){ fprintf(stderr, fprintf(stderr, " " データが大きすぎますデータが大きすぎます \n"); エラーメッセージは全て標 \n"); exit(2); 準エラー出力へ exit(2); scanf 入力データがおかしいか (i (i i i y_size; y_size; i++){ i++){ 個数より早くEOFになった場合 (j (j j j x_size; x_size; j++){ j++){ if(scanf("%d",&img_data[i][j]) if(scanf("%d",&img_data[i][j])!! 1){ 1){ fprintf(stderr, fprintf(stderr, " " データ入力に異常がありますデータ入力に異常があります \n"); \n"); exit(3); データが白黒ではない場合 exit(3); if(img_data[i][j] if(img_data[i][j]!! WHITE WHITE && && img_data[i][j] img_data[i][j]!! BLACK){ BLACK){ fprintf(stderr, fprintf(stderr, " " データが異常でしたデータが異常でした \n"); \n"); exit(4); exit(4); 各エラーコードに対応したサンプルデータが /home/course/prog0/public_html/2006/lec/source/lec12-err{1,2,3a,3b,4.pbm にあるので試してみるとよい
データ出力 左 90 度回転出力 最初にP1と画素数を出力 printf("p1\n"); printf("p1\n"); 縦横反転するので %d\n", %d\n", y_size, y_size, x_size); x_size); y,xの順となる (i (i i i x_size; x_size; i++){ i++){ (j (j j j y_size; y_size; j++){ j++){ ",img_data[j][x_size-1-i]); ",img_data[j][x_size-1-i]); n 個 printf("\n"); printf("\n"); 1 行分終了で改行左 90 度回転 (y_size) [0][0] [0][1] [0][m-2] [0][m-1] [1][0] [1][1] [1][m-1] [2][0] [2][m-1] [3][0] [3][m-1] [n-1][0] [n-1][1] m 個 (x_size) 左 90 度 [n-1][m-2] [n-1][m-1] n 個 (y_size) m 個 (x_size) [0][m-1] [1][m-1] [0][m-2] [1][m-2] [0][m-3] [0][m-4] [n-2][m-1] [n-1][m-1] [n-1][m-2] [n-1][m-3] [n-1][m-4] /home/course/prog0/public_html/2006/lec/source/lec12-rl.c /home/course/prog0/public_html/2006/lec/source/lec12-rl.c Prog-0 2006 Lec 12-17 [0][0] [1][0] [n-2][0] [n-1][0]
実行結果 元データの表示 結果をdisplayコマンドに std1dc1{s10000001: display lec12-1.pbm & パイプ std1dc1{s10000002: gcc gcc lec12-rl.c -o -o rotl rotl std1dc1{s10000003:./rotl lec12-1.pbm display & std1dc1{s10000004: 元画像 元画像ファイルをリダイレクトで入力 Prog-0 2006 Lec 12-18
白黒反転プログラム データ出力 最初にP1とx,yの画素数を出力 printf("p1\n"); printf("p1\n"); %d\n", %d\n", x_size, x_size, y_size); y_size); (i (i i i y_size; y_size; i++){ i++){ (j (j j j x_size; x_size; j++){ j++){ if(img_data[i][j] if(img_data[i][j] BLACK) BLACK) ",WHITE); ",WHITE); else else ",BLACK); ",BLACK); printf("\n"); 白なら黒を出力 printf("\n"); 黒なら白を出力 /home/course/prog0/public_html/2006/lec/source/lec12-iv.c その他以下のプログラムソースがある ( 演習問題の関係で * がついているものは公開していない ) 右 90 度回転 * 左右反転上下反転 * 辺縁検出 /home/course/prog0/public_html/2006/lec/source/lec12-rr.c /home/course/prog0/public_html/2006/lec/source/lec12-lr.c /home/course/prog0/public_html/2006/lec/source/lec12-ud.c /home/course/prog0/public_html/2006/lec/source/lec12-eg.c Prog-0 2006 Lec 12-19
実行結果 左 90 度回転の後白黒反転 std1dc1{s10000001: gcc gcc lec12-iv.c -o -o invrt invrt std1dc1{s10000002:./rotl lec12-1.pbm./invrt display & std1dc1{s10000003: 元画像 ファイルからリダイレクト このようにパイプで処理をどんどん繋いで行く事が出来る Prog-0 2006 Lec 12-20
ノイズ除去 意味のある画像だと ぽつんと点があることはあまりないそこで ある点を見た時自分が黒点で 両隣の点が白い場合自分が白点で 両隣の点が黒い場合は ノイズ 雑音 ( この場合は画像中のゴミ ) と認識して両隣の色で置き換えることにする つまり : 例えば以下のような感じで画像が改善される Prog-0 2006 Lec 12-21
ノイズ除去プログラム printf("p1\n"); printf("p1\n"); %d\n", %d\n", x_size, x_size, y_size); y_size); (i (i i i y_size; y_size; i++){ i++){ 行の先頭は隣がないのでそのまま出力 ",img_data[i][0]); ",img_data[i][0]); (j (j 1; 1; j j x_size-1; x_size-1; j++){ 横方向の両端を除いた点 j++){ if(img_data[i][j-1] if(img_data[i][j-1] WHITE WHITE && に関して処理を行う && img_data[i][j] img_data[i][j] BLACK BLACK && && img_data[i][j+1] img_data[i][j+1] WHITE){ WHITE){ ",WHITE); 白黒白なら白を出力 ",WHITE); else else if(img_data[i][j-1] if(img_data[i][j-1] BLACK BLACK && && img_data[i][j] img_data[i][j] WHITE WHITE && && img_data[i][j+1] img_data[i][j+1] BLACK){ BLACK){ ",BLACK); ",BLACK); 黒白黒なら黒を出力 else else ",img_data[i][j]); ",img_data[i][j]); 2つのケース以外の場合はそのまま出力 ",img_data[i][x_size-1]); 行の最後は隣がないのでそのまま出力 ",img_data[i][x_size-1]); printf("\n"); printf("\n"); Prog-0 2006 Lec 12-22 /home/course/prog0/public_html/2006/lec/source/lec12-nc.c
実行結果 結果をdisplayコマンドにパイプ std1dc1{s10000001: gcc gcc lec12-nc.c -o -o hncut hncut std1dc1{s10000002:./hncut lec12-2.pbm display & std1dc1{s10000003: 元画像 ファイルからリダイレクト Prog-0 2006 Lec 12-23 細かい点状のノイズを加えた ノイズは減ったが一部細い線が欠落した
パイプとリダイレクションのまとめ パイプのまとめ 標準入力の切り替え コマンドの前に 例 :ls wc -l 標準出力の切り替え コマンドの後に 例 :cat hoge.txt head リダイレクションまとめ 標準入力の切り替え 例 :./a.out in.txt 標準出力の切り替え ( ファイル書き換え ) > 例 :ls l > out.txt 標準出力の切り替え ( ファイルに追加 ) >> 例 :ls l >> out.txt Prog-0 2006 Lec 12-24