2023 年度「数学特論」 2024-01-17

§11.1 コマンドライン引数

gcc は「gcc a.c」と実行するとファイル a.c をコンパイルする。 gcc は、コンパイルすべきは a.c, という情報を受け取るわけである。 この受け取り方を学ぼう。 「gcc a.c」のようにコマンドプロンプトに入力されたものを「コマンドライン引数」と呼ぶ。

#include <stdio.h>

int main(int argc, char *argv[]) {
  int i;

  printf("argc は %d です.\n", argc);      /* argc に引数の数がはいる */

  for (i = 0; i < argc; i++) {
    printf("argv[%d] は %s です.\n", i, argv[i]);  /* argv[i] はその引数 */
  }

  if (argv[argc] == NULL) {                /* argv[argc] は NULL になる約束 */
    printf("argv[argc] は NULL です.\n");
  }
}

argc には、起動されたプログラムの名前も含めた、引数の個数がはいる。

argv[0] には起動されたプログラムの名前がはいる。 argv[1] から argv[argc-1] までには起動されたプログラムの名前のあとに打ち込まれたものがはいる。 argv[argc]NULL になる。 そういう約束である。

上のプログラムを「a file.txt "hello, world"」として起動すると、出力は次のとおり。

argc は 3 です.
argv[0] は a です.
argv[1] は file.txt です.
argv[2] は hello, world です.
argv[argc] は NULL です.

二重引用符で囲まれた "hello, world" が、 スペースを含むにもかかわらず一つの引数として渡ったこと、 二重引用符は渡されなかったことに注意せよ。

さらに、コマンドライン引数が「?」「*」を含んでいると、 それらは展開される。 くわしくは延べないが、実験してみよ。 (これは C 言語の仕様ではなく OS の機能である。)

コマンドライン引数は文字列としてプログラムに渡ってくる。 それを数値として int 型に収めたければ n = atoi(argv[1]) のように、 double 型に収めたければ x = atof(argv[2]) のようにする。 atoi(), atof() を使うには stdlib.h#include が必要である。

§11.2 ファイルの扱い(一文字ずつの読み書き)

プログラムの中でファイルの読み書きができる。 それには、次の三つの過程が必要である。

  1. ファイル名を指定して「開く」。すると FILE 型へのポインタ(「ファイルポインタ」)が返ってくる。
  2. そのファイルポインタを利用してファイルを読み書きする。
  3. 読み書きが終わったら、そのファイルポインタを指定してファイルを「閉じる」。

ファイルを開く際には「モード」の指定が必要である。 モードにはいろいろあるが、 ここでは最初に rt(テキストファイルとして読み出し用に)、 wt(テキストファイルとして書き込み用に)、の二つを覚えよう。

「テキストファイルとして」の意味は次の通り。 Windows では、改行は、十六進の 0d に続いて 0a, という、2 バイトで表されることになっている。 モード rt で開くと、ファイルの 0d 0a を一つの \n に変換して読み込む。 モード wt で開くと、一つの \n をファイルへは 0d 0a に変換して書き出す。 そういう仕組みである。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
  FILE *in, *out;
  int c;

  if (argc != 3) {                      /* コマンドライン引数の数が違うとき */
    printf("使い方が違います.\n");
    exit(1);
  }
  if ((in = fopen(argv[1], "rt")) == NULL) {        /* 第一引数で指定されたファイルを開く */
    printf("ファイル %s が開けません.\n", argv[1]);
    exit(1);
  }
  if ((out = fopen(argv[2], "wt")) == NULL) {       /* 第二引数で指定されたファイルを開く */
    printf("ファイル %s が開けません.\n", argv[2]);
    exit(1);
  }

  while ((c = fgetc(in)) != EOF) {      /* 読み出しては */
    fputc(c, out);                      /* 書き込む */
  }

  fclose(in);                           /* 開いたファイルを閉じる */
  fclose(out);                          /* 開いたファイルを閉じる */
}

注意!:このプログラムは、第一引数で指定されたファイルを第二引数で指定されたファイルのコピーする。 第二引数で指定されたファイルは、このプログラムの起動前に存在していればその内容が失われる。 よって、よく注意して実験すること。 また、このプログラムをキーボードから自分で打ち込んだ場合は、打ち込みの間違いにも注意が必要である。

