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