バイナリファイルのdump表示

バイナリファイルのdump表示(基本情報 令和元年秋期午後問9)

 ファイルの先頭位置と、dumpの開始位置・終了位置との関係などが今一歩イメージがわかないので、令和元年秋期午後問9のリストを打ち込んでみました。

色々と読み落としがたくさんある

なんで to にマイナスが入るんだ??

 最初に疑問だったのは、to にマイナスのデータが入る点。
 これは、to の因数仕様の所で、「値が負の場合はファイルの末尾」と書いてあるじゃあありませんか。
 要するに、to がマイナスなら、ファイルの最後まで読み込んでしまうということです。

from は、どこから始まるの??

 これも、一番最初の所に、「バイト位置は,ファイルの先頭のバイトから順に 0,1,・・・と数える」と書いてある。
 ということで、先頭バイトが 0 で、その cnt は 1 なので、 そこから読み込もうとすると、from = 0 となっていなければならないので、問1 C の選択肢は、 cnt > from になります。

ファイルの大きさと文字数との関係は?

 1バイトの文字をn文字分収容したファイルの大きさは、nバイトになるようですね。 EOF というのは、ファイルに書き込んであるのではなくて、ファイルが終わったときに、次を読み込もうとしたら、 EOF が返ってくるということの様です。考えてみれば当然なんでしょうが。しかしそのあたり、文字変数の最後に、’\0′ が入っているのとは、イメージが違います。
 それなので、設問2の e は、to = 99 で、データの最後の100文字目まで読み込んでいるので、次を読もうとして chr に EOF が入ってくるので、データ終了になるということです。
 もう一つ間違いやすいのは、from = 99 、to = 99 ということで、100文字目を1文字読み込んでいるということ。なので、cnt = 1 となって、 END OF DATA … 1 byte(s) が返ってきます。

1行丸ごと印字している

 あまりよく考えないで、印字は1文字ずつやっているのかと漠然と勝手に考えていたら、 bufC[WIDTH] = bufH[WIDTH] = bufL[WIDTH] = ‘\0’; と、配列の最後に終了文字を入れておいて、文字列として、1行丸ごと印字していたのですね。
 ソースを色々改造してみようとしてみるまで、全く気付かなんだ。

条件分岐の及ぶ範囲に注意

 例えば、このソースコードの26行目付近に下のようなコードがあります。

  if ((0x20 <= chr) && (chr <= 0x7E))
    tblC[chr] = chr;
  else
    tblC[chr] = MASKCHR;
  tblH[chr] = hex[chr >> 4];
  tblL[chr] = hex[chr & 0x0f];

 これの、if else の後に { がないので、それぞれの条件が及ぶ範囲は、直後の1行だけになります。
 ですから、このコードの下の2行分は、 if の条件が成立する場合もしない場合も、どちらの場合でも、実行されることになります。
 普通実行範囲を、下の様に、 { } で囲むやり方に慣れているので、この括弧がないと、else 条件がずっと続いて、下の2行も else の場合だけに実行されるように錯覚してしまいがちです。
 この、if else が及ぶ範囲が、直後の1行だけであることは、字下げの仕方を見れば確認できます。これを見て、「なるほどそういうことだったのか。」と、改めて認識した次第です。
 気を付けないといけません。

  if ((0x20 <= chr) && (chr <= 0x7E)){
    tblC[chr] = chr;
  }else{
    tblC[chr] = MASKCHR;
  }
  tblH[chr] = hex[chr >> 4];
  tblL[chr] = hex[chr & 0x0f];

 こんな感じで書いてくれていると、間違わないんだけどなあ。
 自分だったら、絶対問題文のようなコードは書かないし。

ソースコード

#include <stdio.h> 
#define WIDTH 60 /* 行当たり表示バイト数 */
#define MASKCHR '.' /* 16進数表示で 20~7E 以外の場合の表示用文字 */
#pragma warning(disable:4996) /* visual stadioで fopen のエラーを出さない */

void dump(char* filename, long from, long to);

int main() {
  char filename[] = "E:\\Users/****/test1.txt";
    /* Eドライブの絶対指定 ファイル */
  long from = 1;
  long to = 22;
  dump(filename, from, to);
}

void dump(char* filename, long from, long to) {
  FILE* infile;
  int chr, pos = 0;
  long cnt = 0;
  char tblC[256], bufC[WIDTH + 1];
  char tblH[256], bufH[WIDTH + 1];
  char tblL[256], bufL[WIDTH + 1];
  char hex[] = "0123456789ABCDEF";

  for (chr = 0x00; chr <= 0xFF; chr++) {
    if ((0x20 <= chr) && (chr <= 0x7E))
      tblC[chr] = chr;
    else
      tblC[chr] = MASKCHR;
    tblH[chr] = hex[chr >> 4];
    tblL[chr] = hex[chr & 0x0f];
  }
  bufC[WIDTH] = bufH[WIDTH] = bufL[WIDTH] = '\0';

  ;
  if ((infile = fopen(filename, "rb")) == NULL) {
    printf("ファイルオープンエラー");
    printf("\n %s \n", filename);
    return;
  }

  while (((chr = fgetc(infile)) != EOF)
    && ((to < 0) || (cnt <= to))) {
    cnt++;
    if (cnt > from) {
      bufC[pos] = tblC[chr];
      bufH[pos] = tblH[chr];
      bufL[pos] = tblL[chr];
      pos++;
      if (pos == WIDTH) {
        printf("%10ld  \n%s\n%12s%s\n%12s%s\n\n",
          cnt - WIDTH, bufC, " ", bufH, " ", bufL);
        pos = 0;
      }
    }
  }
  if (pos > 0) {
    bufC[pos] = bufH[pos] = bufL[pos] = '\0';
    printf("%10ld  %s\n%12s%s\n%12s%s\n\n",
      cnt - pos, bufC, " ", bufH, " ", bufL);
  }
  if (chr == EOF) {
    printf("END OF DATA ... %ld byte(s)\n", cnt - from);
    printf("cnt=%ld from=%ld to=%d \n", cnt, from, to);
  }else {
    printf("END OF DUMP ... %ld byte(s)\n", cnt - from);
    printf("cnt=%ld from=%ld to=%d \n", cnt, from, to);
  }
  fclose(infile);
}

 下は、24文字あるファイルで、 from = 1, to = 22 を指定した時の表示です。

   1  2345678910.... tes.123
      33333333338A8E27672333
      234567891020280453E123

END OF DUMP ... 22 byte(s)
cnt=23 from=1 to=22
タイトルとURLをコピーしました