エラー処理について

[戻る]
桂華 2002/06/07(金) 18:42:02
こんばんは。いつもお世話になっています。

ファイルの読み書きエラーをヘルプを見ながら追加したのですが、
エラーが出ていないのにエラーが出力されてしまったりするのです。
未熟者で、コードのどこがおかしいのか分かりません...。
もしよろしければご教授ください。お願いします。

//ファイル読み込み
for(iCount=0; iCount<=11; iCount++){
  fgets(cData, 64, fpk);
  //エラー処理
  if((fgets(cData, 64, fpk))==NULL){
#ifdef DEBUG
   printf("csvsep:ファイル読み込みエラー\n");
#endif
    exit(1);
  }
}

//ファイルに書き込み
for(iCount=0; iCount<ilen; iCount++){
  fputc(mChgData[iCount],fpchgptn);
  //書き込みエラー
  if((fputc(mChgData[iCount],fpchgptn))==EOF){
#ifdef DEBUG
    printf("csvsep:ファイル書き込みエラー\n");
#endif
    exit(1);
  }
}

PAI 2002/06/07(金) 19:11:25
if文の評価で読み書きが行われちゃいますけど、意図どおりですか?

int ret;
for( ; ; ){

    ret = fputc(mChgData[iCount],fpchgptn);

    if( ret == EOF ){
        // エラー
    }
}

では?

桂華 2002/06/07(金) 19:42:58
PAI様、早速のアドバイスありがとうございました。

はい、if評価の読み書きで意図とおりです。
ですが、PAI様のアドバイスであるコードと、元のコードの違いは
何でしょうか???    
大変失礼な質問で申し訳ありません...。

また、下記のコードはData配列はData[12][24]と宣言しており、
12行のファイルを読み込んでいるのですが、もしそのファイルが
12行以外の場合にエラーを出したいのですが、書き方が分からず
困っています...。
追加質問になってしまい、大変申し訳ないのですがアドバイスを
お願いできませんでしょうか??

エラー文の書き方が分かっていないようです。
勉強しなおしたいと思います。

//ファイル読み込み
for(iCount=0; iCount<=11; iCount++){
  fgets(cData, 64, fp);
  //配列に格納
  sprintf(tmpData[iCount], "%s", cData);
}    

//エラー処理
if((feof(fp))==0){
#ifdef DEBUG
  printf("csvsep:異常エラー\n");
#endif
  exit(1);
  //ファイルクローズ
  fclose(fp);
}

Bun 2002/06/07(金) 20:03:01
>はい、if評価の読み書きで意図とおりです。
>ですが、PAI様のアドバイスであるコードと、元のコードの違いは
>何でしょうか???    

ヨコからスンズレイすますま。

PAIさんは

