バイナリファイルの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