main() にはいってすぐのところで、 FILE 型へのポインタ inout とを宣言している。 続いてコマンドライン引数の数のチェック。 正しくないときは、やや不親切だが、「使い方が違います」とだけ出力する。 exit(1) は、何かがうまくゆかなかったときにこうする、と思っておけばよい。 この関数 exit() を使うには stdlib.h#include が必要である。

第一引数で指定されたファイルを開く」とあるところ。 関数 fopen() の第一引数はファイル名、第二引数は上で述べた「モード」である。 その返り値は FILE 型へのポインタだが、深く考えず、 FILE * に代入する、と思っておけばよい。 ファイルを開くにあたっては、失敗することがある。 たとえば存在しないファイルから読み込もうとする場合がそうである。 失敗の場合は NULL が返るとの約束なので、inNULL とを比較している。

第二引数で指定されたファイルを開く」とあるところも同様。モードは異なっている。

読み出しては」「書き込む」とあるところ。 関数 fgetc()getchar() と似ているが、 引数として指定された FILE 型へのポインタから読み込む点が異なる。 (引数として指定されたファイルから読み込む、と思っておけばよい。) 関数 fputc()putchar() と似ているが、 第二引数として指定された FILE 型へのポインタに書き出す点が異なる。 (第二引数として指定されたファイルに書き出す、と思っておけばよい。) 書き出すにあたってはディスクがいっぱいであるなどのエラーが起こりえるが、 そのチェックは省略した。

開いたファイルを閉じる」とあるところ。 関数 fclose() でファイルを閉じる。 このときにもエラーが起きる可能性はあるが、 チェックは省略した。 また、プログラムが終了すれば開いたファイルはすべて閉じられるので、 略してもよい。

§11.3 ファイルの扱い(一行ずつの読み書き)

#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 128

int main(int argc, char *argv[]) {
  FILE *in, *out;
  char line[MAXLINE];

  if (argc != 3) {                      /* コマンドライン引数の数が違うとき */
    printf("使い方が違います.\n");
    exit(1);
  }
  if ((in = fopen(argv[1], "rt")) == NULL) {        /* 第一引数で指定されたファイルを開く */
    printf("ファイル %s が開けません.\n", argv[1]);
    exit(1);
  }
  if ((out = fopen(argv[2], "wt")) == NULL) {       /* 第二引数で指定されたファイルを開く */
    printf("ファイル %s が開けません.\n", argv[2]);
    exit(1);
  }

  while (fgets(line, MAXLINE, in) != NULL) {    /* 読み出しては */
    fputs(line, out);                           /* 書き込む */
  }

  fclose(in);                           /* 開いたファイルを閉じる */
  fclose(out);                          /* 開いたファイルを閉じる */
}

関数 fgets() はファイルから一行を読み込む。 第一引数はどこへ読み込むか、 第二引数は読み込む文字数の最大、 第三引数はどのファイルから読み込むか、である。 (第二引数を n とした場合、最大で n-1 文字が読み込まれる。最後には \0 が付加される。)

関数 fputs() のファイルに一行を書き込む。 第一引数は何を書き込むか、 第二引数はどのファイルに書き出すか、である。 書き出すにあたってはディスクがいっぱいであるなどのエラーが起こりえるが、 そのチェックは省略した。

§11.4 課題4

a file1.txt file2.txt」と実行すると、 file1.txtfile2.txt にコピーするが、 小文字を大文字に変換しつつコピーするプログラムを書け。 (いわゆる)日本語が“化ける”のは気にしなくてよい。

レポートの本文冒頭に、氏名を、なるべく大学に届けるある通りの文字で書け。 次に、プログラムソースファイルを、添付ファイルではなく、本文に貼りつけよ。

件名は「??? kadai4」(←全て半角文字、小文字、 ??? は自分の履修者番号、その後ろに半角スペース一つ、 kadai4 の間にはスペースを入れない)とせよ。

§ おまけ

§9.5 のプログラムを改変し、 それぞれの入力行を、その行の中で逆順に出力するプログラム rev.exe を書け。

This is a pen.
This is an apple.
^Z
.nep a si sihT
.elppa na si sihT

のようになればよい。 「rev | sort | rev」とすると、何をしたことになるか?


岩瀬順一