>//ファイルに書き込み
>for(iCount=0; iCount<ilen; iCount++){
>  fputc(mChgData[iCount],fpchgptn);              // ここでも書き込んでいて
>  //書き込みエラー
>  if((fputc(mChgData[iCount],fpchgptn))==EOF){   // 更にここでも書き込んでしま
いますよ!いいのですか?

といいたいのでしょう。
これが意図したとおりですか?(^^;

PAI 2002/06/07(金) 20:34:29
ちょっと確認の言葉がまずかったですね、ごめんなさい。


で、エラー処理に関してですが、
12行であることを判別することが難しいのでしょうか?
ファイルを閉じる等の処理が煩雑になるのが難しいのでしょうか?

前者であればこの場合。

    int i;
    for( i=0 ; i<12 ; i++){
    
        if( fgets(/*・・・*/) == EOF )
            break;// エラー、ループから抜ける
        
        // 正常、データのコピー等
        // ・・・・・
    }
    
    if( i != 12 ){
        // エラー、12行でない。
    }

とするのがスマートですかね?まあいろんな考え方があるんで、
これが一番だ、とは思いませんけど、

あれ?ぴったりファイルの終端まで読んだときって、
もしかしてEOF返ってきましたっけ?

クラフトマン 2002/06/08(土) 00:42:30
>あれ?ぴったりファイルの終端まで読んだときって、
>もしかしてEOF返ってきましたっけ?

fgets関数の戻り値は、読み込んだ文字列へのポインタです。
1文字も読めなかった場合に、NULLが返ってきます。(EOFは返ってきません)
1文字も読めなかった場合とは、既に終端(EOF)に達している状態で、fgetsを呼
び出したか、エラーが発生したかです。
ですから、読み込みの終了をfgets関数の戻り値をNULLかどうかで判定するのは、
間違いではありませんが、NULLであった場合にエラーと判断するのは間違いです。
ferror()または、feof()で判定する必要があります。

因みに、fgets()とfeof()の戻り値の関係は、下記のようになります。

1)残り15文字で、最大10文字読もうとした。
    fgets() --- 読み込めたので、読み込んだ文字列へのポインタが返る。
    feof() ---- 終端に達していないので、0が返る。

2)残り5文字で、最大10文字読もうとした。
    fgets() --- 読み込めたので、読み込んだ文字列へのポインタが返る。
    feof() ---- 終端に達したので、0以外の値が返る。

3)残り0文字で、最大10文字読もうとした。
    fgets() --- 読み込めないので、NULLポインタが返る。
                (エラーではないし、この呼び出しは何の問題もない)
    feof() ---- 終端に達しているので、0以外の値が返る。

PAI 2002/06/09(日) 15:55:38
>fgets関数の戻り値は、読み込んだ文字列へのポインタです。

ほんとだ、何と勘違いしたんだろ。恥ずかしい。
フォローどうもです。

桂華 2002/06/10(月) 09:58:45
おはようございます。
レスが大変遅くなって申し訳ありません。

たくさんのアドバイスありがとうございました。
根本的にエラー処理が間違っていたのですね....。

そして、皆様のアドバイスを参考にして以下のコードで
エラー処理を行う事にしました。
もし、ご指摘がありましたらどんどんつっこんでください。

ところで、未だにファイル読み込みの場合のエラー処理の
書き方がよく分かりません。
12行読み込みの場合と同じにすればよいのでしょうか?
まだコードの理解が完全にできていなくて質問するのも
おこがましいのですが、どうかよろしくお願いします。

書き込みのエラー処理

FILE fp;
int iCount,iput;
//ファイルに書き込み
for(iCount=0; iCount<ilen; iCount++){
  iput=fputc(mData[iCount],fp);
  //書き込みエラー
  if(iput==EOF){
#ifdef DEBUG
    printf("ファイル書き込みエラー\n");
#endif
    exit(1);
    fclose(fp);
  }
}

12行読み込みのエラー処理

FILE fp;
int iCount;
//ファイル読み込み
for(iCount=0; iCount<=11; iCount++){
  fgets(cData, 64, fp);
  //配列に格納
  sprintf(tmpData[iCount], "%s", cData);
}    

   //エラー処理
   if((feof(fp))!=0){
#ifdef DEBUG
      printf("読み込み異常エラー\n");
#endif
      exit(1);
      //ファイルクローズ
      fclose(fp);
    }

クラフトマン 2002/06/10(月) 14:02:48
実際にファイルに存在する行分を読むのではなく、必ず固定で、11行
読み出したいんですね?

であれば、理由にかかわらず、fgets関数がNULLを返してきたらまずい
わけだから、

for(iCount=0; iCount<=11; iCount++){
    if (fgets(cData, 64, fp) == NULL) {
        if (feof()) {
            /* 行数が足りなかった場合のエラー */
        }
        else {
            /* 読み込みエラー */
        }
    }
    //配列に格納
    sprintf(tmpData[iCount], "%s", cData);
}

とすべきでは?

さらに言えば、
 1) 1行の文字数が64文字を超えていた場合、fgets関数は64文字ずつしか
    読まないので、2行にわけて読むことになるがよいのか。
 2) 実際の行数が11行より多い場合は、正常終了するがよいのか。
 3) 11,64などのハードコーディングは避けるべきでは?

