第 10 回 - 文字列操作(1)

[5/30, 2006 H.Aman]
67x27(1669bytes)   67x27(1669bytes) 【課題 3】

例題 3

fgets 関数を使って文字列を 1 行まとめて読み込み, その長さを出力するプログラムを作りなさい. ただし,読み込む文字列の長さは 64 未満とする.

コーディング例

     1	/*
     2	 * プログラミング演習 第 10 回
     3	 * [例題 3]
     4	 * (C) 2006 Hirohisa AMAN <aman@cs.ehime-u.ac.jp, aman@computer.org>
     5	 */
     6	#include <stdio.h>
     7	#include <string.h>
     8	
     9	int main(void){
    10	  char str[64];
    11	  int n;
    12	
    13	  fgets(str, 64, stdin);
    14	
    15	  n = 0;
    16	  while ( str[n] != '\0' ){
    17	    n++;
    18	  }
    19	
    20	  printf("入力文字列: %s\n", str);
    21	  printf("長さ = %d\n", n);
    22	
    23	  return 0;
    24	}
     
※左端の数字は行番号であり,ソースコードには含まれない点に注意!

コンパイル & 実行例

     $ gcc example10_3.c [Enter]
     $ ./a.out [Enter]
     aman@computer.org [Enter]
     入力文字列: aman@computer.org

     長さ = 18  ← 改行文字まで数えているため 18 となる
     

解説

fgets は 1 行(改行まで)の文字列をまとめて読み込んでくれる便利な関数であり, C プログラムでの使用頻度は高い. なお,これを使用する場合はヘッダファイルの読み込み (#include <string.h>)が必要である.

fgets を呼び出す際には 3 つの引数を指定する. 13 行目のコードの場合,

    13	  fgets(str, 64, stdin);
  • 1 つ目は文字列を格納するための配列(str)
  • 2 つ目は配列の大きさ(64)
  • 3 つ目は読み込み元となるファイル(stdin)
という構成になっている.
最後の stdin というのは,標準出力(たいていの場合はキーボード) に対応している. もし,別のファイル foo.txt から読み込みたい場合は
     FILE* fp = fopen("foo.txt", "r");
     fgets(str, 64, fp);
というかたちで使用する.

fgets は

  • 改行文字まで(改行文字も含む)を自動的に配列へコピー
  • 末尾へ '\0' を格納
という作業をまとめてにやってくれる.

なお,文字列の長さは,配列の何番目に '\0' が登場するのかを調べればよい.

	
    15	  n = 0;
    16	  while ( str[n] != '\0' ){
    17	    n++;
    18	  }
    
実は文字列の長さは strlen という関数で調べることができる. 使い方は
     int n = strlen(str);
というかたちであり,引数で指定された文字列の長さが戻り値として得られる. この関数を使用する場合はヘッダファイルの読み込み(#include <string.h>)が必要である.

参考:なぜ scanf を使わないのか?

データの入力といえば scanf という発想があるだろう. たしかに scanf はあらゆるデータ型に対応できる強力な入力用関数である. しかしながら,文字列の読み込みは苦手としている.

例として

     char str[64];

     scanf("%s", str);

     printf("%s\n", str);
というコードについて考えてみる.

一見すると scanf で文字列を読み込み,printf で出力しているだけに見える.
ここで実行すると次のようになる:

     $ ./a.out [Enter]
     aman hirohisa [Enter]
     aman
     
入力文字列の一部しか表示されていない.

実は途中に空白文字があると,scanf は勝手にそこを文字列の終りとして解釈してしまう. つまり,空白文字を含んだ文字列の読み込みには使えないという問題がある.

これに加えてもう 1 つ問題がある. それは, 文字列を格納する先の配列の大きさを考慮していないということである.
この場合 64 文字以上の文字列を格納することはできないのだが, 仮に 64 文字以上が入力されたとしても scanf は全てをその配列に格納しようとする.
それゆえアクセスが許されていないメモリ領域にまで手を出そうとし, セグメンテーション違反を起こしてしまう. このことはバッファオーバーフローというセキュリテイ・ホール (セキュリティ上の弱点.外部からの攻撃の糸口になる.) を作り出してしまう恐れがあり, このようなプログラムは作らないようにしなければならない.