TabIe 7 ファイルの日付と時刻の取得 dos getftime 関数 ( MS-C 5.1 ) getftime 関数 ( Turbo C 2.0 ) #include く dos. h 〉 unsigned d0S getftime(handle, date, time) 日付と時刻を取得するファイル八ンドル int handle unsigned * date , 最終編集日付 ( Tab 厄 5 参照 ) unsigned *time , 最終編集時刻 ( Tab 厄 4 参照 ) 返り値 = = 0 : 正常終了 = 0 : 工ラー ( MS ー DOS のエラーコード ) #include く iO. h 〉 int getftime(handle, pftime) int handle 日付との時刻を取得するファイル八ンドル struct ftime*pftime , ファイルの日付と時刻 struct ftime { unsigned 升 tsec unsigned 升 min unsigned 負 hour unsigned 升 day unsigned ft month unsigned 升 year 0 : 正常終了 ー 1 : 工ラー 秒分時日月年 5 6 5 5 4 「 /. 返り値 = TabIe 8 ファイルの日付と時刻の設定 dos setftime 関数 ( MS-C 5.1 ) setftime 関数 ( Turbo C 2.0 ) #include く dos. h 〉 unsigned dOS setftime(handle, date, time) int handle 日付と時刻を変更するファイル八ンドル unsigned date , 最終編集日付 ( Tab 厄 5 参照 ) unsigned time , 最終編集時刻 ( Tab 厄 4 参照 ) 返り値 = = 0 : 正常終了 ー = 0 工ラー ( MS ー DOS のエラーコード ) #include く iO. h 〉 int setftime(handle, pftime) int handle 日付との時刻を取得するファイル八ンドル struct ftime*pftime , ファイルの日付と時刻 struct ftime unsigne 升 tsec unsigned 負 min unsigned 負 hour unsigned 負 day unsigned 負 month unsigned 升 year 返り値 = = 0 : 正常終了 ー 1 : 工ラー TabIe 10 setfiletime 関数 ファイルの日付の時刻の設定 int setfiletime(path, date, time) 日付と時刻を変更するファイル名 char* path 最終編集日付 ( Tab 厄 5 参照 ) unsigned date 最終編集時刻 ( Tab 厄 4 参照 ) unsigned time 返り値 = 0 : 正常終了 ー 1 : 工ラー 秒分時日月年 5 6 0 5 4 「 / TabIe 9 getfiletime 関数 ファイルの日付と時刻の取得 int getfiletime(path, date, time) 日付と時刻を取得するファイル名 char* path 最終編集日付 ( Tab 厄 5 参照 ) unsined * date unsigned *time 最終編集時刻 ( Tab 厄 4 参照 ) 返り値 = 0 : 正常終了 ー 1 : 工ラー 90 CMAGAZINE 1991 1
EMS メモリを ワン赭ント 使用 ( 3 ) プログラミング 上田哲二 講座 EMS を使ったメモプログラムを作成します。今回は入 出力インタフェイスは簡素なものとして設計してありま すので , 機能をリストから追うのも簡単だと思います。 今回のプログラムて、は , デフォルトて、 48 K バイト ( 3 ページ ) の配列をアクセスしてい ますが , 起動時のパラメータ指定により , たとえば 2M バイトの EMS ポードを使用して いる人ならば , 2M バイトのデータ配列まて、 前回のサンプルプログラムて、は , 4 ページ をアクセスて、きます。 確保した EMS 領域のうち 2 ページを使用し て , それぞれのページにスケジュールとメ モをひとつずっ , 登録 , 参照 , 削除すると いう機能を実現しました。 しかし , このように物理ページに張り付 今回のプログラムの題材は , 前回と同じ きて、論理ページを使用していたのて、は , 物 理ページ分の領域 ( 通常 4 ページ分て 64K バイ く EMS 領域を利用したメモ帳プログラムて、 す。 EMS 領域は , 当たり前のことて、すが , ト ) しか使用て、きません。そこて、 , 今回は物 アロケートしたら解放命令を出すまて、いつ 理ページ 1 ページに , 複数の論理ページを次々 まて、もアロケートされたままて、います。 に割り当てて使用してみます。 ヘッダファイル emm . h 1 : #define OK 2 : #define NG 3 : #define YES NO 4 : #define 5 : #define EMS_Vect 6 : 7 : 8 : / * 関数プロトタイプ * / em m ( vo i d ) : 9 : int get-status( VO id ) : 10 : int get-pfa( unsigned * ) : int get-pgcnt( uns igned * , uns igned * ) : 1 2 : int page_alloc( unsigned, unsigned * ) ; 13 : int mapping( unsigned, unsigned, unsigned ) : 14 : i nt free_pages( unsigned ) : 15 : i nt 16 : get_version( char * ) : int 17 : save-pages( unsigned ) : int 1 8 : restore_pages ( unsigned ) : int 19 : get-page-map(char far * ) : int 20 : set-page-map(char far * ) : int getset-page-map(char far * , char far * ) : int ・ 22 : int get-sizeof_page-map( int * ) ; 第ⅱ回 List 1 1 0 1 0 0X67 130 CMAGAZINE 1991 1
五ロ 言 用 応 C の道具箱 質問 ヘルプのテキストを表示するの に裏画面を使いたいと思いますが , どのよ うな丁・順て、書き込み呼び出しをすればよい て、しようか。表裏を切り換えるとすればど うするのて、しようか。 回答 この質問の内容だけて、は明らか て、はないのて、すが , あらかじめヘルプ画面 の内容を裏画面に表示しておき必要なとき に瞬時にそれを表示させるという使い方を お考えて、したら , その方法はあまり賢明な 方法とはいえないと思います。なぜなら , あらかじめヘルプ文の大きさが限定されて しまうからて、す。したがって , ディスクな いしメモリからへルプ文を読み込み , 画面 に表示し , 元の画面の内容については裏画 面に一時退避するのが賢明な方法て、す。 メモリからの読み込みについては , 前回 および前々回のマルチウインドウブログラ ムを参考にしてください こて、は表画面 と裏画面の表示切り換えについて解説しま す。 この表示切り換えには , 以ドのようなふ たつの関数を使用すればよいて、しよう。 tou 「 a ( ) 関数は , 表画面に表示されている特 定の領域を裏画面に退避します。また f 「 omu 「 a ( ) 関数は , 退避した裏画面の内容を再び表 画面に表示します。 tou 「 a ( ) 関数 (List 3 ) 指定表示領域の裏画面への書き込み 【引数】 xl, yl, x2, y2 List 2 0 11 り乙 CO 4 ・ L.O ^. 0 ー 8 0 , 1 ワ 3 っ 0 ・ 4 ・ 0 っー 8 0 11 っ 4 ・ 0 《 0 ー 8 0 11 っ乙っ 0 -4 ・ド 0 ^ 0 8 0 ) 0 , 1 っ -4 ・′ 0 ー 8 0 っ 0 っ 0 っ 0 っ 0 っっ 0 っっ 0 っっ 4 ・・ 4 ・ -4 ・ -4 ・ 4 ・ -4 ・・ 4 ・ 4 ・ -4 ・ - -0 -0 - -0 戸 0 ′ 0 -. 0 0 戸 0 《 0 《 0 《 0 《 0 《 0 ^ 0 《 0 ローーーーーーー 0 ーーー 8 ylen = cw->ylenn ー (KEIWIDTD * 2 ) : (unsigned)cw->scroll scro Ⅱ w i d if(scrollwid > SCROLL_LIMIT) scrollwid = SCRO しし一い MIT : if(scrollwid くニ return(-l); (x I en * y I (n) * 2 * s cro Ⅱ w i d x I en * 2 num 1 i n e 1 do { scandir(pluskey) sw i tc h ( i i ) { case CUP: break case RO しし UP: break case CDOWN.• break : case RO し LDOWN: break default: continue i f ( 1 y く 0 ) { continue if ()y > (ylen * scrollwid) ) { (ylen * scrollwid) continue cw—>psavecode 十ニ 1 inel*y cw->psaveattr 十ニ I inel*y wrefresh(cw) : } wh ⅱ e ( i i ! ニ ESCAPE ) : return(0) : toura. C List 3 Copyright 1990 E. TOY0kuni 2 : 3 : #include く stdio. h> 4 : #include く stdlib. h> 5 : #include く DOS. h> 7 : toura( unsigned int xl, unsigned int yl, unsigned int x2, unsigned int y2) 13 : unsigned cpos ニ 0 14 : unsigned vramseg=0xa000 ←第 1 画面のセグメントアドレスを指定 15 : unsigned uraseg = 0Xa100 ←第 2 画面のセグメントアドレスを指定 16 : unsigned int x,y; Number T001900901 * / ← (xl, (l) から (x2, Y 2 ) まて、の領域 【戻り値】 正常終「の場合 0 を返す。座標の指定が表 示可能範囲外の場合には , ー 1 を返す。 【機能】 (xl, (l) から ( x2 , (2) まて、の四角領域の 表示内容を裏画面の同一座標へコヒ。ーする。 応用 c 言語 101
#include "cboxprot. h" #include く stdiO. h> 12 : #include "cboxprot. h" 39 : } scrOII(MWlNDOW *pw) 【引数】 * pw ←ウインドウ関連のパラメータを 格納する構造体のポインタ 【戻り値】 List 1 sc 「 0 ル tst 1 : #define EXTERN 2 : 3 : 5 : 6 : 7 : 1 1 : 14 : 1 7 : 1 9 : 20 : 22 : 23 : 24 : 26 : 27 : 4 : # i nc 1 ude く std ⅱ b. h > # i nc 1 ude #include く dos. h> #include 9 : #include ” dlregs. h ” 10 : #include "pldwn. h" く string. h> く conio. h> # include "window. h ” 正常終「の場合は 0 をそれぞれ返す。 コンイ ) ウインドウ pw を I-. ドにスクロールする。 【機能】 行わすコンパイルのみ行う ) unsigned char にする / リンクは を使用 / 警告レベルは 2 / char を ( ラージメモリモード /C0deView CL /AL /Od /W2 /Zi /J /c 関数 . c ンプログラムにリンクする必要がある。 ァイルをライプラリに追加し , 最後にメイ に設定し , コンパイル後 , オプシェクトフ なのて、 , コンパイルスイッチを以ドのよう なお , 各プログラムは分割コンパイル用 パイルすることがて、きる。 本プログラムは , MS-CVer. 5.1 て、コン 1989 年 Addison-WesIey PubIishing WiIliam James Hunt "The C T001b0X " [ 参考文献 ] 質問へ答 Company, その一部について ) 誌面を借りてお答えする ら , 全部にお答えすることは不可能なのて、 , のて、 , ( もちろん誌面や筆者の時間的制約か うな疑間をもっている方もいるかもしれない えた方もいるが , その内容からして同じよ 間が寄せられるようになってきた。直接答 本連・成も今回て、 7 回Ⅱを迎え , 読者から質 void main(void) 16 : char moji [ 256 ] : char *tmpl 1 8 : M W I N DOW w i n 1 : MWINDOW *wx; i nt i CLS; CUROFF; if((wx ニ subwin(&winl,10,30,5,10)) error ("parameter error") : ex i t ( 0 ) ; CURON; CLS; de 1 w i n ( & ⅵ n 1) : werase(&winl) : wclear(&winl); scroll(&winl); 29 : getch() : wrefresh(&winl) ; 40 : 38 : 36 : 35 : 34 : 33 : 32 : 30 : 28 : List 2 ニ NU し L) { sc 「 0 . c く stdio. h> # i ncl ud e く std ⅱ b. h > く memory. h> #include "pldwn. h" #include "fkey. h" #include "cboxprot. h" / * ウインドウのスクロール Copyright 1990 E. Toyokuni Number WA04900922 CM901207 3 : 4 : 5 : 6 : 7 : 8 : 10 : 1 1 : 16 : 17 : 18 : 1 9 : 20 : 21 : 22 : 23 : 24 : 25 : 26 : 28 : 29 : #include # i nc 1 ud e 9 : #include "window. h ” cw->xlenn X I en CW PW MWINDOW *cw ; int *pluskey = 0 unsigned scrollwid unsigned linel : unsigned ylen unsigned Xlen int ly,y unsigned num : 13 : scroll(MWINDOW *pw) ー (KEIWID し R * 2 ) : ことにする。 100 CMAGAZINE 1991 1
List 3 fromura() 関数 (List 4 ) 裏画面の指定領域の内容を表示 【引数】 xl, yl, x2, y2 っー 8 0 、ー 4 っ 0 っ 0 4 ・′ 0 《 0 0 ー 8 0 1 1 よ 11 1 議ワ 3 ワしワ朝ワ朝ワ 3 ワ 3 0 乙ワワ 3 ワ 3 っ 0 っ 0 if ()l く 0 Ⅱ x2 〉 = 80 Ⅱ YI く 0 Ⅱ Y2 > = 25 ) { return ( ー 1 ) : ←座標の指定がまちがっている場合には , for (x = o,y=yl ;y く = Y2;x + = 2 * ( x2- xl + l) , Y + + ) { ニ y * 0xa0 + xl * 2 ↑座標 x , y をテキスト VRAM におけるオフセットアドレスに変換 ,CPOS,2*(x2-X1 + 1) ) : movedata(vramseg , CPOS, uraseg ↑表示内容を第 2 画面に転送 movedata(vramseg 十 0X200, cpos, uraseg + 0X200, cpos, 2 * ( x2 ー xl + 1 ) ) : ↑表示属性を第 2 画面に転送 ー 1 を返す CPOS 裏画面の ( xl , (l) か ら ( x2 , y2 ) まて、の領域 【戻り値】 卍常終「の場合 0 を返す。座標の指定が表 示可能範囲外の場合には , ー 1 を返す。 return(0) : 【機能】 裏画面の ( xl , yl ) から ( x2 , (2) まて、の四 角領域の内容を表画面の同一座標へコヒ。ー する。 fromura. C List 4 Number FR01900902 * / Copyright 1990 E. TOY0kuni 2 : 3 : # i ncl ude く std i 0. h 〉 4 : # i nc lude く std 1 i b. h 〉 5 : #include く dos. h> 7 : fromura(unsigned i n t x 1 , s 引 ba 「 ( ) 関数て、メニューを表 unsigned int YI, 8 : unsigned int 9 : 示 , 選択 , 表示換えをしますが , [ ー , y2) unsigned int 曰曰囮ロ , そのほかのキーのスキャンはて、 きますが , ーキー , ーキー 13 : unsigned CPOS ニ 0 ←第 1 画面のセグメントアドレスを指定 = 0Xa000 ・ 14 : unsigned vramseg のスキャンだけはて、きません。修正は se わ a 「 ←第 2 画面のセグメントアドレスを指定 ニ 0Xa100 ・ unsigned uraseg 15 : 16 : unsigned int x,y; て、 scandir( ) 関数を呼んて、 , case て、 ROL 17 : if ()l く 0 Ⅱ x2 > = 80 Ⅱ yl く 0 Ⅱ y2 〉ニ 25 ) { LUP/ROLLDOWN の場合 , この値を関数 ←座標の指定がまちがっている場合には , ー 1 を返す return(-l) : 19 : 20 : の値に代入しているだけて、す。 for (x ニ 0,y=y1 ;y<= y2;x + ニ 2 * ( x2 ー xl + 1) , Y + + ) { 22 : 23 : cpos = Y * 0xa0 + xl * 2 ↑座標 x , y をテキスト VRAM におけるオフセットアドレスに変換 24 : , cpos , 2 * ( x2 ー xl + 1 ) ) : movedata(uraseg 25 : , CPOS, vramseg ↑表示内容を第 1 画面に転送 26 : movedata(uraseg + 0X200, cpos, vramseg + 0X200, cpos. 2 * ( x2 ー xl 十 1 ) ) : 27 : ↑表示属性を第 1 画面に転送 28 : 29 : 30 : 質問 ーに何らかの値が割り当てられているかを , MS-DOS の KEY コマンドて、確認してくださ い。まったく何の値も割り当てられていな い場合には , スキャンて、きないのて、 , この コマンドて、任意の値を割り当ててください 質問 7 月り・のプログラムて、 fkey. h(List 5 ) が include されているのて、 , type したとこ ろ , キーボ、一ド BIOS の割り込みに使用する ヘッダファイルて、あることは理解て、きまし たが , ESCAPE が 256 に定義されている意味 がわかりません。また次の行の BS は (ESCAPE 十 1) て、すから , 257 に定義されて この定義の意味を いるのだと思いますが , 教えてください return(0) : fkey. h List 5 Number \ FK01900930 * / 1990 E. Toyokuni 2 : / * 押下げキーに対応する戻り値のための定数を定義 * / 3 : 4 : 5 : #define ESCAPE 256 (ESCAPE + 1 6 : #define BS (ESCAPE + 2 7 : #define TAB (TAB 8 : #define CR (CR 9 : #define SPACEE (CR 10 : #define XFER (CR + 10 ) 1 1 : #define RO しし DOWN (CR 12 : #define RO しし UP (R0しし UP + 2 ) 13 : #define INS (ROLLUP + 3 ) 14 : #define DE し (ROLLUP + 4 ) 15 : #define CUP 回篭 ご指摘のように ESCAPE は 256 102 CMAGAZINE 1991 1
(), D) ( 200 個 ) の 3 種類しかないことになり ます。最適な符号て、は , これらのうちて、も っとも頻度の少ないふたつに最長の符号語 を与えるべきて、す。そのような最長の符号 語には先ほど述べたように , 必ず y0 , yl と いうべアが含まれますのて、 , と定めることがて、きますにの反対て、も同じ ことて、す ) 。この共通の部分 y は B と ( C , D ) を 合わせたものを表し , B か (), D) かを区別す るために y の後にヾ 0 〃か〃をつけると考えら れます。つまり , ということになります。 こて、文字は A ( 400 個 ) , (), (), D ) ) ( 400 個 ) の 2 種類になりましたのて、 , と定めることがてきます。すると , さかの ばって , B = 10 となり , さらにさかのばって , C = 1 1 0 D = 1 1 1 となって , すべての符号語が決まります。 これがハフマン符号の構成法と , それが最 適て、あることのインフォーマルな証明て、す。 ハフマン法のアルゴリズムをまとめると , 次のようになります。符号化すべきファイ ルは n 種類の文字からなっているとします ( 1 文字 = 8 ビットならば n = 256 ) 。文字は ・・・ n ー 1 という文字コードて、表すこと 0 n—l } とす { 0 , 1 , ①集合 S る。各文字 c について , その出現頻度 freqCc] を調べておく (c n—l)o ② S からもっとも頻度の低いものをふたっ 取り出す。それらを i, j とする。つま り , S の i , j 以外のどんな要素 x について Li st arith. c ( 簡単な算術圧縮プログラム ) 5 1 : #include ” bitio. c ” 2 : #include <limits. h> 3 : # ifdef max #undef max 5 : #endif 6 : #define nax(), y) ((x) ) (y) ? (x) : (y)) / * 2 数の最大値 * / 7 : #define N 256 8 : #define USHRT_BIT (CHÅR_B は * s izeof(unsigned short)) / * unsigned short のビット数 * / 9 : 10 : #define QI ()U くく (USHRT-BIT ー 2 ) ) 11 : #define Q2 ()U * QI) 12 : #define Q3 ()U * QI) 13 : 14 : unsigned cum[N + 1 ] : / * 累積度数 * / / * 次の output() で出力する補数のカウンタ * / 15 : int ns; : static void output(int bit) / * bit に続いてその補数を ns 個出力 * / putbit(bit); / * 1 ビット書き出す * / 19 : } / * その補数を書き出す * / while ()s > の { putbit(! bit); 20 : 22 : 23 : void encode(void) / * 圧縮 * / 24 : { 25 : i nt C : 26 : unsigned long range, maxcount, incount, cr, d; uns igned short IOW, h igh; static unsigned long countCN] ; 28 : 29 : f0 「 (c に 0 : c く N : c + + ) count [c] = 0 : / * 頻度の初期化 * / 30 : while ()c : getc(infile)) ! ニ EOF) count[c] + + : / * 各文字の頻度 * / / * 原文の大きさ , 頻度の最大値 * / 0 : maxcount 32 : incount for (c = 0 ; c く N : c + + ) { 33 : incount 十 = count[c] : 34 : if (count cc] ) maxcount) maxcount = count [c] : 35 : 36 : if (incount の return; / * 0 バイトのファイル * / 37 : / * 頻度合計が QI 未満 . 各頻度が 1 バイトに収まるよう規格化 * / 38 : d = nax((maxcount 十 N 39 : (incount + QI - 257 ) / ()I - 256 ) ) : 40 : i f (d ! = for (c 0 : c く N : c + + ) 42 : count[c] (count (c) + d ー l) / d : 43 : cum [ 0 ] 44 : for (c ニ 0 : c く N : c + + ) { 45 : fputc((int)count[c], outfile) ; / * 頻度表の出力 * / 46 : = cum[c] + (unsigned)count[c] : cum[c + 1 ] / * 累積頻度 * / 47 : 48 : 49 : outcount / * 巻き戻して再走査 * / rewind(infile); 50 : incount 0 : high USHRT-MAX; IOW = n S while ()c ニ getc(infile)) い EOF) { / * 各文字を符号化 * / 52 : (uns igned long) (high low) + 1 : 53 : range (uns igned short) 54 : h i gh い ow + (range * cum[c + 1 ] ) / cumCN] 55 : (uns igned short) 56 : 00W 十 (range * cum[c 57 : for ( ・ 58 : (high く (2) output(0) : 59 : else if い OW 〉 = (2) output(l); 60 : else if 00W 〉 = QI & & high く (3) { h i gh ー low 62 : ns 十十 : } else break : 63 : (high くく 1) + 1 : h i gh ー OW くく 64 : 65 : lncount) : = の printf( ” %121uYr ” if (( + + incount & 1023 ) 66 : / * 最後の 7 ビットはバッフアフラッシュのため * / 68 : ns 十 = 8 : / * 01 または 10 * / if 00W く (I) output(0); else output(l): 69 : / * 原文の大きさ * / printf("ln : % 朝 bytesYn" incount) : outcounti N) : printf("0ut: % bytes (table: (d)Yn ” / * 圧縮比 * / ( 1000 * outcount + incount / 2 ) / incount: printf("0ut/ln: % . % 0 引 uYn ". cr / 1000 , cr % 100 の : 73 : 74 : } 76 : int binarysearch(unsigned x) / * cum[i] ← x く cum[i + 1] となる i をニ分探索で求める * / 77 : 78 : { 79 : 80 : 82 : / * 文字の種類 ( 文字コード = 0.. N ー 1) * / 1 low wh Ⅱ e ( i く j) { 54 CMAGAZINE 1991 1
Li st 2 ノのほうが圧縮比がよい場合があるという のて、変だなと思っていましたが , 後て、聞い た話によると PKWARE 社が参考にした圧縮 関係の本に誤りが書いてあったということ " 。 LHarc から LH へ さて , LHarc は二分探索木を使っていた ため , 入力によっては非常に遅くなること が指摘されはじめました ( 同種のことは PKZIP て、も起こりえます ) 。この点を改善す るため , 私たちは新しいデータ構造に切り 換えることにしました。また , ハフマン法 についても研究し , 高速化に向くアルゴリ ズムに換えることにしました。 こうしてて、 きたのが LH 2.0 て、す。 arc の 3 文字を外した のは ARC を作っている SEA 社の意向による ものて、す。 LH の基本圧縮部の C プログラムは , どこ を誰が書いたのかわからないほど吉崎さん と私が交互に手を入れましたが , アセンプ ラ化とユーザインタフェイスはすべて吉崎 さんによるものて、す。 一方 , LH の完成があまりにも遅れたた め , 私も同じ基本圧縮部を使ったアーカイ バ ar を作ってみました ( 付録ディスク収録 ) 。 これは実用ソフトて、はなくアルゴリズム試 験用および教育用のものて、 , おおむね LH 互 換て、すが , 機種 (OS, int の幅, 負数の表現 ) に依存しないことと ANSI C に準拠すること に固執したため , ファイル作成日時をへッ ダに入れることなどは断念しました。 unsigned char code[17], mask; unsigned long int incount ニ 0 , printcount = 0 , cr; / * 木を初期化 * / init_tree() : code [ 0 ] 0 : codeptr ニ mask for ( i for (len = 0 : len く P : n + + ) { c = getc(infile); if (c = EOF) break: text[r + len] ニ c: if (incount 0 ) return : incount i く = F; i + + ) insert_node(r for ( i insert_node(r) : do { if (matchlen 〉 len) matchlen け (matchlen く 3 ) { matchlen = 1 : code [ 0 ] ト mask; code[codeptr+ + ] = text[r] : } else { (unsigned char) matchpos; code[codeptr + + ] code[codeptr + + ] = (unsigned char) (((matchpos 〉〉 4 ) & 0xf の一 (matchlen ー 3 ) ) : i f ( (mask く← = 0 : i く codeptr; i + + ) putc(code[i], outfile) : for (i outcount 十 = codeptr : code[0J = 0 : codeptr = mask lastmatchlen = matchlen : for (i ニ 0 : i く lastmatchlen; i + + ) { c = getc(infile); EOF) break : text[s] delete-node(s) : i f (s く F ー 1 ) text [s + N] insert-node (r) : if ((incount 十ニ i) 〉 printcount) { printf("%121uYr ” , incount) : printcount + = 1024 : while ( i + + く lastmatchlen) { delete_node(s) : if (--len) insert-node(r) : } ⅶ ile (len 〉の : if (codeptr > l) { for ( i ニ 0 : i く codeptr; i + + ) putc(code[i], outfile); outcount 十 = codeptr: printf("ln : % ⅲ bytesYn" incount) : / * 結果報告 * / printf("0ut: %lu bytesYn", outcount) : if (incount い 0 ) { - / * 圧縮比を求めて報告 * / ( 1000 * outcount + incount / 2 ) / incount; C r = printf("0ut/In: %lu. %031uYn", cr / 1000 , cr % 100 の : 83 : 84 : 85 : 86 : 88 : 89 : 90 : 91 : 92 : 93 : 94 : 95 : 96 : 97 : 98 : 99 : 100 : 101 : 102 : 103 : 104 : 105 : 106 : 107 : 108 : 109 : 1 10 : 112 : 113 : 114 : 115 : 116 : 118 : 119 : 120 : 121 : 122 : 123 : 124 : 125 : 126 : 127 : 128 : 129 : 130 : 131 : 132 : 133 : 134 : 135 : 136 : 137 : 138 : } 139 : 140 : void decode(unsigned long int size) / * 復元 * / 141 : 142 : i nt i , J, k, 「 , C ; 143 : unsigned int flags; 144 : for ( i と 0 : i く N ー F : i + + ) text [ i ] 145 : 146 : flags = 0 : 147 : 148 : if (((flags 〉 > : 1 ) & 256 ) 149 : if ()c ニ getc(infile)) 150 : flags ニ c ー Oxff00; 152 : i f ( ロ ags & l) { 153 : if ()c = getc(infile)) = EO F) b reak : putc(), outfile) : text[r + + ] 155 : } else { 156 : i f ( ( i ニ getc ( i n f ⅱ e) ) = EOF) break; 157 : = EOF) break; ニ getc ( i nf ⅱ e) ) 158 : = ()j & 0xf0) くく 4 ) : = (j & 0x0f) + 2 : J 159 : for (k = 0 : k ← j : k + + ) { 160 : ニ text[(i + k) & (N - putc(), outfile) : C text [ r + 十 ] 162 : 163 : 4 : 165 : 166 : } 167 : s : i く r : i + + ) text [ i ] : 0 : / * バッフアを初期化 * / EOF) break; いろいろなテータ圧縮 ア丿レゴリズム LH のアルゴリズムについて述べる則に いろいろな圧縮アルゴリズムを概観します。 連長 ( ランレングス ) 圧縮 連続した同じ文字を個数て、表します。た 48 CMAGAZINE 1991 1 printf( ” %121uYn ” S i 2 e) :
ホーランドジャパン lnformation from C0mpiler Makers 24 : } 30 : { 47 : } 1 : を直接ファイルに書き込み , 込みはできますか ? Turbo C Turbo PascaI 読み Turbo C 編 Q 日本語処理ライプラリの h 。。 t ozen 関数で半角文字を全角文字に 変換したあと , その全角文字を表 LlSt 1 角文字として表示するには上位バ signed int になっており , これを全 hantozen の戻り値は un A いのでしようか。 示させるにはどのようにすればよ イトと下位バイトを取り出して , それぞれをレヾイト文字として表示 しなければなりません 0List 1 は半 角文字を入力すると全角に変換し て表示するものてす。 findfi 「 st で日 LE ″を与えたので すが , すべてのファイルが見つか りません。コマンドラインで D 旧 日 LE とすると , すべての拡張子に該 当するファイルが見つかります。 Q コマンドラインの DIR コマンド と findfirst はまったく同じてはあり ません。コマンドラインの DIR コマ のような関数を用意すればよいて えたい場合は , List 2 の efindfirst ンドと同じようにファイル名を与 しよ - スモールモデルで , far ヒープ フ。 この内容 A ライプラリ関数には far ヒープ に対するファイルアクセスを行う 関数は用意されていません。この 場合はふたつの方法があります。 ひとつはファイルアクセスを行う , 関数を自作する。第 2 はいったん near データバッフアに内容を転送 してから読み書きを行ってくださ い (List 3 参照 ) 。 を使っているのですが , LlSt 3 3 : #include く dos. h> 2 : #include く mem. h> 1 : #include く stdio. h> 2 : 3 : 4 : 6 : 7 : 8 : 9 : 10 : 13 : 14 : #include く jctype. h> #include く stdio. h> void main() uns igned int ret, char ch; pr i ntf ( " 半角文字 : ” ) : ニ getchar() : h i , = hantozen ( ch ) : ret = ret > > 8 : = ret & 0x00ff; / * 上位パイトと下位バイトを分ける * / ) : / * 2 つの文字として表示する * / 4 : 6 : 8 : 12 : 14 : 17 : 19 : 20 : 21 : 22 : 23 : 25 : 27 : 28 : 29 : 32 : 33 : 34 : 35 : 36 : 37 : 38 : 39 : 40 : 42 : 43 : 44 : 45 : 46 : * far データをファイルに出力する size t farfwrite(const void far *ptr, size_t size, size_t n, FILE *fp) (unsigned char huge * ) ptr; unsigned char huge *hp char buf[BUFSIZ] : long num (l ong) s i ze * n : S i ze t ー en : printf(" 全角文字 : XcXcYn", long tOtal wh ⅱ e (num > 0 い { len = num > = BUFSIZ ? BUFSIZ : total + ニ fwrite(buf, hp + = len; num return tOtal / s ize; 1 , len, (p) : novedata(FP_SEG(hp) , FP_OFF (hp) , FP_SEG (buf) , (int)num; * ファイルから far データに読み込む FP_0FF(buf), len): List 2 3 : #include く dir. h> #include く string. h> 1 : #include く stdio. h> 2 : 4 : 5 : 7 : 8 : 9 : 10 : 12 : 13 : 15 : 18 : 20 : 21 : 22 : 23 : 24 : 25 : 26 : 28 : 29 : 30 : int efindfirst(const char *filename, char path[MAXPATH] : char drv[MAXDRIVE] , di r[MAXDIR] , int mask; struct ffblk *ffblk, int attrib) ext[MAXEXT] : file[MAXFILE], size_t farfread(const void far *ptr, size_t size, size_t n, FILE *fp) (unsigned char huge *)ptr; unsigned char huge *hp char buf[BUFSIZ] : fnsplit(filename, drv, dir, file, ext) : mask if ((mask & FILENAME) ニ 0 ) strcpy(file, if ((mask & EXTENSION) = 0 ) strcpy (ext, printf("X-16sYn". ffblk. ff_name) : while (stat = の { stat efindfirst(* 十 + argv, while (--argc) { int stat; struct ffblk ffblk; int main(int argc, char **argv) return findfirst(path, ffblk, fnnerge(path, drv, dir, file, &ffb ⅸ , 0 ) : attr (b) : ext) : 1991 1 stat = findnext(&ffblk) : 0 : size_t len; (long)size * n; long num long tOtaI ⅶⅱ e (num 〉 0 い { (int)num; len = num BUFSIZ ? BUFSIZ : len = fread(buf, l, len, (p) : movedata(FP_SEG (buf) , FP-OFF (buf) , FP-SEG (hp) . total 十 = len; hp 十 = len; len : num if (len く BUFSIZ) break; return tOtal / s ize; FP_0FF(hp), len) : 164 CMAGAZIN E
Li st 1 ftime 関数 , setftime 関数を使用してソース ファイルの日付と時刻をデスティネーショ ンファイルに設定しています。 getf ⅱ etime 関 数と setfiletime 関数て、はファイルポインタか ら fileno(fp) て、ファイルハンドルを取り出していますが , 処理系によっては ( たとえば OPT ー C などて、は ) ファイルハンドルが MS ー DOS のファイルハ ンドルと一致せす , このリストのままて、は 動作しない場合がありますから注意してく ださい。本来は List 1 , List 2 のように直接 DOS ファンクションを用いてオープンした ファイルハンドルを用いる必要があります。 また , getfiletime 関数と setfiletime 関数は List I,List 2 の関数に差し換えることがて、 きます。 ( 注 ) コンパイルスイッチはソースリストの コメント部分に記述していますが , MS ー C 5.1 て、のコンパイルにあたっては次の点に注意 してください 1. 筆者はライプラリを結合していないた め , スイッチ / ZI を指定してリンカに渡すス イッチて、ライプラリを指定しています。ラ イプラリを結合されている方はスイッチ / ZI とライプラリ名をとってください 2. コマンドラインて、ワイルドカードを使用 したい場合は setargv. obj をリンクしてくだ さい。筆者はコメント部分に記述している コンパイルスイッチて、 %MSC% YIibYsetargv. obj を指定していますが , % MSC% は MS ー C のディレクトリを示す環境 変数て、す。 set MSC=A : *MSC という設定になっています。また , Turbo C 2.0 に関してもコマンドラインて、ワイルドカ ードを使用したい場合は wildargs. obj をリン クしてください。コメント部分て、指定して いる % TC%YlibYwildargs. obj において % TC% は TurboC のディレクトリを示す環境 変数て、す。 / * ハント・ルのオーフ・ン iregs. x ・ ax = 0X3d00 : intdos( &iregs, &iregs ) : if ( iregs. x. cflag ) return( ー 1 ) : fd = lregs. X. ax; = fd : iregs. X. bX = 0X5700 : 1 regs. X. ax intdos( &iregs, &iregs ) : *date = iregs. X. dx; *time lregs. X. cx; = fd : i regs. X. bX i regs. h. ah = 0x3e; intdos( &iregs, &iregs ) : return( 0 ) : っ 4 ・ LO ^ 0 ー 8 0 1 よっ 0 っ 0 -4 ・尸 0 ^ 0 0 ー ・ー・、 1 1 よ , 1 1 ー・ー - 一 1 よっ 0 り 0 っ 0 っ 0 っ 0 り 0 つワ 0 / * ファイル・ハント・ル / * ファイル・ハント・ル / * ファイルの日付 / 時刻を設定 / * 日付 / * 時刻 / * ファイル・ハント・ル / * ハンドルのクローだ ファイルの日付と時刻の設定 ( set 価 me. c ) LiSt 1 : #include く dos. h> 2 : 3 : int setfiletime(char * , uns igned, unsigned) : 4 : 5 : int setfiletime( path, date, t ime ) / * ファイルの日付と時刻の設定 6 : char path ロ : / * ハ・ス名 / * 日付、時刻 7 : unsigned date, time; 9 : union REGS i regs : register unsigned int fd; iregs. x. dx = ( unsigned )path; 12 : 13 : = 0X3d00 : lregs. X. ax intdos( &iregs, &iregs ) : 14 : if ( iregs. x. cflag ) 15 : return( ー 1 ) : 16 : 17 : lregs. X. ax : 18 : iregs. x. dx = date; 19 : lregs. X. CX = time; 20 : iregs. x. bx = fd; 21 : = 0X5701 : lregs. X. ax 22 : intdos ( &iregs, &iregs ) : 23 : iregs. x. bx = fd; 24 : iregs. h. ah = 0x3e; intdos( &iregs, &iregs ) : 25 : 26 : return( 0 ) : / * ファイル・ハント・ル / * ds:dx ハ・ス名の位置 / * ハント・ルのオーフ・ン / * ファイル・ハント・ル / * 日付 / * 時刻 / * ファイル・ハント・ル / * ファイルの日付 / 時刻を設定 / * ファイル・ハント・ル / * ハント・ルのクロース・ ファイルコ e—(fcopy. c) LiSt 1 : ファイル・北・一サンフ・ル・フ・ロク・ラム 3 : ファイル名 : fcopy. c 5 : 6 : 使用法 : fcopy infile outfile 7 : 8 : 9 : コンハ・イル・スイッチ AIO: : 11 : MS-C 5. 1 Sma Ⅱ Model : 13 : cl /J /W3 /Zdl fc 叩 y. c %MSC%YI ibYsetargv. obj /link slibcr + Iibh/st:0x2800/cp: 0X1000 / noe : 14 : 15 : Turbo C 2.0 Sma Ⅱ Model , 17 : tcc -w fcopy. c %TC%YlibYwi ldargs. obj 0 92 CMAGAZINE 1991 1
00 圧縮 入門 List 5 left[k] =i; right[k] =j; parentCi] =k; parent[j] =k; freq[k] =freqCi] + freq[j] : とする。集合 S から i, j を外し , 代わり に k を入れる。 ④ S の要素の数が 2 個以上なら 2 に戻って繰 り返す。 このアルゴリズムにより , k を頭 ( 根 ) とす る木構造がて、きます。この木を根 k から文字 c まて、たどる経路がたとえば左 , 右 , 左 , 左 だったとすると , 文字 c に対応する符号語は 0100 という 4 ビットと定めます。 つまり , 左なら 0 , 右なら 1 というわけて、 す。実際の符号化て、は , 根からたどらずに 文字 c の側から parent[c] , parent[parent [ c ] ] ・・・・・・のようにして根にたどり , その各 ステップて、の子が親の左の子か右の子かを 調べ , 最後に逆順に出力します。逆に , 復 号て、は , 根からたどるほうが楽て、す。 さて , このようにして作った最適な符号 て、は , 文字がたとえば 256 通りあれば , 符号 語の長さの上限は 255 ビットて、す。実際に んなに長い符号語がて、きることはまずあり 得ませんが , Fig. 3 のようなハフマン木て、は こうなってしまいます。この例は吉崎さん によります。 LH て、は , ハフマン木をたどって 1 ビット ずつ出力していくのてはなく , 全符号語を 最初に構成して表に入れておくことて、高速 化を図っています。そのため , こんな長い 符号語が現れては困るのて , 17 ビット以上 の符号語は強制的に 16 ビットに直し , ほか の符号語を少しずつ変えてつじつまを合わ せています。実際には 17 ビット以上の符号 語が現れることはまずないのて、 , 最適な符 号になるようにはしていません。本当に制 約っきて、最適な符号を得るための方法につ いては文献 [ 7 ] をご覧ください if (cum[k] ← x) i = 83 : 85 : 86 : 88 : 89 : void decode(unsigned long size) / * 復元 * / 90 : { uns igned char count[N] : 92 : 93 : uns igned short low, h igh, value: 94 : uns igned long i, range; 95 : = の return; / * 0 バイトのファイル * / i f (s i ze 96 : cum[0] for (c = 0 : c く N : c + + ) { 98 : fgetc(infile) : / * 頻度分布を読む * / count [c] 99 : : cum[c] + count[c]; / * 累積頻度を求める * / cum[c 十 1 ] 100 : 101 : 102 : va lue = 0 : for (c ニ 0 ; c く USHRT-BIT; c + + ) 103 : / * バッフアを満たす * / val ue = 2 * va lue + getb i t ( ) ; 104 : 105 : low ニ 0 : high = USHRT-MAX; for (i = 0 : i く size; i + + ) { / * 各文字を復元する * / 106 : (unsigned long) (high low) + 1 : 107 : range c ニ b inarysearch ( (unsigned) ( ( ((unsigned long) 108 : の + 1) * cum[N] I) / range)) : ( va lue 109 : (uns igned short) 110 : high 00W + (range * cum[c + 1 ] ) / cum[N] 111 : (uns igned short) 112 : low 00W 十 (range * cumCc 113 : 114 : (h i gh く (2) { / * 何もしない * / } 115 : else if い ow > = (2) { / * 何もしない * / } 116 : else if 00W 〉 = QI & & high く (3) { 117 : 1 18 : val ue = QI : 10W ーニ QI; h i gh } else break: 119 : 120 : (h igh くく 1) + 1 : lOW くく = 1 : h i gh = (value くく 1) 十 getbit(); / * 1 ビット読む * / 121 : value 122 : / * 復元した文字を書き出す * / 123 : 124 : 125 : / * 原文のバイト数 * / 126 : 127 : } 128 : 129 : int main(int argc, char *argv[]) 130 : { 131 : i nt C : unsigned long size: / * 元のバイト数 * / 132 : 133 : if (argc ! : 4 日 ()c ま *argv[l]) ! = 'E' & & c ! ニ 134 : 135 : error(" 使用法 :yn" 136 : ar i th e f ⅱ el f ile2 ・・ f ⅱ e 1 を f ⅱ e2 に圧 fiYn" 137 : ar i th d f ⅱ e2 f ⅱ e3 、・ f ⅱ e2 を f ⅱ e3 に復元 " ) : 138 : fopen(argv[2] , "rb")) = NU しい i f ( ( i nf i 1 e 139 : error ( ”入力ファイルが開きませんつ : 140 : if ((outfile ニ fopen(argv[3], "wb")) = NU しい 141 : error ( " 出力ファイルが開きません " ) : 142 : 143 : / * i nf Ⅱ e の末尾を探す * / fseek (infile, 0 し , SEEK-END) : 144 : ニ fte Ⅱ ( i nf ⅱ e) : / * i nf ile のバイト数 * / 145 : S ー ze fwrite(&size, sizeof size, 1 , outfile); 146 : rewind(infile): 147 : / * 圧縮 * / encode ( ) : 148 : } else { 149 : fread(&size, sizeof sizej 1 , 150 : / * 復元 * / decode(size) : 151 : 152 : fclose (outfi (e) : 153 : fcl ose ( i n f i 厄 ) : 154 : return EXIT SUCCESS; 155 : } ANSI C にほぼ準拠したつもりです。 Turbo C 2.0 でテストしました。 付属の” bi t io. c ”を同じディレクトリに置いてコンパイルしてください。 1 : e lse j return i putc(), outfile) : if ()i & 1023 ) = の printf("X121uYr" printf("%121uYn» / * 元のバイト数 * / infile) : 0 スライド辞書十 ハフマン = LH 作る。具体的には , 初期値 n をもつ大域 変数 avail と , 配列 left ロ , right [ ] , par ・ ent[] を用意しておき , k= avail; avail 十十 ; freq[i] ミ freq [x 」 , freq[j] ミ freq Cx] てあるような i,j ( i 手 j ) を取り出す。 ③ i , j を左右の子としてもつ節点 ( n 。 de ) を 先ほど述べたスライド辞書法て圧縮する 特集圧縮アルゴリズム入門 55