ファイヤープロジェクト
書式付き入出力とストリームエラー
2003-07-20T15:13+09:00   matsu
printf族とscanf族による書式付き入出力と,入出力時のエラーについて.
処理付き入出力はおなじみ(?)のprintf族とscanf族である.
#include <stdio.h>
#include <stdlib.h>

int main()
{
  int i;
  char *str="hoge";
  char *str2;
  int strnum;
  char ch='X';
  int a=17;
  double b=2.5;

  /* printf族は%で始まる変換指定子で書式を設定して出力する.*/
  /* %cは文字を出力する */
  printf("%%cでの出力\t%c\n",ch);

  /* %sは文字列を出力する */
  printf("%%sでの出力\t%s\n",str);

  /* %dは整数を10進数で出力する*/
  printf("%%dでの出力\t%d\n",a);

  /* %fは浮動小数点で出力する */
  printf("%%fでの出力\t%f\n",b);

  /* %gはdouble型の値を普通の形式で出力 */
  printf("%%gでの出力\t%g\n",b);

  /* %eは倍精度浮動小数点で出力する */
  printf("%%eでの出力\t%e\n",b);

  /* %oは8進数,%xは16進数で出力する.*/
  fprintf(stdout,"%%oでの出力\t%o\t\n%%xでの出力\t%x\n",a,a);

  /* sprintfは出力先がchar*なprintf */
  str2=malloc(20*sizeof(char));
  strnum=sprintf(str2,"sprintfしてみた.%d%s\n",a,str);
  for(i=0;i<strnum;i++){
    fprintf(stdout,"%c",*(str2+i));
  }

  printf("\n1234567890\t文字数カウント用\n");
  /* 変換指定子の%の直後に数値でフィールド指定子をいれることができる.*/
  /* 10文字分のスペースに右詰めで出力 */
  printf("%10d\n",a);
  /* 10文字分のスペースに左詰めで出力 */  
  printf("%-10d\n",a);
 /* 10文字分のスペースに右詰めで出力.空きスペースは0で埋める */
  printf("%010d\n",a);
  /* 10文字分のスペースに右詰めで出力. 
     "."の次の数値は小数点第何位まで出力するかを指定 */
  printf("%10.5f\n",b);
  /* 10文字分のスペースに右詰めで出力. 
     "."の次の数値は小数点第何位まで出力するかを指定.
     空きスペースは0で埋める */
  printf("%010.5f\n",b);
  /* *で可変長フィールド幅を指定できる. */
  for(i=1;i<=10;i++){
    printf("%*s\n",i,str);
  }
  printf("1234567890\t文字数カウント用\n");

  /* 入力を読み込んでaにいれる.
     ただし,入力は10で始まる整数でなければならない.
     そうでなければ失敗する. */
  scanf("10%d",&a);
  fprintf(stdout,"%d\n",a);

  str=malloc(10*sizeof(char));
  /* 入力をよみこんでstrにいれる.
     ただし入力のうち4文字目まだしか読みとってもらえない.
     5文字目以降は無視される. */
  scanf("%4s",str);
  fprintf(stdout,"%s\n",str);

  exit(0);
}
これを実行すると以下が出力される.
%cでの出力      X
%sでの出力      hoge
%dでの出力      17
%fでの出力      2.500000
%gでの出力      2.5
%eでの出力      2.500000e+00
%oでの出力      21      
%xでの出力      11
sprintfしてみた.17hoge

1234567890      文字数カウント用
        17
17        
0000000017
   2.50000
0002.50000
hoge
hoge
hoge
hoge
 hoge
  hoge
   hoge
    hoge
     hoge
      hoge
1234567890      文字数カウント用
10555
555
fuga
fuga
.....なんだかかなりやっつけな感じのコードになってしまった.
ストリームエラーというかエラー処理の話.多く処理はエラー処理を必要とする.エラー処理をするためにはエラー情報が必要だ.そのためにはerrnoを調べる.errnoは皆の人気者で皆がちょっかいをだすので,早いものがちだ.そして素早くerrnoを何かにコピーして自分だけ(誰?)のerrnoを手にいれる.そんな感じだ.例えばprintfで直接errnoを出力しようとしても,そのprintfがerrnoを書き換えるなんてことがある(かも).具体的には以下のようになるだろうか.
#include <stdio.h>
#include <errno.h>

extern int errno;

int main()
{
  FILE *file=fopen("/var/log/syslog","rw");
  int errcp;
  /* fopenに失敗すると,NULLが返され,errnoがセットされる. */
  if(file==NULL){
    /* すぐにerrnoをコピー. */
    errcp=errno;

    /* perrorを使用すると,
       引数で指定された文字列を追加してエラーメッセージをstderrに出力してくれる.
エラーメッセージはsys_errlist[errno]に入っているものが出力される.
    */
    perror("error1")
    fprintf(stderr,"error2:\t%s\n",sys_errlist[errcp]);
    exit(-1);

/***************************************************************************/
    /* 当然ながら,sys_errlistにないエラーメッセージを出したいなら,
       自分で実装する必要がある.
       例えば以下のように */
    /* 
       errnoのコピーerrcpをチェックしてみる.
       errcpがもしEACCES(権限がない)ならstderrにPermission errorと出力 */
    /*
    if(errcp==EACCES){
      fprintf(stderr,"Permission error\n");
      exit(-1);
    }else{
      fprintf(stderr,"Error\n");
    }
    */
/***************************************************************************/

  }
  /* エラーが出なかったら */
  fprintf(stdout,"Done.\n");
  exit(0);
}
上の例のなかで,自分でerrnoをチェックするパターンはerrnoの値の意味を調べたり,errnoのチェックを書いたりする必要があるので面倒くさい.そういう意味でperrorを使用するとかなり嬉しい.あと,エラー文字列を取得してエラーログを取るには
#include <string.h>
strerror(int errnum);
やsys_errlist[errno]を使用する.どちらも引数,またはインデクスにエラー番号をいれるとエラーメッセージを取得できる.あとはファイルに書くなりする.
matsu(C)
Since 2002
Mail to matsu