問題
C語言中漢字如何存儲?梳理思路!
答案
在計算機中,一個英文字符占1個字節,漢字占兩個字節,如果用char字符數組存儲字符時,需要在最后面自動加上一個字節的結束符“\0”

漢字轉進制輸出
比如:
//英文字符,占4個字節
char Info[] = "abc";
printf("%s\n",Info);
printf("Info長度:%d\n",sizeof(Info));
//中文,占5個字節
char Han[]="中國";
printf("%s\n",Han);
printf("Han長度:%d\n",sizeof(Han));

不同的編碼方式,漢字存儲的字節數量不同,比如:
GB2312編碼:一個漢字兩個字節
UTF-8編碼: 一個漢字三個字節
GBK編碼:一個漢字兩個字節
UTF-16:一個漢字3個字節

GB2312 --> GBK --> GB18030 是中文編碼的三套方案,出現的時間從早到晚,收錄的字符數目依次增加,並且向下兼容。GB2312 和 GBK 收錄的字符數目較少,用 1~2個字節存儲;GB18030 收錄的字符最多,用1、2、4 個字節存儲。
1) 從整體上講,GB2312 和 GBK 的編碼方式一致,具體為:
- 對於 ASCII 字符,使用一個字節存儲,並且該字節的最高位是 0,這和 ASCII 編碼是一致的,所以說 GB2312 完全兼容 ASCII。
- 對於中國的字符,使用兩個字節存儲,並且規定每個字節的最高位都是 1。
例如對於字母A,它在內存中存儲為 01000001;對於漢字中,它在內存中存儲為 11010110 11010000。由於單字節和雙字節的最高位不一樣,所以字符處理軟件很容易區分一個字符到底用了幾個字節。
2) GB18030 為了容納更多的字符,並且要區分兩個字節和四個字節,所以修改了編碼方案,具體為:
- 對於 ASCII 字符,使用一個字節存儲,並且該字節的最高位是 0,這和 ASCII、GB2312、GBK 編碼是一致的。
- 對於常用的中文字符,使用兩個字節存儲,並且規定第一個字節的最高位是 1,第二個字節的高位最多只能有一個連續的 0(第二個字節的最高位可以是 1 也可以是 0,但是當它是 0 時,次高位就不能是 0 了)。注意對比 GB2312 和 GBK,它們要求兩個字節的最高位為都必須為 1。
- 對於罕見的字符,使用四個字節存儲,並且規定第一個和第三個字節的最高位是 1,第二個和第四個字節的高位必須有兩個連續的 0。
例如對於字母A,它在內存中存儲為 01000001;對於漢字中,它在內存中存儲為 11010110 11010000;對於藏文གྱུ,它在內存中的存儲為 10000001 00110010 11101111 00110000。
字符處理軟件在處理文本時,從左往右依次掃描每個字節:
- 如果遇到的字節的最高位是 0,那么就會斷定該字符只占用了一個字節;
- 如果遇到的字節的最高位是 1,那么該字符可能占用了兩個字節,也可能占用了四個字節,不能妄下斷論,所以還要繼續往后掃描:
- 如果第二個字節的高位有兩個連續的 0,那么就會斷定該字符占用了四個字節;
- 如果第二個字節的高位沒有連續的 0,那么就會斷定該字符占用了兩個字節。
可見,當字符占用兩個或者四個字節時,GB18030 編碼要檢測兩次,處理效率比 GB2312 和 GBK 都低。
GBK 於 1995 年發布,這一年也是互聯網爆發的元年,國人使用電腦越來越多,也許是 GBK 這頭豬正好站在風口上,它就飛起來了,后來的中文版 Windows 都將 GBK 作為默認的中文編碼方案。
注意,這里我說 GBK 是默認的中文編碼方案,並沒有說 Windows 默認支持 GBK。Windows 在內核層面使用的是 Unicode 字符集(嚴格來說是 UTF-16 編碼),但是它也給用戶留出了選擇的余地,如果用戶不希望使用 Unicode,而是希望使用中文編碼方案,那么這個時候 Windows 默認使用 GBK(當然,你可以選擇使用 GB2312 或者 GB18030,不過一般沒有這個必要)。
漢字編碼輸出
漢字編碼並輸出示例:
//漢字編碼
unsigned char Han[]="中國";
printf("漢字:%s\n",Han);
printf("十六進制:");
for(int i=0;i<4;i++)
{
printf("%X",Han[i]);
}
printf("\n十進制:");
for(int i=0;i<4;i++)
{
printf("%d",Han[i]);
}

漢字編碼輸出存儲
漢字編碼存儲示例:
//漢字編碼存儲
unsigned char Han[] = "中國";
FILE* fp = fopen("out.txt", "w");//輸出
printf("漢字:%s\n", Han);
fputs("十六進制:", fp);
for (int i = 0; i < 4; i++)
{
fprintf(fp, "%X", Han[i]);
}
fseek(fp, 0, SEEK_END);
fputs("\n十進制:", fp);
for (int i = 0; i < 4; i++)
{
fprintf(fp, "%d", Han[i]);
}
fclose(fp);

漢字存儲到數組
#include <stdio.h>
#include <string.h>
#define MAX_LINE 100 //每行最大字節數
void main()
{
unsigned char strLine[MAX_LINE];
int len = 0;
FILE *fp = fopen("D:\\code\\vc\\C\\plaintext_keywords.txt", "r");
if(NULL == fp)
{
printf("failed to open dos.txt\n");
}
while (!feof(fp))
{
fgets(strLine,7,fp);
printf("%s",strLine);
}
fclose(fp);
printf("\n");
system("pause");
}

參考
1、漢字的存儲
