れたデータファイルは 1 バイト分後ろへずれ てしまうのて、 , 追加用としてオープンして はいけません ( Fig. 10 ) 。 そこて、 , 「更新用」としてオープンします。 これならばファイルの途中 ( 実際には最終レ コードの次 ) をアクセスする場合にも問題は 生じません。 ファイルポインタのセット 追加する場合のファイルポインタの計算 は読み込みの場合と異なり , 「レコード長 x レコード数」て、行います。なぜなら , 最終レ コードのオフセットは , 「レコード長 x ( レ コード数ー I) 」て、表されるからて、す ( Fi g. 11 ) 。 追加するレコードは , 最終レコード十 1 な 1 : のて、 , 前述のように計算してください データの入力 追加するデータの入力は , gets 関数を使 用してフィールドごとに行います。本来な らば , もっと高レベルな関数を自作したい ところて、すが , 今回の目的て、はないのて、 gets 関数を使用します。 なお , 入力する文字はフィールド長いつば いとはかぎりません。そのため , fputs 関数に 引渡す前に , データを正規化しておく必要 があります。つまり , 入力されたデータが フィールド長に満たない場合には , 残りの 部分に空白 ( 0X20 ) をつめておき , その次に NULL コードを設定しておきます ( 108 ) Fig. 12 ) 。 Fig. 6 レコード表示のフロ dispdata SDF ファイルオープン レコード番号入力 レコード番号表示 1 レコード表示 SDF ファイルクローズ ーチャート ( その 1 ) datO pen SDF ファイルへの DB 的アクセス ( レコードの追加 ) List 4 e ー se pr i ntf ( " ファイルポインタ工ラー Yn " ) : if(stat!=NU しい stat=fseek(datfile, fileptr. SEEK_SET) : pr i nt f ( " ファイルポインタ = %dY n" , f ⅱ e ptr) : fileptr=mng. rec ln*mng. reccnt : if(datfile!=NU しし ) datfile=datopen()r + ") : 9 : FILE *datfile,*datopen(); 8 : char str[61), *fgets ( ) : intMdno,stat,i,j,writeln; long int fileptr; void apndrec( void ) apndrec 復帰 disprec ファイルポインタ計算 ファイルポインタセット 1 フィールド読み込み No 1 フィールド表示 復帰 Yes フィールド終わり gets printf disprec fcl ose fegets printf , wr i te ) ; 106 3 : 4 : 6 : 7 : 12 : 14 : 17 : 20 : 22 : 23 : 24 : 25 : 26 : 28 : 29 : 30 : 31 : 32 : 33 : 34 : 35 : 36 : 38 : 39 : 40 : 42 : 43 : 44 : 45 : CMAGAZINE for(fldno=0;fIdno く mng. fldcnt;fldno + + ) writeln=mng. flntbl[fldno]; pr i ntf ( " 入力文字は %d 半角文字まで Yn " if(gets(str)==NU しし ) printf(" ストリーム入力工ラー \ n " ) : bufCl]=NU しし: bufC0]=0x0a; pr i nt f ( " ライトエラ if(fputs(buf,datfiIe)==NULL) buf [ i ] =str [ i ] : for ( i = 0 : i く j : i + + ) bufCi]=NU しし : bu f [ i ] ニ for(i=0;i く writeln;i + + ) j=strlen(str) : if(fputs(buf, datfiIe)==NULL) pr i ntf ( " C R L F ライトエラー Yn") : buf[0]=0xla; if(fputs(buf, datfile)==NU しい / * write EOF code * / pr i ntf ( " C R L F ライトエラー Yn") : mng. reccnt 十十 : fclose(datfile) : 1990 1 レコード表示のフローチャート ( その 2 ) datopen ファイル名表示 オープン用ファイル名作成 SDF ファイルオープン 復帰 工ラーメッセージ表示 Yes No
く戻り値〉正常に処理てきたとき , 読み 出した文字データを返す。工ラーが発生し たり , 入力データの終端に達したときは EOF を返す。 ・ int sputc( int c ) く要約〉バッフアに文字データを格納す る。もし , バッフアがいつばいなら , メンバ関 数 overflow を呼び出し , バッフアを空の状 態にしてから文字データを格納する。 く戻り値〉正常に処理てきたとき 0 を , 工 ラーが発生したとき EOF を返す。 以上のように , streambuf クラスのメン バ関数のうち , 実際の入出力を行うのは , overflow, underflow だけて、ある。それ以 外のメンバ関数は , バッフアを制御するた めのものて、あり , 実際の入出力装置とは , 直接的には結びついていない こて、重要なのは , streambuf クラスの メンノヾ関数 overflow, underflow が仮想関 数となっていることて、ある。このため , stream buf の派生クラスを作成することて、 ( 入出力 装置と直接的に結びついている ) , これらの 関数 (overflow, underflow) を容易に組み 替えることがて、きる。 ■ストリーム入出力 入出力ストリームクラス (ostream, istream) は , 前述の streambuf クラスをと おして実際の入出力を行う。たとえば cout くく 125 ; のように , ostream を用いて出力す るときの処理の流れは次のようになる。 1 : ostream 内の処理 書式の処理 ( たとえば , 整数値 ( 123 ) を文 字列表現に展開する ) などを行い , メンバ変 数 ostream::bp が指す strembuf 型変数に sputc メンバ関数などを用いて展開後の文字 データ列を送る。 2 : streambuf 内の処理 メンバ関数 s uts などによって送られてき た文字データをメンバ変数 base が指すバッ ファ上に格納する。もし , バッフアがいつば いになったら , メンバ ( バーチャル ) 関数 over 80 CMAGAZINE 19 1 LiSt 3 base ) ) = 0 ) return( EOF ) : if( ( length = strlen( / / バッファ上の文字列の最後まで有効 ※② pptr = base 十 length; i f ( pptr 〉 eptr ) pptr = eptr : / / バッフアをはみ出たデータは無効 / / 「 EOF ではない」という情報を返す return ( *base ) : 20 : 22 : 23 : 24 : } 25 : 26 : char* stringbuf: :str() ・ Y0 ・ : / / 有効なデータの最後にターミネータ ' \ 0 ・を付ける 28 : *pptr return ( base ) : 29 : 30 : } List 4 1 : / / ⅱ st 4 #include ” sstrean. h ” 2 : 3 : #include く co 田 ex. h> 4 : #include く string. h> 5 : 6 : 7 : void testl() 9 : cout くく " 文字列 ( in ー buf ) からの入力 \ n " ・ cout くく 12 : ” 123 abc ( 1.012 , 3. 14 ) " ・ 13 : buf Char *in ”文字列 (in-buf)=Y"" くく in-buf くく” \ ” \ n ” cout くく stringbuf sbin( in-buf ) : istream sin( &sbin ) : 17 : int intnun; / / int 値の入力 20 : S i n > > i ntnum : char stringC100]; / / 文字列の入力 22 : Sin > > string; 23 : complex complexnum: / / 複素数値の入力 24 : S i n > > conp ー exn um : 25 : cout くく " 文字列 (out-buf) への出力 Yn": 26 : 27 : 28 : char out-buf [ 256 ] : 29 : stringbuf sbout( out-buf. sizeof( out buf ) ) : 30 : sout ( &sbout ) : ostream 32 : 33 : 34 : 35 : 36 : 38 : 39 : } 40 : 41 : void test2() 42 : { 43 : cout くく char buf[100] : 44 : 45 : cout くく "CON から読み込み , 文字列に格納します ( 最後は - Z を入力して下さい ) \ n " 46 : stringbuf sbout( buf, 100 ) : ostrean sout ( &sbout ) : 48 : 49 : filebuf fp; fp. open( "CON", / / ファイルバッフアの出力 sout くく fp; 52 : cout くく " 文字列を cout に出力します \ n " cout くく sbout. str() : / / 文字列の出力 54 : 55 : cout くく " 文字列バッフアを cout に出力します \ n " : 56 : stringbuf sbin( buf ) : 57 : / / 文字列パッフアからの出力 58 : cout くく sbin; 60 : 61 : void main() testl() : test2() : 64 : ※④ ⑨ ⑤⑥⑦⑧ カ 出出の のの値 値列数 数字素 整文複 くく くく + 》 E 0 十》 0 く ” i nt num i S sout くく ” string is \ " complex num iS " 文字列 (out-buf)=Y"" cout くく input ) : ※⑩ ※⑩ ※⑩
タて構築された直後の stringbuf クラス変数 buf) を用いたサンプルプログラムを示す 能をサポートしてくれる。よって , complex は , バッフアが空の状態て、あるから , 最初 型の入出力のように , ユーザ定義クラス X (List5 は ,IDEA C 十十用のプロジェクトフ に対し , 次のようなオペレータ関数を定義 の入力が起きたとき , ま iunderflow が呼 ァイル ) 。 するだけて、 , 書式っき入出力が可能になる。 び出される。このとき (buf is valid! = まず , List4- ①のような文字列 in buf を 0 ) , バッフアの内部のデータを有効に ( List3 ostream& operator くく ( ogtream& , 用意し , その文字列て、 , stringbuf 型の変数 ー② ) してやれば , 以降このバッファ ( 文字列 ) sbin を構築する (List4- ② )。次にその sbin istream& operator>> ( istream&, から入力を行ってくれる。次にこのバッフ によって , istream 型の変数 strin を構築す アが空になったとき , 再び underflow が呼 る (List4- ③ ) 。これて、 , strin に対する入力 び出されるが , 文字バッファクラス ( string (operator 〉〉 ) などの操作は , すべて文字列 また , stringbuf は , 基本クラスの stream buf の機能をそのまま継承しているのて、 , buf) は入力装置をもたないため , このと in buf に対して行えるようになる (List4- き , EOF を返せばよい (List3- ③ )。 List4-@のように , ostream : : operator ④ ) 。 文字列への出力として stringbuf を用いた くく (const streambuf&) によって , ノヾッフ 次に , List4- ⑤のような文字列 out buf を 士白 / 、ヾ アの内容を出力することもて、きる ( ただし 万ロ , ハッファ上の文字列の最後にター 用いて , stringbuf 型変数 sbout(List4- List4- ⑩によっても同じ結果を得ることがて、 ネータ、、¥ O 〃がつかない。メンノヾ関数 string ⑥ ) , ostream 型変数 strout(List4- ⑦ ) を構 buf: :str( ) はバッファ上の文字列データにタ 築すると , strout に対する出力 (operator きる ) 。 この stringbuf は , 実メモリ上の文字列バ ミネータを付加する。 くく ) などの操作は , すべて文字列 out buf に ッフアを , 入出力装置と同様にストリーム 対して行われる (List4- ⑧ ) 。 文字リバッファクラス として使用することを可能にする。たとえ 以上のように , ostream, istream は , そ stri gbuf を用いたサンプル ば , List6 のようなモジュールをアプリケー れが streambuf 型て、本冓築されようが , strin gbuf 型て、構築されようが , まったく同じ機 ションプログラムにリンクするだけて , そ List4 に , 文字列ノヾッファクラス (string のプログラム内のすべての標準出力 ( cout ) を文字列バッファ cout buf 上に展開し , す べての標準入力 (cin) を文字列 cin buf から 行うことがて、きる。 サン ル 2 インドウマネージャ ) 最後に , 書籍『 C 十十プログラミング』 ( 日 本ソフトバンク発行 ) のサンプルプログラム て、ある「ミニウインドウマネージャ」 ( 注 ) を用 いたサンプルを示す。このウインドウマネ ージャは , window. h て、宣言されている。そ の概要を List7 に示す。このクラスライプラ リは次のようなクラスて、構成される。 ・ window ウインドウを表現する抽象クラス。 - ユ第 - ー ザが直接利用することはない ・ board フレーム ( 枠 ) をもたないウインドウ。 ・ box フレームをもつウインドウ。 ・ centerbox フレームをもっウインドウ。ウインドウ への文字列出力がセンタリングされる点が box クラスとは異なる。 List 8 1 : / / list 8 ウインドウバッファクラス 2 : 3 : #ifndef WSTREAMH 4 : #def ine WSTREAMH 5 : 6 : #inciude く stream. h> 7 : #include ” window. h ” 8 : 9 : class winaowbuf public streambuf / / オーバーラップを自動制御する時 , 非 0 static char overlap; / / 対応するウインドウ wind0W* wp; pub ⅱ c : int overflow( int c=EOF ) : 〃バッフアからのデータ書き出し 13 : int underflow() : / / バッフアへのデータ読み込み 14 : int set_overlap( int ) : / / オーバーラップモードの設定 / / コンストラクタ windowbuf( window* p ) { wp windowbuf( window* p, int ー 18 : 20 : 21 : #endif Li3t 9 1 : / / list9 ウインドウバッファクラス 2 : #include "wstream. h ” 3 : 4 : 5 : int windowbuf: :overflow( int c ) i f ( overl ap ) wp->act i ve ( ) : / / ウインドウをアクテイプにする 7 : 8 : char IastchrsC3] : 9 : char * ゆ = lastchrs; 10 : / / *gptr . を出力 . *(pptr-l) 12 : if( gptr ! ニ pptr ) { 利 p 十十 = * ( pptr- 1 〃有効なデータを持つ時 82 CMAGAZINE 1990 1
1 : 22 : / * 23 : / * 28 : { 63 : / * 64 : / * Fig. 1 SDF ファイルから差し込みファイルへの List 1 SDF ファイルを差し込みファイルへ変換 sdftodm eftodm フィールド長入力 SDF ファイルオープン 終わり SDF ファイルクローズ 差し込みファイルクローズ データ変換 差し込みファイル作成 変換 ( その 1 ) fopen fopen convert fclose fclos e SDF 6 : 14 : 15 : 16 : 17 : 18 : 20 : 24 : 25 : 26 : 27 : 31 : 32 : 33 : 34 : 35 . 36 : 37 : 38 : 39 : 40 : 42 : 43 : 44 : 45 : 46 : 48 : 49 : 52 : 53 : 54 : 55 : 56 : 57 : 58 : 59 : 60 : 62 : 65 : 66 : 68 : 74 : 76 : 77 : 78 : BUF_SIZ 12 : #define 10 : #include く string. h> 9 : #include く stdlib. h> 8 : #include く dos. h> 7 : #include く stdio. h> dm 1024 Char int int bufCBUF_SIZ] : fi 引 n [ 50 ] : cnvert(FILE *sdf. FILE *dm, int flncnt) : ma ー n sd ftodm VOid main(int argc,char * *arg の int argc : Char * *argv; *sdf, *dm, *fopen ( ) : strC10] : flncnt, convert() : 引数の数 * / 引数 * / SDF ファイルの差し込みファイルへの 29 : F ILE 30 : Char int uns igned int reccnt; convert レコド長を算出 SDF ファイル 1 レコード読み込み レコードの終わりに NULL をセット 1 フィールドを転送 変換 ( その 2 ) fgets strncpy printf("YnSDF file ー > 差し込み file converter verl. 00 (C)Copyright Y. TYnYn") : i f(argc! ニ 3 ) printf("A>sdftodm SDP ファイル名差し込みファイル名 [RET]Yn"); else printf ( " フィールド長を指定してください。 RET のみで変換実行。 Yn"); for(flncnt=0;flncnt く 50;flncnt + + ) / * フィールド長を指定 * / printf(" 第 %u フィールド長 : ",flncnt 十 1): gets (str) : fieldln[flncnt]=atoi (str) : if(fieldln[flncnt]==0) break : dm=fopen (*(argv + 2) , "w") : sdf=fopen ( * (argv + 1), "r") : if (sdf==NU しい pr i ntf ("Yn S D F ファイルをオープンできません。 *(argv + 1)) : else reccnt=cnvert (sdf, 面 , flncnt) : / * 変換本体 ファイル名 : XsYn ” レコードデータへのポインタを設定 EOF コード書き出し Yes EOF 1 プロック書き出し NULL を設定 Yes フィールド終わり No 復帰改行コードを設定 fclose(dm) : fclose(sdf) : printf("YnXd レコードを変換しました。 Yn",reccnt); fputs fputc cnvert(sdf, dm, flncnt) int cnvert(FILE *sdf, FILE *dm, int flncnt) int i,J,recln.rc; 69 : char *sdfptr,*dmptr; 70 : char workCBUF_SIZ) : rc=0; for(i=0. recln=0;i<=fIncnt;i + + ) recln + =fieldln[i]; while(fgets(buf. BUF-SIZ-I, sdf) !=NU し L) buf [recln] =NU しし : / * レコ ード長を得る for ( j : 0. sdfptr=&buf [ 0 ] , dmptr=&work [ 0 ] ;j く flncnt : j + + ) 100 CMAGAZINE 復帰 19 1
Fig. 1 List 2 Streambuf クラスの管理するバッファ バッファリング用の メモリ領域 有効なデータ 領域 charC] streambuf:. ( 固定 ) ・ base ↓高アドレス streambuf::eptr streambuf::pptr streambuf::gptr ・ ( 固定 ) 4 : 5 : 6 : 7 : 8 : 9 : 15 : 1 : / / list 2 文字列バッファクラス 2 : #ifndef SSTREAMH 3 : #define SSTREAMH #include く stream. h> #include く string. h> : public streambuf { class stringbuf char buf_is_valid: / / str i ngbu f が入力用として用いられる場合で、 / / ハ・ツフアの内容が有効な時、 0 public: int overflow( int c =EOF ) : int underflow() : char* str() : stringbuf( char* s. int len ) : ( s, 厄 n - 1 ) stringbuf( char* s ) : ( s, strlen(s) ) { buf_is valid { buf_ls_valid 19 : #endif L 、 ist 3 int stringbuf::overflow( int c #include く string. h> 3 : #include ” sstream. h ” 1 : / / list 3 文字列バッファクラス 2 : 4 : 5 : 6 : 8 : return( EOP ) : / / 常に . 工ラ int stringbuf: :underflow() if( !buf_is_valid ) return( EOF ) : / / 工ラー ( E 0 F である ) ※① ※③ コンストラクタによって構築された直後は、パッフアが空の状態になっている pptr にパッファ上の有効なデータ領域の最後を指させて、バッファ上のデータ を有効なものとする。 ( 但し、データの長さが 0 の時は EOF) buf_is_valid length; int ・ streambuf クラス streambuf クラスは , 入出力装置とメモ リ上のバッファ間のバッファリングを制御 するクラスて、ある。 streambuf クラスが管理するノヾッフアは Fig. 1 のようになる。メンノヾ変数 base, gptr, pptr, eptr の 4 つのポインタがノヾッ フアを管理する。 base, eptr がノヾッファリ ング領域の先端と終端をポイントし , gptr,pptr がノヾッフア内の有効なデータが つまっている領域の先端と終端をポイント する。バッフアに有効なデータが存在しな い ( 「バッフアが空」の状態 ) とき , gptr= pptr となる。 ただし , streambuf クラスがノヾッフアを もたない ( バッファリングを行わない ) とき , base=eptr=gptr=pptr=O て、ある。 次に , 主要なメンバ関数の仕様を示す。 ・ int streambuf::overflow( int c ) く要約〉 バッフア内の有効なデータを出 カ装置に書き出し , バッフアを空の状態 (gptr= =pptr) にする。 く引数〉 c が EOF て、ないとき , c にはバッ フアに収まりきらなかった最後の 1 文字が入 っている。この場合はこの 1 文字 c も出力し なければならない く戻り値〉正常に処理て、きたとき O を , 出 力に失敗するなどのエラーが生じたとき EOF を返す。 ・ int streambuf::underflow() く要約〉入力装置からデータを読み込み バッフアにつめ , バッファ上の有効なデー タ領域を gptr , pptr て、ポイントする。 く戻り値〉正常に処理て、きたとき次の 1 文 字 , すなわち * gptr を返す。入力に失敗し たり , 入力装置が End Of File て、あるとき EOF を返す。 第 2 特集実践 C + + プログラミング 79 ータをつめてから文字データを読み出す。 ノヾッフアに文字デ underflow を呼び出し , 出す。もしバッフアが空ならメンバ関数 バッフアから文字データを読み く要約〉 ・ int snextc()
2 : 4 : 5 : 7 : 8 : 9 : 10 : 12 : 13 : 14 : 3 : # i ncl ude 6 : #define INCORE-STREAM し ENGTH List 5 ーフ・ン centerbox( int xw, int yw, const char* t= 0 , cell 作 0 ) : / / コンストラクタ list 5 文字列バッファクラスのサンプルプログラム 2 : 3 : sstream. C sample. C List 6 ” sstream. 1 : / / ⅱ st 6 標準入出力ストリー ム cout,cin の付け替え 10000 / / cout 用のパッフアの大きさ Char stringbuf ostream Char stringbuf i stream List 7 cout-buf[ INCORE_STREAM し ENGTH ] cout-sb ( cout-buf, INCORE-STREAMLENGTH ) : cout( &cout-sb ) : cin-buf[] cin-sb( cin-buf ) : cin( &cin-sb, 1 , / / 入力すべき文字列 &cout ) : 2 : 3 : 4 : 7 : 8 : 9 : nn : nn : nn : nn : nn : nn : nn : 1 : / / list 7 window. h の概要 5 : / / ウインドウの抽象クラス 6 : class window { public: window() : ⅵ ndow ( ) : typedef unsigned short cell;/* テキスト VRAM の 1 文字 ( フレームの種類指定 ) * / open( int & int Y ) : ⅵ rtua 1 VO i d active() : virtual VOid get( char* buf, int n ) : virtual int V irtua ー W indOW& operator くく ( const char* S nn : / / フレーム ( 枠 ) を持たないウインドウ ・ public window { nn: class board public: / / コンストラクタ / / テ・イストラクタ / / ( x , Y ) にウイント・ウをオ / / アクティフ・化 / / 文字列入力 ) : / / 文字列出力 board( int xw, int YW ) : / / コンストラクタ ( 大きさ xw. xy のウイント・ウを構築 ) / / テ・イストラクタ board ( ) : open( int x, int Y ) : VO i d get( char* buf. int n ) : int window& operator くく ( const char* s ) : / / ( x , y ) に ) イント・ウをオーフ・ン / / 文字列入力 / / 文字列出力 nn : / / フレームを持っウインドウ nn: class box : public board { public: box( int xw, int yw, const char* cell f=0) : / / コンストラクタ nn: / / フレームを持っウインドウ ( センタリング出力 ) nn: class centerbox : public bOX { public: centerbox() : / / 大きさ (xw,yw) / / 大きさ (xw,yw) タイトル t フトムの種類 f のウイント・ウを構築 / / テ・イストラクタ タイトル t フレームの種類 f のウイント・ウを構築 window& operator くく ( const char* s ) : / / テ・イストラクタ / / 文字列出力 flow を呼び出し , バッフアを空にしてから データを格納する。 istream を用いた入力は , この反対と考え ればよい。すなわち , 次のようになる。 1 : istream 内の処理 メンバ変数 bp が指す strembuf 型変数に snextc メンバ関数などを用いて文字データ 列を獲得し書式の処理 ( たとえば , 文字列 表現を整数値に変換する ) などを行う。 2 : streambuf 内の処理 メン六関数 snextc などによって文字デー タが要求されたとき , メンバ変数 base が指 すバッファ上のデータを返す。もしバッフ アが空のときは , メンバ ( バーチャル ) 関数 underflow を呼び出し , バッフアにデータ をつめてからデータを読み出す。 リバッファクラス stri gbuf の作成 文字列へのフォーマットつき入出力 ( C 言 語の sprintf, sscanf) を行うため , strea mbuf の派生クラスて、ある stringbuf を作成 する。実現の方法として , 「基本クラス (streambuf) のノヾッファリングの機能を , 文字列へのデータ埋め込み / 読み出しに使用 する」 , つまり「文字列領域をバッフアと共 有して streambuf の機能を利用する」方法を とる (List2,3)0 文字列への出力の場合 , 基本クラス (streambuf) の機能によって , メンバ変数 streambuf: : base の孑旨すノヾッファ上に文字 第 2 特集実践 C 十 + プログラミング 81 : underflow が呼び出される。コンストラク が空になったとき , メンバ関数 streambuf: ら , データを取り出してくれる。バッファ streambuf: : base が孑旨すノヾッフア領域か (streambuf) の機能によって , メンバ変数 文字列からの入力の場合 , 基本クラス 情報 (EOF) を返せばよい (List3- ① ) 。 め , メンノヾ関数 overflow は , つねにエラー クラス ( stringbuf ) は出力装置をもたないた overflow が呼び出される。文字列バッファ ばいになったとき , メンバ関数 stringbuf: ・ 列を展開してくれる。そしてバッフアがいっ
lnformation from ( ompl 「 ma 「 List 3 カンマ区切り出力の例 (long の場合 ) ます。また , オンラインサインア ップも行っております。皆さんの 積極的な参加をお待ちしておりま す ( 差分ファイル公開以後 , つなが りにくい状況が続き , ご迷惑をお かけしております ) 。 TYMPAS の接続方法ー お近くの TYMPAS のセンターに 接続します。画面に , Please 10g in : と表示されますのて、 , く tympas のユーザネーム〉 : 777 く tympas の /ヾスワード〉 23 : } 26 : { printf("YYXsYn", camma ( 12345 し ) ) : 、フ 0 どうすればファイルの大きさ を小さくすることができますか ? A chsize を使ってください chsize は , すて、にあるファイルの 大きさを自由に変更することがて、 きます。 chsize には , ファイルハ ンドルと変更したい大きさを与え ます。ファイルハンドルは , 書き 込み可能て、なければなりません。 ファイルを大きくする場合は , 増 えた領域は 0 て、埋められます。 0 プログラムから呼び出す子プ ロセスの入出力をファイルにリダ イレクトしたい場合 , どうすれば いいですか ? A MS ー DOS て、は標準て、 5 つのフ ァイルハンドルが用意されていま すが , これらは子プロセスを呼び 出す前に , 別のファイルに置き換 えてオープンすることて、 , 子プロ セスの入出力をリダイレクトする ことがてきます。 たとえば , 子プロセスの標準出 力をリダイレクトするためには List2 のようにプログラムします。 この方法を使って , 標準入出力 以外にも標準ェラー出力や標準プ リンタ出力などをリダイレクトさ せることがてきます。 0 数値を 3 桁ずっカンマで区切っ て出力することはできますか ? A 標準ライプラリの中には , そ 2 : 4 : 5 : 7 : 8 : 9 : 10 : 12 : 13 : 15 : 17 : 20 : 22 : 24 : 1 : / * カンマ区切り出力の例 0 ong の場合 ) * / 3 : #include く stdio. h> char *camma (long X) static char buf[20) : i nt i . n : long bs; for (n 1 し : n く 9 & & x/bs > 9 : n + + , bs * = 1 0 し ) do { buf[i + + ] (x/bs) *bs; X i f (nX3 = の buf [ i + + ] return buf; buf[i-l] } while (n--); bs / = 10 し : と入力してください。 こて表示 される BBS の一覧表から「 MSA ー NET 」を選択してください Q@A 0 環境変数 TZ を , JST ー 9 以外に 設定しても , time と localtime を使 って得られる時刻が変わらないの はなぜですか ? A MS-DOS て、得られる時刻は地 方時間て、すから , time 関数は環境 変数 TZ から時差を求め , グリニッ ジ標準時 ( GMT ) からの経過時間 ( 秒 ) を返します。 localtime は環境 変数 TZ を使って再び地方時間に変 換しますから , 結果として得られ る値は MS - DOS て得られる値と同 じになります。 たとえば , システムを日本標準 時 (JST) とし , 同じ時点て、の太平洋 標準時 (PST) における時間を取得 する場合は , List1 のようにしま す。 25 : main() しく動作するようになります。 て日付 , 時刻を設定することて正 この場合は , 改めて DATE,TIME ているかどうか確認してください し , カレンダ時計が正しく動作し て , DATE, TIME コマンドを実行 MS-DOS のコマンドライン A く動作していないようです。 0 me 関数や sleep 関数が正し のような使い方はて、きません ) 。 s. -*n", camma(x), camma(y)) ; たがって , printf("**%s. ー\\% 出されるたびに破壊されます。し れるポインタの示す内容は , 呼び 合 ) を示しておきますにこて返さ List3 にプログラム例 ( long の場 なければなりません。 このときは , 専用の関数をつくら うした関数は用意されていません。 PC ー 9801 シリーズて、は , 割り込み 関数の中からカレンタ・時計 BIOS を 呼び出すと , 偶然カレンダ時計 BIOS の処理中に割り込みがかかっ た場合にこのような症状になるこ とがあります。 0 ンク中に warning : duplicate in module ・ うメッセージが表示されます。 warn duplicate symbols スイッチが on になっています。ス タックサイズ ( stklen) やヒープサ イズ ( heaplen ) など , 故意にライ プラリ中のシンポル名と同じもの を使った場合てなければ , その関 数名が別のライプラリ関数から呼 び出されている場合など , 正しく A 元の関数が呼び出されないい 問題が生じます。 lnformation from Compiler Maker 137
1 List case 0x3c:/* ← -key for my KEY 本 / if ( *X putchar( 0X07 ) ; else if ( *X - else { while ( c 「 t [ ネ y ] [ ← x ) * 25 ー 8 ] ー break; case Ox0c:/* → -key り case Ox3e:/* → -key for my KEY * / if ( crt い y ] [ い x ) 事 25 + 17 ] ! = else { putchar( 0X07 ) : break; case 0x0d: return( 1 ) : case OXla: return( 2 ) : dir->level; if ( dir->level く 0 ) dir->level if ( dir->br - ニ 0 Ⅱ dir->br - strcpy( spc[dir->level], else { strcpy( spc[dir->level], if ( dir->br - = 0 Ⅱ dir->br x = dir->level; branch[dir->br), 2 ) : strnchg( &(crtCline]Cx*25-16]), crt[lineJ[x*25-9] dir- 〉 name, 11 ) ; strnchg( &(crt[line]Cx*25 crt[Iine][x*25 + 3] else ( + + line; x = dir->level; f0 「 ( j = 0 ; j く MAXWIDTH; crt[line][j] = crtCIine][MAXWIDTH] : ' \ 0 ' ・ f0 「 ( j = 1 : j ← 5 ; j + + ) strnchg( &(crtCline]Cj*25-21J), ” \ xlb [ 32m ” ” \ xlb [ 37m " , 5 ) : strnchg( &(crtCline]Cj*25-14]), ” \ xlb [ 37m ” , 5 ) : strnchg( &(crt[line][j*25-21]), for ( j = 1 ; j く x; j + + ) { strnchg( &(crtCline][j*25-16]), spcCj], 2 ) : strnchg( &(crtCline]Cx*25-16]), branchCdir->br], 2 ) ; crtCIine)[x*25-9] strnchg( &(crt[Iine][x*25-8]), dir->name, 11 ) : crtCline]Cx*25 + 3] 294 : 295 : 296 : 297 : 298 : 299 : 300 : 301 : 302 : 303 : 304 : 305 : 306 : 307 : 308 : 309 : 310 : 311 : 312 : 313 : 315 : 316 : 317 : 318 : 319 : 320 : 321 : 322 : 323 : 324 : return( 0 ) : 325 : 326 327 : 328 : / 事移動先のパスの取得 * / char *path ) VOid get—path( int X, int y, 329 : 330. 331 : int i. J; char sCMAXWIDTH + 1] : 332 : 333 : sprintf( s, ” Xc:Xc ” + DTA. drv - 1 , (x = の ? 334 : if ( x = 335 : strncpy( path, S, 4 ) : 336 : 337 : retur n : 338 : 339 : 340 : 341 : sCMAXWIDTH] = 342 : fo 「 ( i 343 : s [ i * 25 + 16 ] 344 : strnchg( & ( s [ i 本 25 + 17 ] ) , & ( crt [ y ] [ i 事 25 + 17 ] ) , 11 ) : 345 : while ( crtCy]Ci*25-8] 346 : 347 : 348 : 349 : f0 「 ( i = 0 , j 350 : 351 : path[j + + ] = 352 : 353 : 354 : 355 : 356. 357 : 358 : なディレクトリの移動先を選択 & 移動 * / int select( int int line ) 359 : 360 char *tOk, pathC80], s [ 6 ] [ 12 ] , *Stat; 361 : int J' YY' level' rtn; 362 : 363 : 364 : カーソル位置取得り if ( line > 23 ) ( 365 : 366 : 367 : else { 368 : get-pos( &x, &Y ) ; 369 : y0 : y ー 1 i ne : 370 : 371 : 1 eve 1 372 : if ( chg = = 0 ) { 373 : / * カレントディレクトリ取得 * / 374 : stat = getcwd( path' sizeof(path) ) : 375 : strcpy( s [ le I + + ] , strtok( path' yen ) ) : 376 : while( (t0k : strtok( NULL, yen ) ) ! = 0 ) { 377 : strcpy( s[level + + ], t0k ) : 378 : 379 : / * カレントディレクトリ表示位置検索 * / 380 : 381 : 1 eve ト if ( level ! = 0 ) { 382 : dir = first—>next; 383 : for ( i 384 : こ 2 Ⅱ dir->br i f ( d i に >br = 385 : 386 : 十十 y : if ( strcmpi( sCjJ, dir->name ) 387 : = 1 eve 1 ) ( 388 : x = dir->level; 389 : break; 390 : 196 : 197 : 198 : 199 : 200 : 201 : 202 : 203 : 204 : 205 : 206 : 207 : 208 : 209 : 210 : 211 : 212 : 213 : 214 : 215 : 216 : 217 : 218 : 219 : 220 : 221 : 222 : 223 : 224 : 225 : 226 : 227 : 228 : 229 : 230 : line 十十 : return( line ) : 231 : 232 : ) 233 : 234 : / 事力ーソル位置の取得 ( 工スケープシーケンスによる ) void get—pos( int *X, int *Y ) 235 : 236 : ( int i; 237 : char bufC8], c; / 事 ESC + ” [yy;xxR ” * / 238 : 239 : printf( ” \ xlb [ 6n ” ) : 240 : getch(); 241 : fo 「 ( i = 0 : i く 7 : i + + ) { 242 : buf[i] = (char) getch() ; 243 : 244 : sscanf( buf, ” XcX2dXcX2d ” , &c, Y, 245 : 246. 247 : 248 : / ネ矢印キーによるカーソルの移動り int move( int *X, int *Y' int line 249 : 250 : ( i nt c, 100P , yy : 251 : 252 : 253 : 100P : 0 : while ( 100P - 254 : c = getch() : 255 : 256 : 100P switch( c ) { 257 : 258 : default: 100P : 0 ; 259 : 260 : break; case Ox0a:/* ↓ -key * / 261 : case Ox5f:/* ↓ -key f0 「 my KEY * / 262 : i f ( *x 263 : putchar( 0X07 ) : 264 : 265 : break; 266 : 267 : YY while ( crtCyy]C(*x)*25-8] = 268 : 269 : yy 十十 : 270 : if ( yy く line ) ( 271 : 272 : *Y = yy : 273 : else ( 274 : putchar( 0X07 ) : 275 : 276 : 277 : break; 278 : case 0x0b : / 事↑ -key * / 279 : case 0x5e:/* ↑ -key f0 「 my KEY 本 / 280 : 281 : while ( crtCyy][(*x)*25-8J : 282 : 283 : 284 : if ( yy > : 0 ) ( 285 : 286 : *Y = YY : 287 : else ( 288 : putchar( 0X07 ) : 289 : 290 : 291 : break; 292 : case 0X08 : / 事← -key り 293 : & & *x く 5 ) ( fo 「 ( i = 3 ; i く MAXWIDTH; ニ 0 ) ( pathCj] = く line yy i く n; i + + , dir ニ dir->next ) ( 0 & & dir->level CMAGAZINE 132 19 1
実際の手段としては , 書き出し用のバッ フアを空白と NULL コードて、初期化してか ら , 入力文字数分だけ転送します。 strcpy 関数を使うと , 入力されたデータから NULL コードも転送してしまうのて、要注意。 データの書き出し データの書き出しは , フィールドご 行います。このとき , fputs 関数は復帰改行 文字を自動的にセットするわけて、はありま せん。全フィールドを書き出したら , レコ ード区切り記号として , 復帰改行文字を書 き出します。 なお削除マーク用のエリアを別途設ける 場合には , その箇所も書き出しておきます。 EOF コード書き出し レコードデータと復帰改行文字を書き出 したら , EOF コード (Ox1a) を書き出しま す。これがないと , SDF ファイルをエディ タなどて、編集する際に困ることがあります (Fig. 13)。 応用 レコード追加の応用として , ファイルポ インタを最終レコードの次て、はなく , 存在 するレコードに位置づけることて、 , すて、に 存在するデータの更新 ( 修正 ) がて、きます。 1 レコードを更新しても , ファイル全体を 書き換えることなく , 1 レコードのみの書き 全レコードの表示ー a ″ d / s ロー にしたほうがよいてしよう。 タを表示して , 一部の文字を修正するよう ポード入力関数を自作して , 存在するデー なお , 修正する際には , 高レベルなキー は書き出しません ) 。 出してすみます ( 修正する場合は EOF コード ポインタも表示するようにプログラムしま 確認のため , 各レコードごとにファイル データを読み込んて、表示します。 つまり , ファイルの先頭レコードから順次 シーケンシャルアクセス風に行うものてす。 全レコード表示は , ランダムアクセスを データファイルオープン した (List5) 。 08 CMAGAZINE 19 囲 1 1 レコード表示とまったく同じように行い ます。 データ表示 1 レコード表示と同じように disprec 関数 を使用します。違うのは , 1 レコード目から 最終レコードまて、を順次アクセスして表示 Fig. 12 文字列の正規化 することて、す。 これて、 , ファイルアクセスの概要を理解 していただけたかと思います。 ぜひともサンプルプログラムをいろいろ 改造して , 試してみてください フィールド長 6 バイト 6 0 0 0X20 0X00 List 5 1 : 3 : 6 : 7 : 8 : 9 : 10 : 20 : 22 : 25 : 26 : 28 : 29 : 30 : int stat, i,readln; long int fileptr; 23 : void disprec(FILE *datfile• long recno) disprec fclose(datfile) : d isprec (datfile, recno) : printf(" 第 %d レコード Yn",recno); for(recno=l ;recno く =mng. reccnt ;recno 十十 ) SDF ファイルへの DB 的アクセス ( 全レコードの表示 ) a Ⅱ d i sp ( ) 4 : vo i d a Ⅱ d i sp ( vo i d ) long int recno; FILE *datfile; datfile=datopen ("rt") : if(datfile!=NU しい 32 : 33 : 34 : 35 : 36 : 37 : 38 : 39 : 40 : 42 : 43 : } fileptr=mng. recln*(recno-l) : printf(" ファイルポインタ =%dYn",fileptr): stat=fseek(datfi le, fileptr, SEEK-SET) : if(stat!=NU しい pr i nt f ( " ファイルポインタ工ラー " ) : for(i=0;i く mng. fldcnt;i + + ) readln=mng. flntblCiJ + 1: if(fgets(buf,readln. datfile)==NU しい pr i ntf ( " リードエラー \ n" ) : printf("%syn",buf); printf("Yn"); e ー S e
レコード表示するには , ランダムアクセ スて、行います。ランダムアクセスするため , まず指定したレコードのファイルポインタ を設定します。ファイルポインタの設定に は , fseek 関数を使用します。 fseek 関数は , ファイルの先頭からのオフ セットを与えます。このため , ファイルポ インタを , レコード番号とレコード長を元 に計算します。 計算時の注意として , ファイルポインタ はオフセットて、表すという点て、す。つまり , ファイルの先頭のオフセットは , ゼロて、あ ることて、す。実際に計算する場合には , レ コード長 x ( レコード数ー 1 ) て、計算します 23 : } 28 : { 52 : { するモードになってしまいます。これて、は ンしてしまうと , 「 EOF コードの後へ」追加 注意すべき点は , 「追加用」としてオープ ドが異なります。 いくら追加しても , 必ず読み込みのときに fgets 関数て、 EOF コードによってエラーにな ってしまいます。よしんば ,fgets 関数が EOF コードをチェックしていなくても , 追加さ SDF ファイルへの DB 的アクセス ( n 番目のレコードを表示 ) List 3 7 : 6 : 3 : 1 : 9 : F I LE *datf ⅱ e : 8 : char str[20] : int fseek() : long int recno; 4 : void dispdata( void ) dispdata (Fig. 7 ) 。 終わりにアクセスしてしまうのて、注意して 数に与えたモードによっては , ファイルの ます。また , ファイルオープン時に fopen 関 イルの先頭からのアクセスとなってしまい ファイルポインタをセットしないと , ファ 与えてファイルポインタをセットします。 これによって算出した結果を fseek 関数に ください データファイルに 1 レコードのデータを追 レコードの追加ー a ロ nd 「 ec イト数十 1 」て、あることに注意してください 場合 , 引数に与える値は , 「読み込みたいバ イト数を引数として与えます。なお , その NULL コードなどがないのて、 , 読み込むバ フィールドの終わりに復帰改行コードや fgets 関数て、行います。 SDF ファイルて、は , データの読み込みは , フィールド , 加します ( 107 第 , Fig. 8)。 フローチャートとリストを , Fig. 9 と List4 ファイル ( db. mng ) へ書き込みます。 理テープルのレコード数を更新して , 管理 ープをかけてください。追加した後は , 管 データを連続して追加する場合には , ル に示します。 104 CMAGAZINE 19 1 datopen 関数を使用しますが , オープンモー す。 1 レコード表示のときと同じように 追加用にデータファイルをオープンしま データファイルオープン 14 : 18 : 19 : 20 : 22 : 24 : 26 : 29 : 30 : 32 : 33 : 34 : 35 : 36 : 38 : 39 : 40 : 42 : 43 : 44 : 45 : 46 : 48 : 50 : 55 : 59 : 60 : 62 : 63 : 64 : 65 : 66 : datfile=datopen("rt") : if(datfile!=NU しい pr i nt f ( " 表示するレコード番号を指定してください。 YnYn " ) : gets(str) : recno=atoi (str) : if(recno>mng. reccnt) printf(" レコード数は , %d です。 Yn",mng. reccnt); fclose(datfile) : disprec (datfi le, recno) : e I se disprec 27 : void disprec()I し E *datfile,long recno) long int fileptr; int stat, i, readln; fileptr=mng. rec ln*(recno-l) : pr i ntf ( " ファイルポインタ = %dYn " , f ⅱ eptr) : stat=fseek(datfile, fileptr, SEEK-SET) : if(stat!=NU しい pr intf ( " ファイルポインタ工ラー Yn") : for(i=0;i く mng. fldcnt;i + + ) readln=mng. flntbl[i] + 1: if(fgets(buf. readln,datfiIe)==NU しい pr i ntf ( " リードエラー Yn " ) : printf("XsYn", buf) : printf("Yn"); datopen 51 : F ILE *datopen (char *mode) 53 : char fname[20] : 54 : FILE *file; i nt i : fname[0]=drive; fname[l]= for ( i = 2 : i ← 13 : i + + ) fname[i]=mng. fname[i-2] : fname[i]=NU しし : printf(" ファイル名 =%syn"'fname); file=fopen(fname, mode) : if(file==NU しい pr intf ("Yn データファイルをオープンできません。 " ) : return(file) : e ー se