360 likes | 541 Views
檔案二 - 二進位檔. 二進位檔與本文檔的結構不同。在 Windows 作業系統的本文檔每一記錄之後均有返回字元( CR )及換列字元( LF ),「 CR 」以十六進位表示為「 0d 」, LF 以十六進位表示為「 0a 」。 C 程式輸入本文檔時將「 CR/LF 」轉換為只有「 LF 」字元,輸出本文檔時又將「 LF 」轉換為「 CR/LF 」兩個字元。但二進位檔之輸入、輸出都沒有這種轉換的動作。.
E N D
檔案二-二進位檔 • 二進位檔與本文檔的結構不同。在 Windows 作業系統的本文檔每一記錄之後均有返回字元(CR)及換列字元(LF),「CR」以十六進位表示為「0d」,LF 以十六進位表示為「0a」。 • C 程式輸入本文檔時將「CR/LF」轉換為只有「LF」字元,輸出本文檔時又將「LF」轉換為「CR/LF」兩個字元。但二進位檔之輸入、輸出都沒有這種轉換的動作。 正修科技大學計算機中心
開檔使用 fopen() 時,模式 mode 後附加「t」表示本文檔,附加「b」表示二進位檔。 二進位檔 mode 情形如下: rb 開啟二進位檔為唯讀。 wb 開啟二進位檔為唯寫。 ab 開啟二進位檔為附加。 rb+ 開啟二進位檔為更新,讀及寫。 wb+ 開啟二進位檔為唯寫更新。 ab+ 開啟二進位檔為附加更新。 二進位檔存取可對某一檔之資料做讀取及寫入之動作。 正修科技大學計算機中心
讀取可使用 fread() 函式,寫入可使用 fwrite() 函式,其語法說明如下: #include <stdio.h> size_t fread(void *ptr, size_t size, size_t n, FILE *f) 由指定檔 f 讀取 n 項資料,每一項為 size 個位元組,至 ptr 指標所指之記憶體位址。讀取成功時傳回 n 值,讀入失敗(或讀至檔尾)時傳回 0 值。size_t 為不帶符號的整數 unsigned int 型態。 正修科技大學計算機中心
#include <stdio.h> size_t fwrite(const void *ptr, size_t size, size_t n, FILE *f); 將 ptr 指標所指處之 n 項資料,每項 size 個位元組,共 n*size 個位元組寫入 f 所指的檔案。寫入成功時傳回寫入項數,寫入失敗時傳回短缺之項數。 正修科技大學計算機中心
#include <stdio.h> fseek(FILE *f, long offset, int whence); 對於指定檔案 f 設定檔案指標於偏離檔案指標 whence 處 offset 個位元組之位置。定位成功時傳回 0 值,定位失敗時傳回非 0 值。whence 之意義如下表 11.1 所示。 表 11.1 whence值及其意義表 whence值 whence識別字 常數檔案指標位置 -------------- -------------------- ------------------------ 0 SEEK_SET 檔案開頭 1 SEEK_CUR 目前檔案位置 2 SEEK_END 檔尾 正修科技大學計算機中心
檔案指標位置若置於檔案開頭,則開頭之位元組編號為 0,下一個位元組編號為 1,再下一個位元組編號為 2,等等。檔案指標位置若為目前檔案位置,則目前檔案位置位元組編號為「檔案指標位置+0」,下一個位元組編號為「檔案指標位置+1」,等等。 正修科技大學計算機中心
【例題binary】 將字串 "binary out" 輸出至二進位檔 "binary.dat",然後又從該檔讀 入記憶體後再行輸出。 【分析】 開啟 binary.dat 為輸出二進位檔,宣告如下: FILE *f=fopen("binary.dat", "wb"); 使用 fgets() 函數從鍵盤輸入 "binary out" 字串至 s 變數。 然後使用 fwrite() 函式將變數 s,共 strlen(s)+1 個位元組的一筆資 料輸出至 f 所指的二進位檔案 binary.dat。 fwrite(s, strlen(s)+1, 1, f); 正修科技大學計算機中心
整個輸出完畢後再將二進位輸出檔案 binary.dat 關閉,一定要關閉整個輸出的動作才算完成。關閉的動作先將在記憶體緩衝器中的資料輸出至二進位輸出檔案 binary.dat,然後在檔尾處加上一個檔案結束符號,在 Windows 作業系統檔案結束符號為十六進位「0x1a」。 fclose(f); 第二階段將剛剛建立的資料檔 binary.dat 重新開啟為二進位輸入檔案,檔案指標仍為 f,然後從 binary.dat 每次讀入一個位元組至不帶符號的字元變數 uc 位址處加於處理。 f = fopen("binary.dat", "rb"); fread(&uc, 1, 1, f); 正修科技大學計算機中心
【執行結果】 請輸入一個字串: binary out<Enter> 傾印binary.dat檔 62 69 6e 61 72 79 20 6f 75 74 0a 【二進位檔案binary.dat】 binary out 正修科技大學計算機中心
【例題dumpfile】 以十六進位印出已知檔名及指定每列位元組數之資料。測試資料檔名 為 fixout.txt,內定每列位元組數為 16,總共 64 位元組。 【分析】 已知檔可為任意型態之檔,如循序檔、二進位檔、或隨機檔,每次讀入一 個位元組,故均以二進位檔處理。 【說明】 本程式所要操作的檔案要執行時才會決定,如此才有彈性,因此檔名設計 執行時才從命令列輸入,每列所要列印的位元組數 bytesperline 以及共 要從檔案讀入多少個位元組 totalbytes 也是執行時才給的,這三個引數 都從命令列輸入,因此執行的格式如下: dumpfile 檔名 每列位元組數 總共讀入位元組數 <Enter> 正修科技大學計算機中心
主函式 main() 的簽名提供這些引數資料,簽名如下: int main(int argc, char *argv[]) 參數 argc 表示引數個數,若三個引數全部都提供的話引數個數為 4。 第零個引數存於 argv[0],為執行的程式名稱,本例為 dumpfile。 第一個引數存於 argv[1],為「檔名」,本例為 outfile.txt。 第二個引數存於 argv[2],為「每列位元組數」,本例為 16。 第三個引數存於 argv[3],為「總共讀入位元組數」,本例為 64。 FILE *f = fopen(argv[1],"rb"); 開啟 argv[1] 檔名為輸入二進位檔,檔案指標為 f。 ch=fgetc(f); 每次從 f 檔案讀取一個位元組,存入字元變數 ch。 if (feof(f) || count>=totalbytes) break; 若已讀至檔尾或已超過指定的總共位元組數,這時就跳出 while 無窮 迴圈,關閉 f 檔案後就結束了。 正修科技大學計算機中心
printf("%02x ", ch); count++; if (count%bytesperline==0) printf("\n"); 將讀取的字元 ch 以十六進位二位數輸出,若沒有高位數則補零。位元組計數 count 增一。若讀取的位元組數是 bytesperline 的倍數,則跳至下一列繼續執行。 正修科技大學計算機中心
/****************** dumpfile.c *****************/ #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int ch, count, bytesperline=16, totalbytes=64; FILE *f = fopen(argv[1],"rb"); if (argc>=3) bytesperline = atoi(argv[2]); if (argc>=4) totalbytes = atoi(argv[3]); count=0; while (1) { ch=fgetc(f); if (feof(f) || count>=totalbytes) break; printf("%02x ", ch); count++; if (count%bytesperline==0) printf("\n"); } fclose(f); return 0; } 正修科技大學計算機中心
【檔案fixout.txt】 39309101092100 39309103075008 39309105083095 39309107083087 39309109072086 【執行結果】 ch11> dumpfile fixout.txt 16 64 <Enter> 33 39 33 30 39 31 30 31 30 39 32 31 30 30 0d 0a 33 39 33 30 39 31 30 33 30 37 35 30 30 38 0d 0a 33 39 33 30 39 31 30 35 30 38 33 30 39 35 0d 0a 33 39 33 30 39 31 30 37 30 38 33 30 38 37 0d 0a 正修科技大學計算機中心
11.11 隨機檔 • 隨機存取檔其資料儲存之方式與循序存取大不相同。循序存取檔之資料每一筆長度不一且均以 CR 歸位鍵及 LF 換列結束,而隨機存取檔之資料每一筆均為定長,並不以 CR 及 LF 結束。循序存取檔之記錄,資料為變長,而隨機存取檔之記錄,其資料為定長。隨機存取檔適用於資料庫管理系統之設計,以考生之成績籍為例,每一個考生包含下列的資料: • 准考證號碼 為八位整數,欄名 id。 • 姓名 以英文字表示為十二位,以中文表示為六位, • 宣告為字元陣列,欄名 name。 • 性別 為一個字元,'1' 表男,'2' 表女,欄名 sex。 • 總分 為三位整數,欄名 score。 • 以結構表示如下: • struct stag • { • int id; • char name[13]; • char sex[2]; • int score; • } ; 正修科技大學計算機中心
【例題sfile】 設計一個程式從本文檔 sfile.txt 建立一個 stag 格式的隨機檔,命 名為 sfile.dat。 本文檔 sfile.txt 的內容如下: 23010001陳一心 1411 23010003林二聖 2533 23010005李三多 1455 23010007龔四維 2577 23010009蔡五福 1499 23010011丁六合 2511 23010013邱七賢 1433 23010015歐楊八德 2555 23010017張簡九如 1477 23010019高十全 2599 正修科技大學計算機中心
【分析】 本文檔 sfile.txt 對程式 sfile.c 而言是本文輸入檔,您可以使 用 "rt" 模式開啟,其檔案指標為 FILE 型態的指標變數 f。 FILE *f=fopen("sfile.txt", "rt"); 而考生資料庫 sfile.dat 可使用 "wb+" 開啟為輸出二進位檔如下: FILE *sfile; sfile=fopen("sfile.dat", "wb+"); 接著呼叫 fgets() 函式從本文檔讀入一筆記錄至 line 字串中,讀 至 '\0' 或 '\n' 時才會停止讀取的動作,因此記錄中含有空白字元 仍可讀取。 #include <stdio.h> char *fgets(char *s, int n, FILE *stream); 讀取由 stream 所指檔案 n-1 個字元存於 s。 字串以 '\0' 結束。讀至 '\n' 也會停止 fgets() 之執行。 成功時傳回 s 位址,失敗或讀至檔尾時傳回 NULL 值。 正修科技大學計算機中心
在字串 line 中可使用 sscanf() 函式依指定的格式取至指定的變數位址。 #include <stdio.h> int sscanf(const char *buffer, const char *format [, address] ...); 依格式 format 由 buffer 讀取相對應之資料送至相對應之位 址 address 處。成功時傳回輸入欄數,讀至 buffer 結 束 '\0' 時傳回 EOF 值。 snode 為 stag 結構型態的變數,定義於引入 stag.h 表頭檔。 sscanf(line,"%8d%12s%1s%3d", &snode.id, &snode.name, &snode.sex, &snode.score); 第一欄將八個數字的整數(%8d)讀取至變數 snode.id 位址處。 第二欄將十二個字元以字串的方式(%12s)讀取至 snode.name 位址。 第三欄將一個字元的字串(%1s)讀取至變數 snode.sex 位址。 第四欄將三個數字的整數(%3d)讀取至變數 snode.score 位址。 fwrite(&snode, sizeof(snode), 1, sfile); 然後將整個結構 snode 以二進位方式寫至 sfile.dat 隨機檔中。 正修科技大學計算機中心
【例題recshow】 設計一個程式將考生資料庫 sfile.dat 指定編號的記錄顯示出來。 【分析】 首先將考生資料庫 sfile.dat 開啟為二進位輸入檔, FILE *sfile; sfile=fopen("sfile.dat", "r+b"); 設 n 為輸入之記錄編號,sfile.dat 每一記錄長度為 sizeof(snode),本例為 24 個位元組,n 個記錄共偏移「n*24」個位元組,您可使用函 式 fseek() 將其定位於該記錄處。程式碼如下: offset = n * sizeof(snode); fseek(sfile, offset, SEEK_SET); 長整數型態的變數 offset 表示從 SEEK_SET 處偏移的位元組數。符號常數 SEEK_SET 表示檔案開始處。 fread(&snode, sizeof(snode), 1, sfile); 既已定位,就從該定位處呼叫 fread() 函式將該筆記錄讀入 snode 結構,就可處理了。 正修科技大學計算機中心
【執行結果】 請輸入記錄編號(0-9): 4 <Enter> 准考證號碼=23010009 姓名=蔡五福 性別=1 總分=499 請輸入記錄編號(0-9): 44 <Enter> 輸入記錄編號44超出範圍 執行recshow程式發生錯誤!! 正修科技大學計算機中心
【例題seqaccess】 將考生資料庫 sfile.dat 指定編號的區段記錄顯示出來。 【分析】 將上題的 recshow() 函式稍加修正為 shownode() 只顯示指定的記錄而已。 void shownode(long n, FILE *sfile) { long offset = n * sizeof(snode); fseek(sfile, offset, SEEK_SET); fread(&snode, sizeof(snode), 1, sfile); printf("准考證號碼=%8d\n", snode.id); printf(" 姓名=%-12s\n", snode.name); printf(" 性別=%1s\n", snode.sex); printf(" 總分=%3d\n", snode.score); } 因為很多程式都會用到這個函式,因此將它存入 stag.h 表頭檔裡,要用時再將它引入您的程式。在主函式 main() 裡開啟 sfile.dat 資料庫,輸入起迄記錄編號 n1、n2,然後將此區段的記錄逐一呼叫 shownode() 顯示出來。 正修科技大學計算機中心
【說明】 程式 seqaccess.c 中: printf("\n請輸入起迄記錄編號(0 %ld): ", totalnodes-1); scanf("%ld %ld", &n1, &n2); if (n1>=totalnodes || n2>=totalnodes) printf(" 輸入起迄記錄編號超出範圍!!\n"); else for (i=n1; i<=n2; i++) shownode(i,sfile); 輸入起迄記錄編號 n1 及 n2 之後,檢查是否超出檔案 sfile.dat 的範 圍,若在範圍內則逐一呼叫 shownode() 函式將 n1 至 n2(含)區段的 記錄逐一顯示出來。 正修科技大學計算機中心
【程式seqaccess.c】 /************************ seqaccess.c *****************/ #include <stdio.h> #include "stag.h" FILE *sfile; long totalbytes, totalnodes; int main() { long i, n1, n2; sfile=fopen("sfile.dat", "r+b"); fseek(sfile, 0L, SEEK_END); totalbytes = ftell(sfile); totalnodes = totalbytes/sizeof(snode); printf("\n請輸入起迄記錄編號(0 %ld): ", totalnodes-1); scanf("%ld %ld", &n1, &n2); if (n1>=totalnodes || n2>=totalnodes) printf(" 輸入起迄記錄編號超出範圍!!\n"); else for (i=n1; i<=n2; i++) shownode(i,sfile); fclose(sfile); return 0; } 正修科技大學計算機中心
【執行結果】 請輸入起迄記錄編號(0-9): 3 5 <Enter> 准考證號碼=23010007 姓名=龔四維 性別=2 總分=577 准考證號碼=23010009 姓名=蔡五福 性別=1 總分=499 准考證號碼=23010011 姓名=丁六合 性別=2 總分=511 請輸入起迄記錄編號(0-9): 0 99 <Enter> 輸入起迄記錄編號超出範圍!! 正修科技大學計算機中心
【例題addnode】 將一新考生資料加入考生資料庫 sfile.dat 的尾端。 【分析】 首先將考生資料庫 sfile.dat 開啟為二進位附加檔。 FILE *sfile; sfile=fopen("sfile.dat", "a"); 將新考生資料建立存於 snode 結構中後,使用 fwrite() 函式將該節點 附加於考生資料庫的尾端。 fwrite(&snode, sizeof(snode), 1, sfile); 然後關閉考生資料庫 sfile.dat。 【說明】 執行 addnode.c 後將准考證號碼 23010004 資料附加於 sfile.dat 尾 端。然後執行 seqaccess 程式將該筆資料顯示。 正修科技大學計算機中心
【程式addnode.c執行結果】 (1). 准考證號碼(8位整數) : 23010004 <Enter> (2). 姓名(12個字元之字串) : 李四 <Enter> (3). 性別(1個字元之字串) : 2 <Enter> (4). 總分(3位整數) : 444 <Enter> 23010004 記錄已經加入資料庫中! 請輸入起迄記錄編號(0 10): 10 10 <Enter> 准考證號碼=23010004 姓名=李四 性別=2 總分=444 正修科技大學計算機中心
【例題idfile】 從資料庫 sfile.dat 建立一個以准考證號碼為順序的索引檔 idfile.txt,每一個索引記錄包括兩欄: 准考證號碼 欄名為 id,整數型態,佔 4 個位元組。 索引 欄名為 nodenum,整數型態,佔 4 個位元組, 為該准考證號碼之記錄在資料庫 sfile.dat 中的 記錄編號。 【分析】 將資料庫 sfile.dat 中的記錄逐筆讀入記憶體中,逐筆輸出至索引本文檔 idfile.txt。 正修科技大學計算機中心
【說明】 在 idfile.c 程式中宣告兩個檔案 sfile 及 idfile 如下: FILE *sfile; sfile=fopen("sfile.dat", "r+b"); FILE *idfile=fopen("temp.txt", "w"); idfile 訂為輸出本文檔,檔名暫訂為 "temp.txt",這個檔案在程式執行完畢後會被刪除的。 for (n=0L; n<totalnodes; n++) { offset = n * sizeof(snode); fseek(sfile, offset, SEEK_SET); fread(&snode, sizeof(snode), 1, sfile); fprintf(idfile, "%08d%5ld\n", snode.id, n); } 逐筆讀入 snode 後,將准考證號碼 snode.id 及索引 n 輸出至索引本文檔 idfile。 正修科技大學計算機中心
system("SORT temp.txt /o idfile.txt"); system("DEL temp.txt"); system("TYPE idfile.txt"); 將 temp.txt 本文檔依准考證號碼排序後存入 idfile.txt 本文檔,然後將 temp.txt 本文檔刪除,再將 idfile.txt 檔的內容顯示出來。這三個命令 SORT、DEL、TYPE 是 Windows 系統所提供的命令,您若使用 Linux 系統,其寫法會不同的。在 Windows 系統您要了解有那些命令可用,可在「命令提示視窗」輸入 HELP 命令就可以了。 C:\> HELP <Enter> 您想了解 SORT 的使用法,可輸入: C:\> SORT /? <Enter> 正修科技大學計算機中心
【程式idfile.c執行結果】 23010001 0 23010003 1 23010004 10 23010005 2 23010007 3 23010009 4 23010011 5 23010013 6 23010015 7 23010017 8 23010019 9 正修科技大學計算機中心
【例題query】 利用索引檔 idfile.txt,從鍵盤輸入一個准考證號碼,在索引檔中使用 二分搜尋法,找出該准考證號碼的考生在資料庫 sfile.dat 中的記錄編 號,然後列印出該考生的明細。 【分析】 因為 sfile.txt 檔已經依准考證號碼順序,所以可以使用二分搜尋法找尋指定的准考證號碼,取得其相對應的記錄編號(索引),從而取得該考生的明細。 為了提高搜尋速度,將 sfile.txt 讀入記憶體的 itag 結構裡,itag 結構宣告如下(本結構宣告存於stag.h表頭檔): 正修科技大學計算機中心
struct itag { int id; int index; } ; struct itag inode, *ip; inode 為 struct itag 的結構型態變數,ip 為其指標。每一個結構包含兩欄,id 欄為准考證號碼,index 欄為相對應記錄編號(索引)。在程式裡您可宣告一個 inodes[] 陣列,將 sfile.txt 檔每筆記錄讀入陣列裡。 #define MAXNODES 300 struct itag inodes[MAXNODES]; 就可對該陣列使用 qsort() 加於排序或使用 bsearch() 搜尋了。 正修科技大學計算機中心
【說明】 程式 query.c 中設計一個 setupinodes() 函式負責從 idfile.txt 逐一 讀入至 inodes[] 陣列,並傳回總筆數,存入 idcount 變數。 qsort(inodes, idcount, sizeof(struct itag), comp); 將 inodes[] 陣列依准考證號碼由小至大排序。若 idfile.txt 已經排序 過了,那麼這個 qsort() 呼叫就可免了。 ip=(struct itag *)bsearch(&n, inodes, idcount, sizeof(struct itag), comp); 整數變數 n 是從鍵盤輸入的准考證號碼,在 inodes[] 裡依 comp() 的 比較方法搜尋,找到時 ip 為該 struct itag 節點的指標,找不到時其 指標值為 NULL。找到的話索引值為 ip->index,它是該 n 准考證號碼 在 sfile.dat 裡的記錄編號。 shownode((long)ip->index, sfile); 從 sfile 檔案讀取第 ip->index 筆記錄,並顯示。 正修科技大學計算機中心
【程式query.c執行結果】 請輸入准考證號碼(-1結束): 23010005 <Enter> 記錄編號=2 准考證號碼=23010005 姓名=李三多 性別=1 總分=455 請輸入准考證號碼(-1結束): 23010004 <Enter> 記錄編號=10 准考證號碼=23010004 姓名=李四 性別=2 總分=444 請輸入准考證號碼(-1結束): 23010001 <Enter> 記錄編號=0 准考證號碼=23010001 姓名=陳一心 性別=1 總分=411 請輸入准考證號碼(-1結束): -1 <Enter> 正修科技大學計算機中心
【例題update】 將指定的准考證號碼記錄從考生資料庫 sfile.dat 讀入,修改資料欄值後再輸出至同一個位置。 【說明】 將指定記錄編號的記錄使用 fread() 函式讀入至 snode 結構變數中,將每一欄的資料顯示出來後,再輸入各欄的值。更改後使用 fseek() 定位 於原來記錄位置後再將更改後的資料寫入。 正修科技大學計算機中心
【程式update.c執行結果】 請輸入准考證號碼(-1結束): 23010004 <Enter> 記錄編號=10 准考證號碼=23010004 姓名=李四 性別=2 總分=444 請輸入姓名: 李四雲 <Enter> 請輸入性別: 2 <Enter> 請輸入總分: 444 <Enter> 請輸入准考證號碼(-1結束): -1 <Enter> 請輸入准考證號碼(-1結束): 23010004 <Enter> 記錄編號=10 准考證號碼=23010004 姓名=李四雲 性別=2 總分=444 正修科技大學計算機中心