ファイヤープロジェクト
標準入出力ライブラリでのファイル操作
2003-07-20T15:13+09:00   matsu
標準入出力ライブラリを使用するとファイル操作がしやすく,バッファリングなども効くので便利である.
ここでは,標準入出力ライブラリを使用したファイル操作について書く.先のシステムコールを使用したファイル操作では以下のような不満があるかもしれない.
  • バッファリングがない
  • 書式付き出力などの機能がない
ないなら自分で実装しなければならないが,標準入出力ライブラリを使うと嬉しい.このライブラリを使用するにはstdio.hをincludeする.
標準入出力でもシステムコールと同様,openしてreadやwriteをおこないcloseするという流れで処理する.ただし,システムコールにおけるファイルディスクリプタに相当するものは,ストリームというものに置き換わる.具体的には,openするとFileという構造体へのポインタが返って来るのでそれにたいしていろいろする.また,標準ファイルディスクリプタに対応するストリームはプログラム実行時に時動的にオープンされる.だからいきなり(openせずに)printfとかできたりする.標準ファイルディスクリプタとそれらに対応するストリームを以下に示す.
ファイルディスクリプタストリーム名
標準入力0stdin
標準出力1stdout
標準エラー出力2stderr
これらのストリームはstdio.hで定義されている.以下/usr/include/stdio.hから引用.
/* Standard streams.  */
extern FILE *stdin;             /* Standard input stream.  */
extern FILE *stdout;            /* Standard output stream.  */
extern FILE *stderr;            /* Standard error output stream.  */
システムコール編では関数の仕様を書いたけど,それって全部manに書いてあるので(というかmanを見ながら書いた)私個人にとっても意味がない.ので,今回から試用してみる(ほんとはフリーのソースとかを読んだりしたらカッチョイイんだけど,私には無理.でもいつかは....).
#include <stdlib.h>
#include <stdio.h>

int finalize();
int init_name();
int init_value();

char *name;
int *value;
FILE *file;
size_t result;

int main()
{
  init_name();
  /* nameにaとbを代入して出力 */
  *name='a';
  *(name+1)='b';
  /* 標準出力に書式付き出力 */
  fprintf(stdout,"%c\n",*name);
  fprintf(stdout,"%c\n",*(name+1));

  init_value();
  /* valueに1と1000を代入して出力 */
  *value=1;
  *(value+1)=1000;
  fprintf(stdout,"%d\n",*value);
  fprintf(stdout,"%d\n",*(value+1));

  /* hogeファイルへのストリームを書き込みモードでオープン */
  file=fopen("hoge","w");
  if(file==NULL){
    /* 標準エラー出力に出力 */
    fprintf(stderr,"File open failed (write only mode)");
    finalize();
    exit(-1);
  }

  /* ストリームへのポインタfileへnameにあるサイズcharのデータを二つ書き込む */
  if(fwrite(name,sizeof(char),2,file)<0){
    fprintf(stderr,"File write failed");
    finalize();
    exit(-1);
  }
  fprintf(stdout,"File write successed(name)\n");

  /* ストリームへのポインタfileへvalueにあるサイズintのデータを二つ書き込む */
  if(fwrite(value,sizeof(int),2,file)<0){
    fprintf(stderr,"File write failed");
    finalize();
    exit(-1);
  }
  fprintf(stdout,"File write successed(value)\n");

  /* 一旦忘れ去る */
  finalize();

  /* hogeファイルへのストリームを読み込みモードでオープン */
  file=fopen("hoge","r");
  if(file==NULL){
    fprintf(stderr,"File open failed (read only mode)");
    finalize();
    exit(-1);
  }

  /* ストリームfileからサイズcharのデータを2つ読み込む */
  if(fread(name,sizeof(char),2,file)<0){
    fprintf(stderr,"Read char datas form file failed");
    finalize();
    exit(-1);
  }
  fprintf(stdout,"Readed %c\n",*name);
  fprintf(stdout,"Readed %c\n",*(name+1));

  /* ストリームfileからサイズintのデータを2つ読み込む */
  if(fread(value,sizeof(int),2,file)<0){
    fprintf(stderr,"Read int datas from file failed");
    finalize();
    exit(-1);
  }
  fprintf(stdout,"Readed %d\n",*value);
  fprintf(stdout,"Readed %d\n",*(value+1));

  finalize();

  exit(0);
}

int finalize()
{
  fprintf(stdout,"Free name....");
  free(name);
  fprintf(stdout,"Done\n");
  fprintf(stdout,"Free value....");
  /* ストリームstdoutをフラッシュする */
  fflush(stdout);
  free(value);
  fprintf(stdout,"Done\n");
  /* ストリームfileを閉じる */
  fprintf(stdout,"Close file....");
  if(fclose(file)!=0){
    fprintf(stderr,"File close failed");
    exit(-1);
  }
  fprintf(stdout,"Done\n");
  return 1;
}

int init_name(){
  /* nameにchar二個分のメモリを割り当てる */
  name=malloc(2*sizeof(char));
  if(name==NULL){
    fprintf(stderr,"Malloc for name failed");
    finalize();
    exit(-1);
  }
  return 1;
}

int init_value(){
  /* valueにint二個分のメモリを割り当てる */
  value=malloc(2*sizeof(int));
  if(value==NULL){
    fprintf(stderr,"Malloc for value failed");
    finalize();
    exit(-1);
  }
  return 1;

}
こいつをコンパイルして実行すると,以下が出力される.
a
b
1
1000
File write successed(name)
File write successed(value)
Free name....Done
Free value....Done
Close file....Done
Readed a
Readed b
Readed 1
Readed 1000
Free name....Done
Free value....セグメンテーション違反です
うん,ファイルの入出力はうまくできているようだ.が,二度目のfree(value)に失敗する.なんでだろー.
どうでもいいけど,なんだかとてつもなく恥ずかしいコードのような気がする(汗).
matsu(C)
Since 2002
Mail to matsu