Ex10
From Prog0
演習第10回
Contents |
演習問題
主な内容
- 標準入出力・エラー出力
- リダイレクション(リダイレクト)
- パイプ
以下の問題を解いて期限内に解答を提出してください。 また、一人ひとりの授業理解度を確認することを目的として、口頭試問形式による採点も行います。 採点対象の問題が解けたら手を挙げ、教員またはTA/SAを呼んでください。口頭で解答の内容を説明してもらいます。正しく説明できた場合は点数を付け、説明が不十分な場合はやり直してもらいます(間違えても減点はしません)。今回の口頭採点の対象は A-2 の1問のみです。その他の問題については、これまでと同様にmenuコマンドを使ってファイルを提出してください。なお、採点の都合上、口頭採点の対象となる問題のファイルもmenuコマンドで提出してください。
出席確認
演習時間中に出席確認をLMS上の「演習出欠」で行ってください。出席確認用のパスワードは演習時間のどこかのタイミングで提示されます。
A問題
A-1 パイプ、リダイレクション(リダイレクト)
ファイル名: ex10a1.txt
以下は世界の統計2025(出展、総務省統計局 https://www.stat.go.jp/data/sekai/pdf/2025al.pdf )による2021年におけるアジア各国の平均寿命・健康寿命をリストしたものである。国名の横左から順に平均寿命(男女平均、男、女)、健康寿命(男女平均、男、女)(単位:年)を示している。(文字数の問題でタブがずれて表示される場合がありますが、気にせず進めてください。)
まず、これ(以下)をコピーペーストしてファイル[age_agia.txt]を作成しなさい。
日本 84 82 87 73 72 75 アフガニスタン 59 57 61 50 50 51 アラブ首長国連邦 78 77 80 67 67 67 イエメン 66 63 69 56 56 57 イスラエル 82 82 82 71 72 70 イラク 71 69 74 61 60 62 イラン 75 73 76 64 64 64 インド 67 66 69 58 58 58 インドネシア 68 66 70 61 60 61 ウズベキスタン 72 70 74 63 63 64 オマーン 72 71 75 63 63 64 カザフスタン 70 66 74 62 59 64 韓国 84 81 87 72 71 74 カンボジア 69 66 71 61 59 62 北朝鮮 73 70 76 65 63 66 クウェート 79 77 82 68 67 68 サウジアラビア 76 76 77 66 66 65 シリア 72 71 74 62 62 63 シンガポール 84 82 86 74 72 75 スリランカ 77 74 80 67 65 68 タイ 75 72 79 66 63 68 中国 78 75 81 69 67 70 トルコ 75 73 78 65 64 66 ネパール 70 68 72 60 60 61 パキスタン 66 65 67 57 57 57 バングラデシュ 73 72 75 63 63 63 フィリピン 66 63 70 59 57 61 ブルネイ 77 75 79 67 66 68 ベトナム 74 70 78 65 63 68 マレーシア 73 71 75 64 63 65 ミャンマー 68 65 71 60 58 62
次に、このファイルを基にパイプ、リダイレクト等を利用して、健康寿命の男女平均が長い国から五か国を順にファイル[age_asia_s.txt]に保存するコマンド列を解答ファイル[ex10a1.txt]に記して提出しなさい。
この際、ハンドアウト[Lec10-13]等を参照にしてコマンドを調べると良い。
A-2 (口頭試問) 標準エラー出力
ファイル名: ex10a2.c
以下は入力された整数値に対して各桁の数字を逆順にして出力する(「123」と入力したら「321」と出力する)処理を繰り返し行い、「0」が入力されたらそのまま終了、負数が入力されたらエラーメッセージを出力して終了するプログラムである。
しかし、このままだと、リダイレクションでファイル出力する場合に、[実行例1]のように入力を促すメッセージやエラーメッセージも一緒にファイルに書き込まれてしまい不便である。
[実行例2]のように、メッセージが出力ファイルではターミナル上に表示されるよう、プログラムを修正すると供に下線部を埋めてプログラムを完成させなさい。
#include <stdio.h> int main() { int num, rev_num; while (1) { printf("Enter a number: "); scanf("%d", &num); if (________) break; if (_______) { printf("An error occurred.\n"); _____; } for (rev_num = 0; num > 0; num /= 10) { rev_num = __________ + ______; } printf("%d\n", rev_num); } return 0; }
[実行例1]
% ./a.out > output.txt
2345
87
456
-3
% cat output.txt
Enter a number: 5432
Enter a number: 78
Enter a number: 654
Enter a number: An error occurred.
%
[実行例2]
% ./a.out > output.txt
Enter a number: 2345
Enter a number: 87
Enter a number: 456
Enter a number: -3
An error occurred.
% cat output.txt
5432
78
654
%
B問題
B-1 リダイレクションによるデータの入力と2次元配列の操作
ファイル名: ex10b1.c
以下のような行列がテキストファイルとして保存されているものとする。
4 3 10 11 12 20 21 22 30 31 32 40 41 42
1行目には行列の行数と列数が記入され、その後に行列の要素が列挙されている。
行列のデータをリダイレクションにより読み込んで、その転置行列(m 行 n 列の行列 A に対して A の (i, j) 要素と (j, i) 要素を 入れ替えた n 行 m 列の行列、つまり対角線で成分を折り返した行列)を出力するプログラムを作成しなさい。
上記のサンプルデータでは4行3列の行列が格納されているが、プログラムの仕様としては最大10行10列の行列まで対応できるようにすること。
そのため、
int matrix[SIZE][SIZE] int t_matrix[SIZE][SIZE]
という二つの二次元配列を宣言し(マクロSIZEの値を10とする)、実行例に従って配列matrixにファイルから読み込んだ配列の要素を格納・表示し、配列matrixから 取り出した要素を配列t_matrixに転置した形で格納してから、その内容を表示するという手順で処理を進めることとする。
プログラムは、/* 入力と入力データの表示 */、/* 転置処理 */、/* 表示 */の3つの処理ブロックを意識し、コメントや空行を入れて分かりやすく書きなさい。
なお、設定された配列・行列のサイズを超えた入力があった場合のエラー処理は考えなくてよい。
動作の確認にあたっては、上記のテキストデータの内容を matrix.txt というファイル名で自分の作業ディレクトリに保存して使用すること。
実行例
% ./a.out < matrix.txt 行数:4, 列数:3 10 11 12 20 21 22 30 31 32 40 41 42 転置後行数:3, 列数:4 10 20 30 40 11 21 31 41 12 22 32 42
B-2 二次元配列、標準エラー出力
ファイル名: ex10b2.c
以下は2行3列の行列matの指定された各要素に正の整数dataを順に入力して全要素数MAX回入力後に、[実行例1]のようにその行列を表示するプログラムである。
このプログラムでは、matの行数GYOと列数RETSUの値はマクロで定義しており、dataに負の数を入力した場合に、[実行例2]のようにエラーメッセージを出力して処理を終了するようになっている。この時、メッセージの出力先は標準エラー出力(stderr)である。また、ハンドアウトLec10-4の例と異なり、forの二重ループでなくwhile文のみで二次元配列の要素を入力できるようにしている。下記の空白部を埋めて実行例のような動作を示すプログラムを完成させ、提出すること。
#include <stdio.h>
#define GYO 2
#define RETSU 3
int main(){
int i,j,k;
int data,mat[GYO][RETSU];
int MAX=GYO*RETSU;
k=0;
while(k<MAX){
-------------------------ここから----------------------------------
------------------------ここまで----------------------------------
mat[i][j]=data;
k+=1;
}
------------------------ここから------------------------------------
-----------------------ここまで------------------------------------
}
return 0;
}
[実行例1]
% ./a.out
1行1列の要素の値を正の整数で入力してください
1
1行2列の要素の値を正の整数で入力してください
2
1行3列の要素の値を正の整数で入力してください
0
2行1列の要素の値を正の整数で入力してください
4
2行2列の要素の値を正の整数で入力してください
5
2行3列の要素の値を正の整数で入力してください
8
行列の値は
1 2 0
4 5 8
%
[実行例2]
% ./a.out
1行1列の要素の値を正の整数で入力してください
7
1行2列の要素の値を正の整数で入力してください
11
1行3列の要素の値を正の整数で入力してください
-4
Error!負の値が入力されました
%
B-3 画像の上下反転
ファイル名: ex10b3.c
以下に、プログラムで画像を上下反転させるための手順とそのプログラムのソースがあります。 プログラム中の下線部を埋めて完成させなさい。
なお、実行ファイル名は gflpud とすること(以下の実行例とハンドアウトを参照)。
手順:
- 画像情報を保持するための2次元配列とその他の変数の宣言
- カウンタ変数の宣言
- 画像サイズ、濃淡情報の読み込み及びエラーチェック
- 画像データの読み込み及びエラーチェック
- データ形式の出力
- 画像サイズ、濃淡情報の出力
- 画像データの出力(2重ループを使い、上下を逆にする)
/*
* pgm形式の画像データを受け取って,上下反転する。
*
* 終了コード
* 0 : 正常終了
* 1 : 入力データの形式がおかしい
* 2 : 入力データの画素数または階調が範囲外
* 3 : 入力データがおかしいかデータが揃わないうちにEOFになった
* 4 : 入力データの画素の値が範囲外
*/
#include <stdio.h>
#include <stdlib.h>
#define MSIZE 800
int main()
{
int dat[MSIZE][MSIZE];
int i, j, width, height, maxval;
/* 入力 */
/* ファイル形式のチェック */
/* この部分はプログラミング入門の範囲を越えているので、このまま使って下さい。*/
if (getchar() != 'P' || getchar() != '2'){
fprintf(stderr, "データの形式が違います\n");
exit(1);
}
/* x,yそれぞれの画素数を得る */
scanf("%d %d", &width, &height);
/* Maxval(白の値)を得る */
scanf("%d",&maxval);
/* 画素数が範囲外の場合 */
if (width < 1 || height < 1 || width > MSIZE || height > MSIZE){
fprintf(stderr, "設定サイズが範囲外です\n");
exit(2);
}
/* Maxvalが範囲外 */
if (maxval < 1 || maxval >= 65536){
fprintf(stderr, "階調が範囲外です\n");
exit(2);
}
/* 実際のデータ入力 */
for (i = 0; i < height; i++){
for (j = 0; j < width; j++){
/* scanf入力データがおかしいかEOFになった場合 */
if(scanf("%d",&dat[i][j]) != 1){
fprintf(stderr, "データ入力に異常があります\n");
exit(3);
}
/* データが範囲外 */
if (dat[i][j] < 0 || dat[i][j] > maxval){
fprintf(stderr, "データが異常でした\n");
exit(4);
}
}
}
/* これより上はどのプログラムもほぼ同じ */
/* 出力 */
/* 最初にP2とx,yの画素数、Maxvalを出力 */
printf("P2\n");
printf("%d %d\n", width, height);
printf("%d\n", maxval);
/* 実際のデータ出力 */
for (i = 0; i < _____; i++){
for (j = 0; j < _____; j++){
printf("%2d ",dat[________][________]);
}
printf("\n");
}
return 0;
}
以下のようにして、テスト用画像ファイル(.pgm)を自分の ~/Prog0/Ex10 にコピーして使用すること:
% cd ~/Prog0/Ex10 % cp /home/course/prog0/public_html/2025/ex/source/ex10/feep64.pgm . % cp /home/course/prog0/public_html/2025/ex/source/ex10/neko64.pgm . % cp /home/course/prog0/public_html/2025/ex/source/ex10/ajisai64.pgm . % cp /home/course/prog0/public_html/2025/ex/source/ex10/Nakaniwa64.pgm .
テスト用画像ファイルの情報:
feep64.pgm: Netpbm image data, size = 24 x 7, greymap, ASCII text neko64.pgm: Netpbm image data, size = 265 x 202, greymap, ASCII text ajisai64.pgm: Netpbm image data, size = 437 x 305, greymap, ASCII text
"size"の部分が画像の横縦のピクセル数である。
コピーしたテスト用画像ファイルは以下のコマンドで表示することができる。
% display feep64.pgm
"feep64.pgm"は非常に小さい画像のため、ディスプレイ上では注意すること。
この実行例は拡大した画像を表示している。"neko64.pgm"、"ajisai64.pgm" 、"Nakaniwa64.pgm"についても、表示して確認するとよい。
[プログラムの実行例1]
% gcc ex10b3.c -o gflpud
% ./gflpud < feep64.pgm
P2
24 7
63
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 12 0 0 0 0 0 29 29 29 29 0 0 46 46 46 46 0 0 63 0 0 0 0
0 12 0 0 0 0 0 29 0 0 0 0 0 46 0 0 0 0 0 63 0 0 0 0
0 12 12 12 0 0 0 29 29 29 0 0 0 46 46 46 0 0 0 63 63 63 63 0
0 12 0 0 0 0 0 29 0 0 0 0 0 46 0 0 0 0 0 63 0 0 63 0
0 12 12 12 12 0 0 29 29 29 29 0 0 46 46 46 46 0 0 63 63 63 63 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
%
[プログラムの実行例2]
% ./gflpud < feep64.pgm | display -
[プログラムの実行例3]
% ./gflpud < neko64.pgm | display -
Extra問題
E-1 コントラストを上げる
ファイル名: ex10e1.c
コントラストを上げるプログラムを作成しなさい。
「コントラストを上げる」とは下の実行例の画像を見ても分かるように、各点の明るさ(輝度)を、明るい物はより明るく、暗い物はより暗くする事で、 全体として元画像よりコントラストが高い画像(白黒がはっきりした画像)を得ることです。
プログラムの実行ファイル名は gcont として作成すること。
「考え方」
- 倍率をマクロCOEFにて定義(#define COEF 1.5 のように)
- 濃淡の幅Maxvalの半分Maxval/2を基準に、ある点の明るさが
- Maxval/2以上(明るい)の場合、点の明るさをCOEF倍する
- Maxval/2未満(暗い)の場合は、点の明るさを1/COEF倍する
- また、補正後の値がMaxvalを超えた場合は値をMaxvalとする
- 最終的な値は整数(切り捨て)とする
[例](例はCOEFを1.5とした時の実行例)
% display Nakaniwa64.pgm
元画像![]()
[プログラムの実行例]
% ./gcont < Nakaniwa64.pgm | display -
コントラストを上げた画像![]()
課題提出上の注意事項
解答ファイルはmenuコマンドを使って提出してください。以下のようにmenuコマンドを実行し、表示されるメッセージに沿って操作すること。
%
~prog0/bin/menu
menuコマンドは、解答ファイルが ~/Prog0/Ex## のディレクトリに指定されたファイル名で置かれているものとして処理します。正常に提出された場合は ○ が、何らかのエラーが生じた場合は × が表示されます。
解答の提出期間は以下のとおりです。
問題 | 提出受付開始 | 提出〆切 |
---|---|---|
A問題 | 演習日の6日前の午後9時 | 演習終了時刻 |
B, Extra問題 | 演習日の6日前の午後9時 | 演習日の6日後の午後9時 |
提出は〆切前であれば何度でもやり直すことができます。再提出すると、前に提出したファイルは新しい内容で上書きされます。