最近在搗鼓C,在CSDN中第一次發帖,不對之處望指正。
本地化
很多人認為C語言只支持ascii碼,這是誤解,你用printf(“這是中文”)同樣可以輸出中文,用fputs(“C語言本地化”)也可以向檔案中寫入中文。那么C語言默認使用什么編碼呢?這與作業系統、所在區域、編譯器有很大的關系,因為C標準并未規定使用什么編碼。另外可能你會發現雖然可以輸出中文字串,但并不能用char c=’中’來宣告一個中文字符,如果用strlen(“C語言”)來求字串長度,給出的值為5,因此默認情況下C并不能很好的支持中文。如何讓C語言很好的支持中文呢?這需要我們從基本編碼概念講起。
計算機剛剛發明時,只支持ascii碼,也就是說只支持英文,隨著計算機在全球興起,各國創建了屬于自己的編碼來顯示本國文字,中文首先使用GB2132編碼,它收錄了6763個漢字,平日里我們作業學習大概只會用到3千個漢字,因此日常使用已經足夠,GBK收錄了21003個漢字,遠超日常漢字使用需求,不管是日用還是商用都能輕松搞定,因此從windows95開始就將GBK作為默認漢字編碼,而GB18030收錄了27533個漢字,為漢字研究、古籍整理等領域提供了統一的資訊平臺基礎,平常根本使用不到這種編碼。這些中文編碼本身兼容ascii,并采用變長方式記錄,英文使用一個位元組,常用漢字使用2個位元組,罕見字使用四個位元組。后來隨著全球文化不斷交流,人們迫切需要一種全球統一的編碼能夠統一世界各地字符,再也不會因為地域不同而出現亂碼,這時Unicode字符集就誕生了,也稱為統一碼,萬國碼。新出來的作業系統其內核本身就支持Unicode,由萬國碼的名稱就可以想象這是一個多么龐大的字符集,為了兼容所有地區的文字,也考慮到空間和性能,Unicode提供了3種編碼方案:
utf-8 變長編碼方案,使用1-6個位元組來儲存
utf-32 定長編碼方案,始終使用4個位元組來儲存
utf-16 介于變長和定長之間的平衡方案,使用2個或4個位元組來儲存
utf-8由于是變長方案,類似GB2132和GBK量體裁衣,最節省空間,但要通過第一個位元組決定采用幾個位元組儲存,編碼最復雜,且由于變長要定位文字,就得從第一個字符開始計算,性能最低。utf-32由于是定長方案,位元組數固定因此無需解碼,性能最高但最浪費空間。utf-16是個怪胎,它將常用的字放在編號0 ~ FFFF之間,不用進行編碼轉換,對于不常用字的都放在10000~10FFFF編號之后,因此自然的解決變長的問題。注意對于這3種編碼,只有utf-8兼容ascii,utf-32和utf-16都不支持單位元組,由于utf-8最省流量,兼容性好,后來解碼性能也得到了很大改善,同時新出來的硬體也越來越強,性能已不成問題,因此很多純文本、源代碼、網頁、組態檔等都采用utf-8編碼,從而代替了原來簡陋的ascii。再來看看utf-16,對于常見字2個位元組已經完全夠用,很少會用到4個位元組,因此通常也將utf-16劃分為定長。Windows內核使用utf-16,linux,mac,ios內核使用的是utf-8,我們就不去爭論誰好誰壞了。另外雖然windows內核為utf-16,但為了更好的本地化,控制面板提供了區域選項,如果設定為簡體就是GBK編碼,在win10中,控制臺默認編碼為gbk,文本默認編碼為utf-8,其它第三方軟體就不好說了,它們默認編碼各不相同,現在我們來看看C語言采用什么編碼。
C語言源代碼采用什么格式取決于IDE環境,通常是utf-8或ANSI,什么是ANSI編碼呢?相比unicode它是采取一種思路,嚴格來說ANSI不是一種編碼,而是一種替代方案,力求找到顯示內容的最低編碼需求,如果內容只有英文字符就使用ascii,如果發現漢字就替換成本地的GBK編碼,如果發現既有漢字又有日語又有韓語是否會自動選擇unicode這個沒有試過。前面講過C語言使用的編碼和作業系統、區域選擇、編譯器都有關系,但有一個現象,通常原始碼采用什么格式的編碼,運行時就使用這樣的編碼,因此我們可以通過源代碼先看看這個IDE會使用什么編碼。在Dev C++中測驗的結果是,如果只有英文原始碼默認采用utf8,現在的編輯器很少使用純ascii了,如果發現原始碼里面有漢字,則將編碼改為ANSI,由于windows控制臺默認使用ANSI,因此可以顯示標準輸入輸出中的漢字。
如果只需要向控制臺輸出一段字串或者向檔案中寫入一段中文,那么使用標準輸入輸出函式即可,但如果要操作字符就不行了,char資料型別不夠表示2個位元組的中文,這是歷史遺留的問題,對于變長和定長,在計算機行業種還有一個術語稱為窄字符和寬字符,此時我們需要依賴寬位元組庫wchar,匯入wchar.h后就可以使用寬位元組了,此時每個字符用2個位元組表示,也就是說wchar庫使用定長字符編碼,測驗代碼如下:
#include <stdio.h>
#include <wchar.h>
int main()
{
wchar_t wc1=L'a';
wchar_t wc2=L'寬';
printf("%d,%d",sizeof(wc1),sizeof(wc2));
return 0;
}
上面代碼使用wchar_t宣告一個寬字符,字符前面要添加L,使用sizeof()檢測的結果是無論中文還是英文都是2位元組,如果要顯示一個寬位元組符,需要換成putwchar()和wprintf()函式,如下:
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main()
{
setlocale(LC_ALL, "");
wchar_t wc1=L'a';
wchar_t wc2=L'中';
putwchar(wc1);
putwchar(wc2);
wprintf(L"%c%c",wc1,wc2);
return 0;
}
這兩個方法需要先呼叫setlocale()函式進行初始化,告訴它們用什么編碼顯示寬字符,setlocale()的格式為:
char* setlocale (int category, const char* locale)
category是型別,表示區域編碼影響到的型別,型別有時鐘、貨幣、字符排序等,通常設定為常數LC_ALL表示影響所有型別。locale表示區域,windows和linux表示區域的方式各不相同,例如windows表示簡體中文用”chs”,linux用”zh_CN”,但有3個區域總是相同的:”C”表示中立地域,不表示任何一個地區,只對小數點進行了設定,是默認值;“”表示本地區域;NULL表示不指定區域僅僅回傳區域名稱。寬字符輸入輸出獨立于標準輸入輸出,它只認setlocale()設定的值,標準輸入輸出使用哪種編碼與它無關,而顯示寬字符用哪種編碼,用變長還是定長是編譯器的事情。如果不設定區域,默認區域為”C”,這個區域只能顯示英文,無法顯示任何中文字符,實際測驗結果也是如此,因此先要呼叫setlocale()將區域設定為本地才行。由于setlocale()包含在locale.h中,因此要先匯入locale庫,下面我們再來看看寬字串:
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main()
{
setlocale(LC_ALL, "");
wchar_t wstr[]=L"寬字串";
wprintf(L"%s,%c\n",wstr, wstr[1]);
printf("%d",wcslen(wstr));
return 0;
}
宣告寬字串后應該使用wcslen()函式獲取字串的長度,使用下標和指標運算都能正確定位,這也是因為寬字符采用定長的原因。除此之外,字串的復制、連接等都有配套的寬字串操作方法,可以自己查詢,但是gets()和puts()沒有對應的寬字串函式是個遺憾。其實在后來的標準庫中已經加入了對寬字串的支持,測驗代碼如下:
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main()
{
setlocale(LC_ALL, "");
wchar_t c=L'國';
wchar_t wstr[]=L"寬字串";
printf("%lc,%ls",c, wstr);
return 0;
}
這樣printf()就可以將數值、普通字符和寬字符一同輸出了,但如果你用puts()直接輸出寬字符依然是亂碼。
uj5u.com熱心網友回復:
好文,感謝分享。。uj5u.com熱心網友回復:
鼓勵支持一下uj5u.com熱心網友回復:
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/225170.html
標籤:C語言
