2.6 ファイル
前章までで利用者が求めるアルゴリズムや手続をC言語にどう展開するかの基本について理解できたかと思います。この章では、より実務として使用する際のテクニックについて話を進めます。パーソナルコンピュータを使用して処理をするメリットの1つは、ハードディスク等の補助記憶装置を利用した計算結果の再現性やデータの保存の機能でしょう。このメリットはC言語ではファイルの取り扱いとして実現されます。
2.6.1 ファイルに何かを書いてみる
まずは、ファイルに hello FILE world と書いてみましょう。
<リスト60>filew0n.c /* filew0n.c */ #includemain() { FILE *fp; fp = fopen( "test.txt", "w" ); fprintf( fp, "hello FILE world\n" ); fclose( fp ); } このリストのプログラムでは、画面に出力するプログラムとくらべ、ファイルに関するいくつかの記述が増えています。 FILEはファイルに関する情報を持つ構造体です。この構造体の宣言は標準入出力ヘッダー・ファイル(stdio.h)の中で行われています。fp はFILE構造体を指すポインタで、ファイルの識別のために用います。fopen()はこのファイル・ポインタを返す関数です。fopen()はファイルのオープンに必要です。ファイルをアクセスする前に、 fp = fopen( filename, mode ); を実行します。ここで、filenameは、ファイル名へのポインタです。modeはオープンの方法を表す文字列へのポインタで、読み込みの場合は"r"、書き込みの場合は"w"、追加の場合は"a"を指定します。fopen()で返される値は、ファイル・ポインタと呼ばれ、ファイルの識別のために用いるので、この fp の値は保存しておきます。通常のファイル操作ではFILE構造体そのものを操作する必要はないので「FILEという型の変数を使う」という理解でも問題ありません。この fp がNULLの時には何かの事情でファイルがオープンできないことを意味しています。 ファイルのアクセスの終了は、 fclose( fp ); を実行して、ファイルをクローズします。fclose()は、読み込みファイルのオープンに対しては必ずしも必要ではありませんが、書き込みや、追加オーブンの場合にはバッファ(システムの方で自動的に用意される)のデータを書き戻すことを行うので、必ず必要です。プログラムの終了時には fclose()を実行して、全てのファイルをクローズしてから、プログラムを終了します。
●ファイルに1から10まで書いてみる
次はファイルに1から10まで書いてみます。この場合もfopen()、fclose()がふえただけで、標準入出力への操作と変わりありません。
<リスト61>filew1n.c
│ <図2 test2.txtの内容> /* filew1n.c l4.3 */ │ 1 #include│ 2 main() │ 3 { │ 4 int i; │ 5 FILE *fp; │ 6 fp=fopen("test2.txt","w"); │ 7 for(i=1;i<=10;i++) │ 8 fprintf(fp,"%d\n",i); │ 9 fclose(fp); │ 10 } │ 2.6.2 ファイルからの読み込み
ファイルからの読み込みプログラムを作ります。filew1n.cで作ったtest2.docがカレント・ディレクトリにあるとします。これを引き上げてみましょう。
<リスト62>
/* filer0n.c */ #includemain() { int i,d,s; FILE *fp; fp=fopen("test2.txt","r"); s = 0; for(i=0;i<10;i++){ fscanf(fp,"%d",&d); printf("data = %d \n",d); s = s + d; } printf("sum = %d\n",s); fclose(fp); } ●FILE構造体
構造体と呼ばれるメカニズムはC言語の特徴の1で、異なった型の変数を1つの複合型として扱うことができます。ここではFILE構造体について簡単に解説します。
構造体は
struct x { int i; double a; };と宣言を記述することにより「x という「構造体タグ」名は int x と double a の合わさった構造を表す」ことになります。
その後で
struct x x0; と記述すると「x という構造をもった複合型の変数 x0 を定義する」ことになります。 このx0について、例えば構造体内部のiを操作するには x0.i = 10; というふうに使います。x は構造体タグ名ですので、直接 x を使って変数の型を決めることはできません。そこで、 typedef struct x X と記述すると「x という構造を持った複合型の変数を X 型と決める」ことになります。X は変数の型を表しますから、 X x1; と記述すれば「x という構造をもったX型の変数 x1 を定義する」となります。 このx1はx0と同じ構造をしていることになり、内部を操作するには、 x1.i = 20; というふうに使います。FILE構造体はVisualCの場合、インクルードファイルの stdio.h の中で struct _iobuf { char *_ptr; int _cnt; char *_base; int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname; }; typedef struct _iobuf FILE; と記述されています。 つまり char *_ptr;、int _cnt;、char *_base;、int _flag;、 int _file;、int _charbuf;、int _bufsiz;、 char *_tmpfname; の8つの変数を持つ構造を _iobuf(構造体タグ名)と決めて、さらにその構造をFILE型という型にすると決めています。 10.3 typeコマンドを作る MSDOS(DOS窓)のコマンドTYPEと同じ動作をするプログラムをリスト10.4に示します。短いプログラムですが、インクルードファイル、main()の引き数、ポインタ配列、FILE構造体、if文、 while文、ファイルなど、C言語のエッセンスが詰まっています。 リスト10.4 /* utypen.c */ #includeint main(int ac,char *av[]) { int c; // fgetc()は文字コードをint型の数値として // 返すので、int型変数で受ける FILE *fp; if(ac != 2){ // コマンドラインの文字列が2つ必要、つまり // 読み出すべきファイル名が指定されないと終了 printf("Usage: utype filename \n"); return 0; } if((fp = fopen(av[1],"r")) == NULL ){ // 読み出すべきファイルが開けられないと終了 printf("file open err \n"); return 0; } while((c = fgetc(fp)) != EOF ){ printf("%c",(char)c); } fclose(fp); return 1; } 使い方:Zドライブ直下のctype.cというファイルを見る場合 Z:>utype utype.c プログラムの組み立てとしては、書き込みと同様にファイルのオープン、実際の入出力、ファイルのクローズから成ります。出力と違う点は、読み込むファイルの終了を意識しないといけない点です。ここではfgetc()をwhileの条件判断式に入れることにより、ファイルの終わりと同時にループを終了しています。fgetc()はファイル入力用の関数で、ファイルから1文字読み込み、その文字コードを関数の値として返し、ファイルを読み終えるとファイルの終了を表すコードEOFを返します。 テキストファイル、バイナリファイルとも、C言語でファイルを扱うときにはヘッダファイル stdio.h で typedef 定義されている FILE 型を指すポインタのファイルポインタを使用します。ですから、ファイルの操作をするときには必ず stdio.h をインクルードします。EOFもstdio.h の中で #define EOF (-1) と記述されていて、-1の値を持ちます。 10.4 他のアプリケーションとの連携 ●九九の表を作る 例えば九九の表をワープロで作る場合を考えます。この場合、プログラムを使って数値を計算して、その値をワープロに張り込めば簡単です。リスト10.5では kuku.txt というテキストファイルを出力します。 リスト10.5 : 九九データ出力 #include main() { int x,y; FILE *fp; fp = fopen( "kuku.txt", "w"); for( x = 1; x <= 9; x++){ for( y = 1; y <= 9; y++){ fprintf( fp, "%2d ", x * y ); } fprintf( fp, "\n" ); } fclose( fp ); } 図 kuku.txtをMS-Wordで読み込む ●SIN/COSデータを出力する〜MS-Excelとの連携 MS-Excelとのやりとりを紹介します。MS-Excel自身も他のソフトウェアとのファイル変換ユーティリティがあり多数のフォーマットに対応しています。機能は低くなりますが、C言語で取り扱うデータファイルの形式はとしてはCSVフォーマットが適当でしょう。 CSVフォーマットは数字や文字列を , (コンマ)で区切ったもので、行と列もテキストに縦横に書けばいいだけです。リスト10.6はサインとコサインのデータをCSVフォーマット出力するプログラムです。これをMS-Excelに読みこませて描いたグラフを図に示します。 リスト10.6 : CSVフォーマット出力のサインコサイン /* sincos1.c for vc6 /* -127から127までの整数値で100点のデータを作る */ #include #include #define IDATN 100 /* データセット数 */ #define SIN 0 #define COS 1 int iodat[2][IDATN]; void main() { int i; double kaku; FILE *fp; for(i=0;i int main(int ac,char *av[]) { FILE *fp; char buff[200]; if(ac != 2){ printf("err .. xcat filename \n"); return 0; } if((fp = fopen(av[1],"r")) == NULL){ printf("open err \n"); return 0; } while(fgets(buff,200,fp) != NULL) printf("%s",buff); fclose(fp); return 1; } <リスト7>fscanf("%s")を使用 /* utype02n.c */ #include int main(int ac,char *av[]) { FILE *fp; char buff[200]; if(ac != 2){ printf("err .. xcat filename \n"); return 0; } if((fp = fopen(av[1],"r")) == NULL){ printf("open err \n"); return 0; } while(fscanf("%s",buff) != EOF) printf("%s\n",buff); fclose(fp); return 1; } fgetsを使用した方のソースファイル読み込ませた結果を図3、fscanf("%s");を使用した方を図4に示します。 <図3>fgetsを使用したリスト5の場合 #include "stdio.h" main(ac,av) int ac; char *av[]; { FILE *fp; char buff[200]; ・ ・ ・ <図4>fscanf("%s")を使用したリスト6の場合 #include "stdio.h" main(ac,av) int ac; char *av[]; { FILE *fp; char buff[200]; ・ ・ ・ <クイック・リファレンス5 逆引きファイル操作関数> ファイルポインタ変数の定義 FILE *fp; ファイルのオープン fp = fopen(filename, "r" ); /* 読み込みモードでオープン */ fp = fopen(filename, "w" ); /* 書き込みモードでオープン */ fp = fopen(filename, "a" ); /* 追加モードでオープン */ ※MS−DOS系のC言語の場合、テキストモードでファイル をオープンするときは、通常は上のようにします。 バイナリモードは場合はそれぞれ、"rb"、"wb"、"ab"となり ます。 ファイルからの1文字読み込み int c; c = fgetc( fp ); または getc( fp ); 返り値は読み込んだ文字のコード ファイルの終わりまたはエラーの時は EOF を返す。 ファイルからの1行読み込み char buf[256]; int n = sizeof buf; fgets( buf, n, fp ); 最大 (n-1) 文字として改行文字までを読み込む 文字列の終わりには \0 が追加される 返り値は bufに読み込まれた文字数もしくは、 ファイルの終わりまたはエラーの時は NULL を返す。 ファイルからのフォーマットつき入力 fscanf( fp, " 入力フォーマット ", 入力パラメータをさすポインタ...); 入力フォーマットについては scanf と同じ。 ファイルへの1文字書き込み int c; fputc( c, fp ); または putc( c, fp ); ファイルへの文字列書き込み char buf[256]; fputs( buf, fp ); fgets とは異なり、文字列の終わりまで書かれる ファイルへのフォーマットつき出力 fprintf( fp, " 出力フォーマット ", 出力パラメータ...); 出力フォーマットについては printf と同じ。 図5に示す様なデータファイルを基に棒グラフを描いてみます。棒グラフの部分をグラフィック関数にすれば結構実用になります。リスト7。実行結果を図6に示します。 <リスト8>filer1n.c /* filer1n.c l4.8 */ #include int main(int ac, char *av[]) { int n,i,j,d; FILE *fp; if((fp=fopen(av[1],"r")) == NULL){ printf("file open err\n"); return 0; } fscanf(fp,"%d",&n); printf("data name[%s], num[%d]\n",av[1],n); for(i=0;i < n;i++){ fscanf(fp,"%d",&d); printf("data = "); for(j=0;j < d;j++) printf("*"); printf("\n"); } fclose(fp); return 1; } <図5>データファイルtest.dat 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <図6>リスト7の実行結果 data name[test.dat], num[20] data = * data = ** data = *** data = **** data = ***** data = ****** data = ******* data = ******** data = ********* data = ********** data = *********** data = ************ data = ************* data = ************** data = *************** data = **************** data = ***************** data = ****************** data = ******************* data = ******************** <クイック・リファレンス6 よくつかう入出力関数> C言語には、その生い立ちから、複数系統の入出力関数があります。それらは大きく3系統に分けられます。 1.マクロ系 getc、putc、getchar、putchar 2.関数系 fgetc、fputc、fgets、fputs、他 3.フォーマット系 printf、scanf、他 よく使う関数を系統別にまとめておきます。 1.マクロ系 ◆putc ┌────────────────────────────┐ │int putc(int ch, FILE *fp); │ └────────────────────────────┘ - 124 - putc は、文字 c を stream に指定されたストリームへ出力す るマクロです。 戻り値:成功した場合出力した文字 c を返し、エラーの場合は EOF を返します。 ◆getc ┌────────────────────────────┐ │int getc(FILE *fp); │ └────────────────────────────┘ getc は、指定の入力ストリームから次の 1 文字を読み込んで、 そのストリームのファイルポインタをインクリメントするマク ロです。 戻り値:成功した場合、読み込んだ文字を返し、ファイルエンドまたはエラーの場合は EOF を返します。 ◆putchar ┌────────────────────────────┐ │int putchar(int ch); │ └────────────────────────────┘ putchar(c) は、putc(c, stdout) と定義されたマクロです。 戻り値:成功した場合文字 c を返し、エラーの場合には EOF を返します。 ◆getchar ┌────────────────────────────┐ │int getchar(void); │ └────────────────────────────┘ getcharは、ストリーム stdin 上の次の 1 文字を返すマクロ です。 戻り値:getchar は、成功した場合読み込んだ文字を返し、ファイルエ ンドまたはエラーの場合は EOF を返します。 2.関数系 ◆fputc ┌────────────────────────────┐ │int fputc(int c, FILE *fp); │ └────────────────────────────┘ fputc は、putc の関数版です。 fputc は、文字 c を指定したストリーム stream へ出力します。 戻り値:成功した場合、文字 c を返し、エラーの場合は EOF を返します。 ◆fgetc ┌────────────────────────────┐ │int fgetc(FILE *fp); │ └────────────────────────────┘ fgetc は、getc の関数版です。 戻り値:成功した場合、読み込んだ文字を返し、ファイルエンドまたはエラーの場合は EOF を返します。 ◆puts ┌────────────────────────────┐ │int puts(const char *s); │ └────────────────────────────┘ puts は、ヌル文字で終わる文字列 s を、標準出力ストリームstdout に出力し、最後に改行文字をつけます。 戻り値:成功した場合は負でない値を返し、エラーの場合は EOF を返します。 ◆gets ┌────────────────────────────┐ │char *gets(char *string); │ └────────────────────────────┘ gets は、標準入力ストリーム stdin から、復帰文字で終わる 文字列を読み込み、s に格納します。 戻り値:成功した場合文字列引数 s を返し、ファイルエンドあるいはエラーの場合は NULL を返します。 ◆fputs ┌────────────────────────────┐ │int fputs(const char *s, FILE *fp) │ └────────────────────────────┘ fputs は、ヌル文字で終わる文字列 s を、指定の出力ストリーム stream へコピーします。 戻り値:成功した場合は最後に書いた文字を返し、エラーの場合は EOF を返します。 ◆fgets ┌────────────────────────────┐ │char *fgets(char *s, int n, FILE *fp); │ └────────────────────────────┘ fgets は、ストリーム stream から文字の列を読み、文字列 string に格納します。読み込みは、(n-1) 個の文字を読むか、改行文字を読むかした段階で終わります。 戻り値:成功した場合文字列引数 string を返し、ファイルの終わりあるいはエラーの場合は、NULL を返します。 ◆ブロック単位入出力関数 ────────────────────────────── 関 数 内 容 ────────────────────────────── fwrite ストリームにデータを書く fread ストリームからデータを読み込む。 ────────────────────────────── ◆fwrite ┌────────────────────────────┐ │size_t fwrite(const void *ptr, size_t size, │ │ size_t n, FILE *fp);│ └────────────────────────────┘ fwrite は、n 個のデータを指定の出力ストリームにptr が指すデータをが出力します。 fwrite は、成功した場合実際に書き込んだデータの個数を返 し、エラーがあった場合には、データ数より小さな値を返します。 ◆fread ┌────────────────────────────┐ │size_t fread(void *ptr, size_t size, size_t n,FILE *fp);│ └────────────────────────────┘ fread は、nitems 個のデータを指定の入力ストリームから読 み込んで、ptr が指すブロックに格納します。 fread は、成功した場合は実際に読み込んだデータの個数を返 し、ファイルの終わりやエラーの場合、0 を返します。 3.フォーマット系 ◆printf ┌────────────────────────────┐ │int printf(const char *format, ...); │ └────────────────────────────┘ printf は、format によって指される書式文字列中の書式指定 を、format の後に続く各引数に適用し、書式化されたデータ を stdout に出力します。 戻り値:出力したバイト数を返し、エラーの場合はEOF を返します。 フォーマット指定 ────────────────────────────── % [flags] [width] [.prec] [F|N|h|l] type ────────────────────────────── タイプ 出力のフォーマット ───────────────────────────── d 符号つき 10 進法の整数 i 符号つき 10 進法の整数 o 符号なし 8 進法の整数 u 符号なし 10 進法の整数 x 符号なし 16 進法の整数の子文字 X 符号なし 16 進法の整数の大文字 f 浮動少数点 [-]dddd.ddd e 浮動少数点 [-]d.ddd e [+/-]ddd g 精度を基にしたフォーマット e または f E e と同じですが、E は指数です。 G g と同じですが、E は指数です。 c 一文字 s '\0' または [.prec] までプリントします。 % % 文字 p ポインタ near - YYYY; far - XXXX YYYY n 入力引数で指定された位置に書かれた文字数を格納します。 ────────────────────────────── [flag] [flag] の指定 ────────────────────────────── none 0 またはブランクで埋めて、右へ揃えます。 - スペースで埋めて、左へ揃えます。 + いつも + または - で始まります。 blank 負の値だけをプリントするサインです。 # 他の方法を使用しての変換 c,s,d,i,u 効果なし o 0 以外の引数だと 0 を前に付けて、その 欄を埋めます。 x か X 引数の前に 0x または 0X を付けます。 e, E, f いつも少数点を使用します。 g か G 同上ですが、後ろに 0 が付きません。 ────────────────────────────── [width] 出力の効果 ────────────────────────────── n 少なくとも n 文字あり、ブランクで埋めます。 0n 少なくとも n 文字あり、0 で左を埋めます。 * リストからの次の引数は width(幅)です。 ────────────────────────────── [.prec] 出力の効果 ────────────────────────────── none デフォルトの精度 .0 d,i,o,u,x デフォルトの精度 e, E, f 少数点なし .n 最高 n 文字 * リストからの次の引数は precision(精度)です。 ────────────────────────────── 修正語 引数の解釈の方法 ────────────────────────────── F 引数は far ポインタ N 引数は near ポインタ h d,i,o,u,x,X 引数は short 整数 l d,i,o,u,x,X 引数は long 整数 e,E,f,g,G 引数は double ────────────────────────────── ◆scanf ┌────────────────────────────┐ │int scanf(const char *format, ...); │ └────────────────────────────┘ scanf は、一連の入力フィールドをスキャンして、一度に1文字ずつストリーム stdin から文字を読み込みます。次に、引数 format によって指される書式文字列中の書式指定にしたがって、各フィールドを書式化します。 ◆書式つき入出力関数 ────────────────────────────── 関 数 内 容 ────────────────────────────── fprintf ストリームに書式付き出力を行います。 sprintf 文字列に書式つき出力を書き込みます。 fscanf ストリームから書式付き入力を行なう sscanf 文字列をスキャンして書式つき入力を行ないます。 ────────────────────────────── ◆fprintf ┌────────────────────────────┐ │int fprintf(FILE *fp, const char *format, ...); │ └────────────────────────────┘ fprintf は、format によって指される書式文字列中の書式指 定を、format の後に続く各引数に適用し、書式化されたデータを指定されたストリームに出力します。 書式指定に関する詳細な情報については、printf の解説を参照してください。 ◆sprintf ┌────────────────────────────┐ │int sprintf(char *buffer, const char *format, ...); │ └────────────────────────────┘ sprintf は、format によって指される書式文字列中の書式指 定を、format の後に続く各引数に適用し、書式化されたデー タを文字列 *buffer に出力します。 sprintf は、出力したバイト数を返し、エラーの場合は EOF を返します。 ◆fscanf ┌────────────────────────────┐ │int fscanf(FILE *fp, const char *format, ...); │ └────────────────────────────┘ fscanf は、一連の入力フィールドをスキャンして、一度に 1 文字ずつストリームから文字を読み込みます。 fscanf は、正しくスキャンし、変換し、格納した入力フィー ルドの数を返します。 ◆sscanf ┌────────────────────────────┐ │int sscanf(const char *buffer, const char *format, ...);│ └────────────────────────────┘ sscanf は、一連の入力フィールドをスキャンして、一度に 1 文字ずつ文字列から文字を読み込みます。 次に、引数 format によって指される書式文字列中の書式指定 にしたがって各フィールドを書式化します。 [コラム ちょっと便利なリダイレクトとパイプ] 本来C言語の機能ではありませんが、MS−DOSのコマンドラインにはリダイレクトとパイプという機能があります。入出力とも複数のファイルを扱わないならC言語の標準入出力をリダイレクトでファイルに振り分けたり、パイプで複数のプログラムを接続できて便利です。 素数をもとめるプログラムを分解してパイプでつないで実験します。まずリストAで1から100までの数を発生させます。 <リストA> /* gen1100n.c l4.ca */ int main(void) { int i; for(i=1;i<=100;i++){ printf(" %2d",i); } return 1; } <図A> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 リストAを gen1100 > gen1100.kek とリダイレクトすることで1から100までの数のファイルができます。つぎに入力した数が素数であるかのリストBのようなフィルタをつくります。 <リストB> /* so2n.c */ #include int main(void) { int i,j,sosu; while(EOF != scanf("%d",&i)){ sosu = 1; for(j=2;j so2.kek として、もう一度ファイルにすることもできます。1から100までの数発生プログラムとパイプで結ぶのなら gen1100 | so2 のようにコマンドラインに書きます。 リストCは数値を2倍にするフィルタです。これと組み合わせて、 gen1100 | so2 | bai のように書けば図Cのような1から100までの素数の2倍の数が表示されます。 <リストC> /* bain.c */ #include int main(void) { int i; while(EOF != scanf("%d",&i)) printf(" %3d",i * 2); return 1; } <図C> 2 4 6 10 14 22 26 34 38 46 58 62 74 82 86 94 106 118 122 134 142 146 158 166 178 194 [ファイルのダンプ] これも定番です。ファイルの16進ダンプのプログラムを書いてみましょう。この場合文字コード以外もそのまま読み込みたいのでfread()を使用します。データの16進表示と併せて、文字コードは文字としても表示させたいので文字コードとそうでないものとを分けてキャラクタ表示するようにします。 <リスト3>ファイルの16進ダンプ /* dpn.c l6.3 */ #include #include int main(int ac,char *av[]) { FILE *fp; int cn,i,n; unsigned char buff[20]; if(ac != 2){ printf("usage:dp filename\n"); return 0; } if((fp=fopen(av[1],"rb")) == NULL){ printf("file open err[%s]\n",av[1]); return 0; } cn=0; while(1){ n=fread(buff,1,16,fp); printf(" %04X : ",cn); cn = cn + 16; for(i=0;i<16;i++) if(i< n) printf("%02X ",buff[i]); else printf(" "); printf(" "); for(i=0;i < n;i++) if(iscntrl(buff[i])) printf("."); else printf("%c",buff[i]); printf(" \n"); if(n != 16) break; } return 1; } <実行結果>dp.cをダンプする 0000 : 2F 2A 20 09 64 70 2E 63 09 2A 2F 0D 0A 23 69 6E /* .dp.c.*/..#in 0001 : 63 6C 75 64 65 20 3C 73 74 64 69 6F 2E 68 3E 0D clude . 0002 : 0A 23 69 6E 63 6C 75 64 65 20 3C 63 74 79 70 65 .#include ..main(ac,av) 0004 : 0D 0A 69 6E 74 09 61 63 3B 0D 0A 63 68 61 72 09 ..int.ac;..char. 0005 : 2A 61 76 5B 5D 3B 0D 0A 7B 0D 0A 09 46 49 4C 45 *av[];..{...FILE 0006 : 09 2A 66 70 3B 0D 0A 09 69 6E 74 09 63 6E 2C 69 .*fp;...int.cn,i 0007 : 2C 6E 3B 0D 0A 09 75 6E 73 69 67 6E 65 64 20 63 ,n;...unsigned c 0008 : 68 61 72 09 62 75 66 66 5B 32 30 5D 3B 0D 0A 0D har.buff[20];... 0009 : 0A 09 69 66 28 61 63 20 21 3D 20 32 29 7B 0D 0A ..if(ac != 2){.. 000A : 09 09 70 72 69 6E 74 66 28 22 75 73 61 67 65 3A ..printf("usage: 000B : 64 70 20 66 69 6C 65 6E 61 6D 65 5C 6E 22 29 3B dp filename\n"); 000C : 0D 0A 09 09 65 78 69 74 28 31 29 3B 0D 0A 09 7D ....exit(1);...} 000D : 0D 0A 09 69 66 28 28 66 70 3D 66 6F 70 65 6E 28 ...if((fp=fopen( 000E : 61 76 5B 31 5D 2C 22 72 62 22 29 29 20 3D 3D 20 av[1],"rb")) == 000F : 4E 55 4C 4C 29 7B 0D 0A 09 09 70 72 69 6E 74 66 NULL){....printf 0010 : 28 22 66 69 6C 65 20 6F 70 65 6E 20 65 72 72 5B ("file open err[ 0011 : 25 73 5D 5C 6E 22 2C 61 76 5B 31 5D 29 3B 0D 0A %s]\n",av[1]);.. 0012 : 09 09 65 78 69 74 28 31 29 3B 0D 0A 09 7D 0D 0A ..exit(1);...}.. 0013 : 09 63 6E 3D 30 3B 0D 0A 09 77 68 69 6C 65 28 31 .cn=0;...while(1 0014 : 29 7B 0D 0A 09 09 6E 3D 66 72 65 61 64 28 62 75 ){....n=fread(bu 0015 : 66 66 2C 31 2C 31 36 2C 66 70 29 3B 0D 0A 09 09 ff,1,16,fp);.... 0016 : 70 72 69 6E 74 66 28 22 20 25 30 34 58 20 3A 20 printf(" %04X : 0017 : 22 2C 63 6E 2B 2B 29 3B 0D 0A 09 09 66 6F 72 28 ",cn++);....for( 0018 : 69 3D 30 3B 69 3C 31 36 3B 69 2B 2B 29 0D 0A 09 i=0;i<16;i++)... 0019 : 09 09 70 72 69 6E 74 66 28 22 25 30 32 58 20 22 ..printf("%02X " 001A : 2C 62 75 66 66 5B 69 5D 29 3B 0D 0A 09 09 70 72 ,buff[i]);....pr 001B : 69 6E 74 66 28 22 20 20 20 22 29 3B 0D 0A 09 09 intf(" ");.... 001C : 66 6F 72 28 69 3D 30 3B 69 3C 31 36 3B 69 2B 2B for(i=0;i<16;i++ 001D : 29 0D 0A 09 09 09 69 66 28 69 73 63 6E 74 72 6C ).....if(iscntrl 001E : 28 62 75 66 66 5B 69 5D 29 29 0D 0A 09 09 09 09 (buff[i]))...... 001F : 70 72 69 6E 74 66 28 22 2E 22 29 3B 0D 0A 09 09 printf(".");.... 0020 : 09 65 6C 73 65 0D 0A 09 09 09 09 70 72 69 6E 74 .else......print 0021 : 66 28 22 25 63 22 2C 62 75 66 66 5B 69 5D 29 3B f("%c",buff[i]); 0022 : 0D 0A 09 09 70 72 69 6E 74 66 28 22 20 20 5C 6E ....printf(" \n 0023 : 22 29 3B 0D 0A 09 09 69 66 28 6E 20 21 3D 20 31 ");....if(n != 1 0024 : 36 29 0D 0A 09 09 09 62 72 65 61 6B 3B 0D 0A 09 6).....break;... 0025 : 7D 0D 0A 7D 09 09 09 62 72 65 61 6B 3B 0D 0A 09 }..}...break;...