檔案的儲存單位是位元組(byte),一個位元組(byte) 由8個位元(bit) 組成,一個位元(bit)可以表示兩個數值 0 與 1,可以用連續位元(bit) 的組合來表示更大的二進位數值。
範例:
1 個位元(bit): 可表示 (2 的 1 次方 = 2種值,從 0 ~ 1)
(二進制) (十進制) (16進制)
0 0 0x00
1 1 0x01
2 個位元(bit): 可表示 (2 的 2 次方 = 4種值,從 0 ~ 3)
(二進制) (十進制) (16進制)
00 0 0x00
01 1 0x01
10 2 0x02
11 3 0x03
一個位元組(byte),8 個位元(bit) 可表示 (2 的 8 次方 = 256種值,從 0 ~ 255)
(二進制) (十進制) (16進制)
00000000 0 0x00
00000001 1 0x01
00000010 2 0x02
...
11111101 253 0xFD
11111110 254 0xFE
11111111 255 0xFF
兩個位元組(bytes),16 個位元(bit),可表示 (2 的 16 次方 = 65536種值,從 0 ~ 65535)
(二進制) (十進制) (16進制)
0000000000000000 0 0x0000
0000000000000001 1 0x0001
0000000000000010 2 0x0002
...
1111111111111101 65533 0xFFFD
1111111111111110 65534 0xFFFE
1111111111111111 65535 0xFFFF
因為檔案的內容是由一個一個位元組(byte) 組合而成,把兩個或四個位元組(bytes) 組合起來一起看,可以表示不同的數值範圍。
文字檔(text file) 是怎麼被儲存的?
其實文字檔的內容也是由位元組(byte) 組成,只是要怎麼去解讀其內容。
最基本的 ASCII 編碼,以一個位元組(byte) 為單位,定義了數字範圍 0 - 127 與文數字等符號的對應表。
數字 32 (十進制) 表示空白 (space)
數字 97 (十進制) 表示英文小寫字母 a
數字 116 (十進制) 表示英文小寫字母 t
數字 104 (十進制) 表示英文小寫字母 h
數字 105 (十進制) 表示英文小寫字母 i
數字 115 (十進制) 表示英文小寫字母 s
數字 101 (十進制) 表示英文小寫字母 e
數字 120 (十進制) 表示英文小寫字母 x
文字編輯軟體開啟文字檔時,根據 ASCII 編碼,將每一個位元組(byte) 代表的數值轉換成對應的符號,顯示出來。
使用底下 C code 將存在 str 陣列之一連串的位元組(byte),寫入檔案 text_file。
範例使用方式
1. 把底下 C code 存成檔案,命名如 main.c
2. 輸入底下指令將 main.c 編譯成 main
gcc -o main main.c
3. 輸入底下指令執行程式
./main
4. 分別用 od 與 cat 指令觀察 text_file 的內容。
範例:
#include<stdio.h>
#define FILENAME "text_file"
int main(int argc, char *argv[])
{
FILE *fp = NULL;
char str[] = {116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 120, 116};
fp = fopen(FILENAME, "w");
fwrite(str, sizeof(str), 1, fp);
fclose(fp);
return 0;
}
1. 執行指令 od -t d1 text_file
0000000 116 104 105 115 32 105 115 32 97 32 116 101 120 116
0000016
使用 od 來顯示檔案每一個位元組(byte) 的內容,檔案的內容以十進制,一個一個位元組(bytes) 的方式顯示,可以看出檔案內容就如同 C code 裡的 str 陣列。
this is a text
改用 cat 來顯示 text_file 後會看到一個句子,由此可知,實際上文字檔內容也是數字,只是 cat 以 ASCII 編碼的方式來解讀並顯示此檔案內容。
底下另一個 C code 可以建立一個內容是 this is a text 的文字檔範例。
此範例呼叫 fprintf() 可以直接以 this is a text 為參數建立檔案。
範例:
#include<stdio.h>
#define FILENAME "text_file"
int main(int argc, char *argv[])
{
FILE *fp = NULL;
fp = fopen(FILENAME, "w");
fprintf(fp, "%s", "this is a text");
fclose(fp);
return 0;
}
1. 執行指令 od -t d1 text_file
0000000 116 104 105 115 32 105 115 32 97 32 116 101 120 116
0000016
結果如同上一個範例,檔案內容完全相同
2. 執行指令 cat text_file
this is a text
顯示同上一個範例一樣的字串,由此可知,當要產生文字檔時,應該選用 fprintf 的方式會比 fwrite 的方式容易的多。
二進位檔(binary file) 是怎麼被儲存的?
從上面可知,二進位檔也是一連串位元組(byte) 所組成,但是如果不知道解讀二進位檔內容的方式,也就是位元組的組成結構,則無法使用此檔案,用文字編輯器打開就像一堆亂碼。
其實二進位檔的範圍很廣
例如:
1. 執行檔 (如剛剛 C code 產生的 main 執行檔本身)
2. 圖檔
3. 音樂檔
4. 你自己定義結構的檔案
底下 C code 建立一個名為 MY_BIN_FILE 的資料結構,對資料結構的成員填值後寫入檔案,再使用 od 與 cat 觀察內容。
範例:
#include<stdio.h>
#define FILENAME "bin_file"
struct MY_BIN_FILE
{
unsigned char num0;
unsigned char num1;
unsigned short num2;
unsigned int num3;
};
int main(int argc, char *argv[])
{
struct MY_BIN_FILE data;
FILE *fp = NULL;
fp = fopen(FILENAME, "w");
data.num0 = 1;
data.num1 = 100;
data.num2 = 30000;
data.num3 = 1234567890;
fwrite(&data, sizeof(data), 1, fp);
fclose(fp);
return 0;
}
因為 num0 與 num1 都是一個位元組,所以使用 d1 顯示(以每一個位元組來做解讀)。
從結果可以看出,確實有符合寫入的預期。
od -t d1 bin_file
0000000 1 100 48 117 -46 2 -106 73
0000010
因為 num2 是二個位元組,所以使用 d2 顯示(以每兩個位元組來做解讀)。
從結果可以看出,確實有符合寫入的預期。
od -t d2 bin_file
0000000 25601 30000 722 18838
0000010
因為 num3 是四個位元組,所以使用 d4 顯示(以每四個位元組來做解讀)。
從結果可以看出,確實有符合寫入的預期。
od -t d4 bin_file 從結果可以看出,確實有符合寫入的預期。
0000000 1966105601 1234567890
0000010
由此可知,如果你有一個二進位檔(binary file),但是不知道其組成結構,就算讀取其內容,還是無法使用。必須找到正確的軟體去開啟。
到底該選擇存成二進位檔(binary file) 還是文字檔(text file)?
因為數字也可以用文字檔的方式做儲存,只是檔案佔用的空間會變多,但好處是可以透過文字編輯軟體做閱讀與編輯,不需要透過特定程式才能解讀。
底下 C code 修改了一下前面的範例,改用 fprintf 的方式來寫入成文字檔。且相同的四個數字資訊也以文字的方式儲存在檔案中,使用 od 與 cat 觀察內容,並用 wc 觀察檔案大小。
範例:
#include<stdio.h>
#define FILENAME "text_file"
struct MY_BIN_FILE
{
unsigned char num0;
unsigned char num1;
unsigned short num2;
unsigned int num3;
};
int main(int argc, char *argv[])
{
struct MY_BIN_FILE data;
FILE *fp = NULL;
fp = fopen(FILENAME, "w");
data.num0 = 1;
data.num1 = 100;
data.num2 = 30000;
data.num3 = 1234567890;
fprintf(fp, "%d %d %d %d", data.num0, data.num1, data.num2, data.num3);
fclose(fp);
return 0;
}
數字都以 ASCII 的方式被儲存在檔案中
數字 48 (十進制) 表示數字 0
數字 49 (十進制) 表示數字 1
od -t d1 text_file
0000000 49 32 49 48 48 32 51 48 48 48 48 32 49 50 51 52
0000020 53 54 55 56 57 48
使用 cat 可以看到數字,而不是亂碼
cat text_file
1 100 30000 1234567890
使用 wc 指令加上 -c,計算檔案佔用的位元組(byte) 數,很明顯,為了保留相同的"資訊",存成二進位檔(binary file) 只有佔用 8 個位元組,但是文字檔(text file) 卻佔用 22 個位元組。
wc -c bin_file
8 bin_file
wc -c text_file
22 text_file
注意:以上 C code 範例為了減少版面,沒有做函數回傳值的檢查,並不是很 solid 的程式。
沒有留言:
張貼留言