とっておきの「まかない飯」を教えて下さい!

前回「EOF判定されない」で回答、アドバイスしていただいた
a-kumaさん、Haizyさん、inthefloiさん、anisolさん、leaz024さん、cherry-moonさん
本当にありがとうございました。頂いたアドバイスを試行してみましたが、なぜか、ダメでした。(T-T)
環境が悪いのかもしれませんね。

あれから、feof関数を使わずにファイルの終わりを算出して判定するなどの方法を試みましたが、EOF判定だけのために妙に複雑になってしまい、自分でも納得がいかなかったので、再度こちらで皆さんの意見をもらおうと投稿させてもらいました。

今回は質問の仕方を変えて、モジュールの仕様を挙げますので、それから「自分ならこうする」というようなお答えを頂けたらと思っています。
feof関数を使用しても、その他の方法でもなんでもアリです。(^^)

「ファイル一行入力モジュール」(仕様)
1.このモジュールは実行されると「inputfile.txt」から一行だけ(改行まで)読み込み、終了します。次に実行された時は、その次の行を読み込みます。
2.ファイルの終端に達したら、リターンコードに'4'を設定し、終了する。
以上これだけ。(^^;

「inputfile.txt」(仕様)
インプットファイルは、テキストファイルで、一行30バイトの文字列を格納しています。ファイルの総バイト数は不定です。
文字コードは「Shift-JIS」「改行=CR+LF」、ファイルの最後にEOF制御文字を設定しています。

「インプットファイルの内容」

 1行目 AAAAAAAAAABBBBBBBBBBCCCCCCCCC[改行文字]
 2行目 DDDDDDDDDDEEEEEEEEEEFFFFFFFFF[改行文字]
          ・
          ・
          ・
n-1行目 XXXXXXXXXXYYYYYYYYYYZZZZZZZZZ[改行文字]
 n行目 [EOF]

「ここはこうしたらいいんじゃないかな?」的な事でかまいませんので、是非みなさんの意見を聞かせてください。

A 回答 (8件)

問題がでそうなところと言うと,[EOF](^Z)を追加しているところと、ファイルのオープン方法でしょうか。




まず、WindowsとかMS-DOS環境の場合は,
fopenを使い,テキストモードにしておけば問題無いように思います。

f=fopen("inputfile.txt","r");
while( fgets(buff,sizeof(buff),f) );
if ( feof(f) )printf("EOF\n");

みたいにするとこちらではEOF検出してます。

a-kumaさんのが仕様に忠実にできていて正しく動きそうですね。
わがままな私はこの仕様に不満があるので書きませんが(笑)


ところでテキストモードですが、元々unixとのライブラリの整合性をとるためのエミュレーションモードみたいなものです。
これは、DOSやWindowsがunixと改行コードが違ったり,EOFコードがあったりしても
同じプログラムで動作するようにしたものですから、
unixはそういう変換は不要でそもそも区別する必要がなく、モードが存在しないのです。

改行等がからむ物はテキストモードにするのが簡単で間違いないですし、そのままunixでも動作させられます。
バイナリモードでオープンしてfgetsで読むとどうなるか等調べないとわからないような物は面倒です(^^;;


また、EOFコードは確かCP/Mかなんかの名残であるもので、本来は不要なコードです。
必要だったのは,ファイルシステムにファイルサイズが存在しないためです。(クラスタ単位でしかわからない)
    • good
    • 0

補足ありがとうございます。

素人ですが、もうちょっと調べてみました。私の前の回答は的外れだったみたいです……。

feof()と[EOF]の組み合わせは使わないほうがいいかもしれません。というのは、feof()で[EOF]を認識させることはバイナリモードではできないようです。またUNIXではfopen()はバイナリモードしかないようで、機種依存的になります。[EOF]のコードを使わず、単純にファイルの終わりをfeof()で調べるのはだめですか?

とりあえず、今まで調べた結果なので間違っているまたは的外れだったらごめんなさい。
    • good
    • 0

私も前回の回答を見たらトホホでした。



29文字+CR+LFの31バイトに確実に固定されていて、ウィンドウズ系以外のOSであるならば、fgetsを使わずに、

char buff[31];
if (fread(buff, 1, 31, fp) < 31)
{
 if (feof(fp))
 {
  rtn_code[0] = '4';
 }
 else
 {
  rtn_code[0] = '8';
 }
}
else
{
 buff[29] = '\n';
 buff[30] = '\0';
}

というような、やり方もあるかなと思います。
    • good
    • 0

こんにちは。



前回は、とろけていてゴメンナサイ。

さて、前回の最後の投稿で、触れたかもしれませんが、「改行=CR+LF」ということなので、これ自身が ”2バイト” 食っていると思います。
ですので、fgetsの第二引数(何文字読み込むか)を第一引数の限界も考慮しつつ、大きくする事が有効ではないかと思います。
【#3の例】
>char buf[1024]
完璧です。
>fgets(buf, sizeof(buf), in) ・・・・
sizeof(buf) が、文句なしの完璧です。さすがッス。こんな感じです。

同時に16進で、監視式によりbufの値を監視してみてください。
動きが変だと、ココでわかると思います。

でわ、失礼します
    • good
    • 0

前回の質問、解決したのか気になって、夜も寝られませんでした(嘘)。

早めに補足を行ってくださいね……。

ところで、
・[EOF]はどのような方法で書きこんでいるのでしょうか。またそのコードは何ですか。
・Windowsベースでのプログラムですか、またはUNIXベースですか。
・単純に[EOF]だけ書きこんだファイルで、fgets(buff, 2, fp) およびfeof(fp)を実行したとき、EOF判定はされますか。(この点は重要かも)

以上お手数ですが補足していただけますか。ちょっと情報が少なすぎますので。

この回答への補足

毎度毎度説明不足でスイマセン(--:

・EOF制御文字は、秀丸エディタのオプションで「保存する時に、EOF制御文字を付加する。」にチェックを入れることでファイルに保存されています。
バイナリエディタで見ると'1A'となっています。
・Windowsベースのプログラムです。
次に、
EOFだけを書き込んだファイルですが、秀丸では文字が記述されていないと保存できずに削除しようとするので、他のバイナリエディタを使用し1Aとだけ書いて保存しました。

それから説明不足がもう二つあります。(ホントすいません)

1.fgetsの第二引数にunsigned long型のlengthという箱を使ってます。
で、このlengthにrecord_lngsをstrtolでlong型に変換して代入しています。

unsigned long length;
char record_lngs[5]; ←これには00020という文字列が格納されています。
char *endptr;
char buf[32756];

length = strtol(record_lngs, &endptr, 10);

if (fgets(buf, length, in) == NULL) {
   if (feof(in)) {
     return 4;
   }
}
こんなかんじで実行しましたが、4は返ってきませんでした。
lengthに入ってるの20じゃダメじゃん!?と、お思いでしょうがなぜかこれでgetは正常に行われます。lengthに1や2を足してgetすると、何も読みこまれなくなります。これも謎ですね。

2.inputfile.txtは1レコード(一行)の大きさが32756バイトです。今回その1レコード(一行)に入っているデータは固定長の20バイトですが、他にも5000や,32756バイト入っている場合もあります。
こんな粗悪な説明でわかりますでしょうか?また何か説明が必要でしたら書き込んでください。(うう、もっと文章力つけとけばよかった)

補足日時:2001/10/31 14:07
    • good
    • 0

ん~、何がうまくいかないのか、よく分からんです。



と、いうわけで、提示された仕様に忠実にデータを抱えてしまっていますし、
エラーチェックもしてませんが、こんな感じの関数で良いんじゃないですか?

#include <stdio.h>

int xxx()
{
  char buf[1024];
  static FILE* in = NULL;
  if (in == NULL) {
    in = fopen("inputfile.txt", "r");
  }
  if (fgets(buf, sizeof(buf), in) == NULL) {
    if (feof(in)) {
      return 4;
    }
  }
  return 0;
}

/* テストコード */
int main()
{
  int ret;
  int i;
  for (i = 0 ; (ret = xxx()) == 0 ; ++i) { printf("%d\n", i); }
  printf("ret = %d\n", ret);
  return 0;
}
    • good
    • 0

訂正!回数間違えました!!



従ってfgetsは4回行われますが、3回目で1文字以上を格納し、かつ、EOFを検出しています。つまりfgetsはNULLを返していません。このときにfeofを実行すれば「EOF検出」となるのですが、4回目を実行したためにfeofは「非EOF検出」となっています。

以上です(汗)
    • good
    • 0

前回の質問を今さら見ました。



まず、fgetsの使い方が間違ってます。第2引数は読みとる文字数+1なんです。つまり、テキストモードで開いた場合、20文字+改行文字で21キャラですから「22」以上を指定しなければなりません。これは、読みとった文字列の最後にナル文字を格納するためです。
従ってfgetsは5回行われますが、4回目で1文字以上を格納し、かつ、EOFを検出しています。つまりfgetsはNULLを返していません。このときにfeofを実行すれば「EOF検出」となるのですが、5回目を実行したためにfeofは「非EOF検出」となっています。

対策としては、充分な格納領域buffと読みとり文字数を引数にして、最後の行に改行があることを信じないで、毎回EOF判定する。

こんなとこでしょうか?
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています


おすすめ情報