また、掲載されたコードでは、書き込み側で、エラー発生時にしかfclose
していませんが、fcloseはきちんとしなければなりません。
それと、書き込みデータは、バッファリングされているはずなので、fclose
した時点(厳密には、fclose内でバッファをフラッシュしようとした時点)で
エラーとなる可能性があります。この点も注意しなければなりません。


>ほんとだ、何と勘違いしたんだろ。恥ずかしい。
>フォローどうもです。

過去の発言から、PAIさんが実力者であることは推測していますし、誰でも
勘違いはありますからね。そういった場合は、気付いたものが指摘すれば
いいというのが私の考えです。
後になって言葉足らずの部分があって、気分を害されちゃうかなと心配しま
したが、好意的に受け取ってくれてありがとう。

桂華 2002/06/10(月) 19:28:11
クラフトマン様、ご指摘ありがとうございました。
詳しい解説も添えていただき、大変勉強になりました。
また、他にも至らない点が多くあったようで。
きちんとエラー処理も書けない未熟者でお恥ずかしいです...。

ところで、while文の読み込みのエラー処理を書いたのですが
←の部分で「'==' : 間接参照のレベルが 'int ' と 'void *' で異なっています。」
とエラーが出てきます。
根本的に間違っているような気がするのですが...。
つたないデバッグをしてみたのですが、やはり書き方の問題だと思います。
エラー処理は以外と難しいですね...。

本当に何回も質問して申し訳ないのですが、アドバイスをいただけませんでしょうか?


char cget;

//c_systpndataファイル読み込み
while (cget=fgets(cData, 128, fp) != NULL)
{
    if(cget==NULL)←ここでエラーが出ます。
    {
        // 読み込みエラー
#ifdef DEBUG
        printf("読み込みエラー\n");
#endif
        exit(1);
       //ファイルクローズ
       fclose(fp);
    }
・
・
・

PAI 2002/06/10(月) 21:52:12
fgetsの戻り値はchar*だから、char*で受けなきゃいけませんよね。


で、そうすると、どうしてコレ

char cget;
while (cget=fgets(cData, 128, fp) != NULL);

でエラーがでないか、という話になりますが、
これはコンパイラには

char cget;
while (  cget=( fgets(cData, 128, fp) != NULL )  );

こう見えてしまうからです。

・・・なんか微妙ですが、一応意図どおりに
NULLだったら 0
NULLじゃなかったら 1
がはいってくれますね。

桂華 2002/06/11(火) 09:23:02
PAI様ご指摘ありがとうございます。
そうですね、fgetの戻り値はchar*型でした。
うっかりしておりました。

結局、読み込みエラーの処理は以下のようにしました。
PAI様が書かれた通り、なんだか微妙です...。^^;

デバッグしてcgetの値を見てみると「1'・'」となっていたのが
気になる所ですが。
う〜ん、もうちょっとスマートなやり方を考えてみます。
もしよろしければまた何かアドバイスをしてやってください。

char cget;

//c_systpndataファイル読み込み
while (cget=fgets(cData, 128, fp) != NULL)
{
    if(cget==0)
    {
        // 読み込みエラー
#ifdef DEBUG
        printf("読み込みエラー\n");
#endif
        exit(1);
       //ファイルクローズ
       fclose(fp);
    }

YuO [E-Mail] 2002/06/11(火) 09:52:51
> while (cget=fgets(cData, 128, fp) != NULL)
> {
>     if(cget==0)

whileループが実行されるのは,cgetが非0の時です。
#今回は常に0か1。ループ実行時,cgetは1のみ。
なので,このif文は常に偽となります。

whileから抜けたところでferrorやfeofを使ってエラーかEOFなのかを判別するとよいかと。
#cget変数は不要になる。


あと,
>        exit(1);
>        //ファイルクローズ
>        fclose(fp);
exitの後の文は実行されませんが……。
fcloseはexitの前に実行するか,exitによるクローズに任せればよいです。

古いスレッドには追加発言できません。