『C語言』二進位檔(binary file)與文字檔(text file)的差異 程式範例/完整說明

歡迎分享:
  •  
  •  
  •  
  •  
  •  
  •  

檔案內容是怎麼被儲存的?

檔案的儲存單位是位元組(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 陣列。

2. 執行指令

cat text_file
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

使用 cat bin_file 會看到亂碼,請自行實驗。

由此可知,如果你有一個二進位檔(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 的程式。

發表迴響