練習 [13] ファイル処理(5/5)

練習 5(提出プログラム名:p1305.c)※必須ではない

この練習問題は必須ではない(提出しなくてもよい)が適切に解けていれば加点の対象とする.

練習 4 で作成したプログラム(p1304.c)を p1305.c という別名のファイルにコピーしなさい.
そして,p1305.c を編集して,コピー元とコピー先のファイル名を scanf で読み込むのではなく,コマンドライン引数で指定するかたちに変更しなさい.

さらに,コピー先には(実行例のように)行番号も書き込むようにしなさい.
(桁数は特に気にしなくてもよいが,"%3d: " といった書式にしておくと揃えやすい)
なお,行番号の出力部分はコピー文字数には含めないものとする.

【注意】
例年,行番号を付けることばかりに注意が向いてしまいそもそもコマンドライン引数を使っていないというミスが目立つので自分が該当しないよう注意せよ.
コマンドライン引数を使っている場合は,プログラム実行の際(a または ./a.out と入力する際)に一緒にコピー元とコピー先のファイル名もまとめて記述するという実行方式になる:
(例:Windows の場合)※コピー元 sample4.c  コピー先 aaa.c
  a sample4.c aaa.c

【ヒント】
行番号を書き込むのは常に「その行の先頭」部分なので,これからファイルへ書き込もうとしている文字その行の先頭文字ならば先に行番号を書き込むという処理にしておくとよいでしょう.
例えば,その行での出力文字数をカウントするための変数を用意しておいて,改行文字('\n')を出力するたびに 0 にリセットすることにしておきます.そうすれば,その変数の値が 0 の時だけ特別に行番号を出力するようにしておくと目的を達成できます.

【ヒント2】コマンドライン引数の使い方
まず,main 関数を
  int main(int argc, char *argv[])
と書いて始めるようにするとコマンドライン引数が使えるようになります.
そして例えば,
  a sample4.c aaa.c
として実行した場合,argc には自動的に 3 (上の個数)が格納されます.
さらに argv[1]"sample4.c" に,argv[2]"aaa.c" になります.
したがって,例えば
  fopen(argv[1], "r")
でもって "sample4.c" を読み出しモードでオープンできます.
こうすることで scanf を使っていちいちファイル名を読み込む必要が無くなります.

  • 【プログラムの実行例】(その1)赤字は実行時にキーボードから入力する内容
    ※ Mac 環境の人は a./a.out になります.
a sample4.c aaa.c
aaa.c に sample4.c の内容をすべて大文字にしてコピーしました(842文字)

【実行例を実行後の aaa.c の内容;行番号を “%3d: ” で表示した場合】
  1: #INCLUDE 
  2: #DEFINE SIZE 256
  3: 
  4: INT MAIN(VOID)
  5: {
  6:     CHAR FNAME[SIZE], TNAME[SIZE];
  7:     INT C;
  8:     FILE *FROM, *TO;
  9: 
 10:     PRINTF("コピー元のファイル名を入力してください:");
 11:     SCANF("%S", FNAME);
 12:     PRINTF("コピー先のファイル名を入力してください:");
 13:     SCANF("%S", TNAME);
 14: 
 15:     IF ((FROM = FOPEN(FNAME, "R")) == NULL)
 16:     {
 17:         PRINTF("[エラー] %S を開けませんでした\N", FNAME);
 18:         RETURN 1;
 19:     }
 20: 
 21:     IF ((TO = FOPEN(TNAME, "W")) == NULL)
 22:     {
 23:         PRINTF("[エラー] %S を開けませんでした\N", TNAME);
 24:         RETURN 1;
 25:     }
 26: 
 27:     C = FGETC(FROM);
 28:     WHILE (C != EOF)
 29:     {
 30:         FPUTC(C, TO);
 31:         C = FGETC(FROM);
 32:     }
 33: 
 34:     FCLOSE(FROM);
 35:     FCLOSE(TO);
 36: 
 37:     PRINTF("%S に %S の内容をコピーしました\N", TNAME, FNAME);
 38: 
 39:     RETURN 0;
 40: }
※末尾に “41:” が表示されて終わっても問題ないです(41: が表示されてもされなくても,どちらも正解とします)

【過去にあったミス(減点となり,やり直しを命じられる)】
上に示した実行例を確認せずに提出してしまっている.
 (例1)行番号が適切に出力されていない.
 (例2)コピーした文字数が違っている.
インデントに不備がある(VSCode 上でインデントを自動で揃える作業をやっていない).


第13回(Cプログラミング;3 限目の方)の練習問題は以上の5問です.
p1301.c ~ p1305.c を Moodle から提出してください.※ただし,p1305.c は必須ではないです.
くれぐれも各問題で記載されている注意事項や「過去にあったミス」を見落とさないようにしてください.
なお,コンパイルエラーや無限ループになるプログラムを 1 個でも提出した場合は総合評価を 0 点とするので十分に注意すること.
なお,提出後に間違いに気付いた場合,〆切前であれば差し替え(上書き)は可能です.