嵌入式開發中中文的識別、顯示一直是一個比較讓人頭疼的問題,這是因爲嵌入式系統的精簡要求所致使的。我在前一個項目中用Qt作終端的顯示界面時,就被中文字符的顯示搞得焦頭爛額,最後仍是在網上下了一個文泉驛的中文字體庫才解決問題,不過並不使人滿意,主要是字體庫太大了,影響了性能。今天在項目中又遇到了中文字符的編碼轉換和顯示問題,趁機學習總結一下。shell
國際標準有UTF-八、UTF-16等,國家標準有GB23十二、GB18030、GBK等,至於這些標準的編碼實現就不在這裏介紹了。編碼標準實現中文字符在計算機內部的機器表示,可是怎麼將這些內部表示在顯示設備上顯示出來,則不是編碼標準的範疇了,這就須要中文字庫的做用。中文字庫包含已經作好的顯示「模板」(字模),經過中文的編碼值來映射漢字在字庫中的字模索引,從而使用字模來實現中文顯示。緩存
可是大部分的中文字庫都是映射國家標準的,如GB2312的HZK16點陣字庫。不知道文泉驛字體庫是不是直接映射UTF-8,仍是也是GB2312。所以,當在程序中涉及兩種不一樣的中文編碼方式時,就須要進行編碼方式的轉換,今天我遇到的就是要實現中文字符UTF-8到GB2312的編碼轉換。函數
HZK16是符合GB2312標準16*16的點陣宋體字庫,支持的漢字有6763個,符號682個。其中一級漢字有 3755個,按聲序排列,二級漢字有3008個,按偏旁部首排列。性能
有關點陣漢字顯示的更多瞭解,能夠閱讀:點陣漢字顯示學習
Linux系統中的iconv命令能夠實現一個文件的總體編碼轉換,詳細幫助能夠經過man iconv查看。iconv命令的基本格式爲:字體
<!-- lang: shell --> iconv -f from-encoding -t to-encoding input-file -o output-file
至於from-encoding和to-encoding的可用值能夠經過選項-l進行查看:編碼
<!-- lang: shell --> iconv -l
能夠在輸出結果中找到GB23十二、UTF-八、GB18030、UTF16。上述的中文編碼文件轉換:.net
<!-- lang: shell --> iconv -f GB2312 -t UTF8 display_utf8.c -o display_gb2312.c
POSIX.1-2001標準和UNIX 98規範中均包含了iconv編碼轉換函數族,它們分別是:code
<!-- lang: cpp --> #include <iconv.h> //成功返回轉換句柄,失敗返回-1;字符集參考iconv -l命令結果 iconv_t iconv_open(const char *tocode, const char *fromcode); //成功返回不可逆編碼轉換的字符數,失敗返回-1;查看幫助:man 3 iconv size_t iconv(iconv_t cd,char **inbuf,size_t *inbytesleft,char **outbuf,size_t *outbytesleft); //成功返回0,失敗返回-1 int iconv_close(iconv_t cd);
這三個函數均是從glibc2.1版本開始被引入的,所以使用它們須要有glibc庫的支持。此次咱們的項目中由於不是用的glibc庫,因此無法用它們。不過我仍是順便學習了,下面是我編寫的一個簡單的實例程序,在這裏面仍是有一些注意點的,看程序:blog
<!-- lang: cpp --> #include <stdio.h> #include <stdlib.h> #include <iconv.h> #include <string.h> #define OUTLEN 128 int display(char *incode, int len); int main() { char *string = "開源中國"; iconv_t cd; int inbuf_len = strlen(string); char outbuf[OUTLEN]; char *pin = string; char *pout = &outbuf[0]; //用"pout=&outbuf" 會引起SIGSERV信號,致使段錯誤 int outbuf_len = OUTLEN; memset(outbuf, 0, OUTLEN); //清空輸出緩存,不然會有意外結果的 printf("Originial Data:\n"); //打印轉換前的一些參數和信息,以進行比較 printf("\tpin str: %s, outbuf str:%s\n", pin, outbuf); printf("\tinbuf_len=%d, outbuf_len=%d\n", inbuf_len, outbuf_len); printf("\tstrlen(outbuf)= %d\n", strlen(outbuf)); cd = iconv_open("GB2312", "UTF8"); if(cd == 0) return EXIT_FAILURE; int count = iconv(cd, &pin, &inbuf_len, &pout, &outbuf_len); printf("iconv count : %d\n", count);//觀察iconv返回值,理解不可逆轉換含義 iconv_close(cd); printf("After Converted Data:\n"); //注意發生變化的變量 printf("\tpin str: %s, gb2312 str:%s\n", pin, outbuf ); printf("\tinbuf_len=%d, outbuf_len=%d\n", inbuf_len, outbuf_len); printf("\tstrlen(outbuf)= %d\n", strlen(outbuf)); int i,j; for(i = 0; i < strlen(outbuf); i += 2) { display (outbuf+i, 2); //使用HZK16字庫顯示GB2312編碼的中文點陣 } return EXIT_SUCCESS; } int display(char *incode, int len) { int i, j, k; char mat[16][2]; FILE *HZK=NULL; unsigned char qh,wh; unsigned long offset; qh=incode[0]-0xa0;//得到區碼,中文編碼從0xA1開始 wh=incode[1]-0xa0; //得到位碼,中文編碼從0xA1開始 offset=(94*(qh-1)+(wh-1))*32; //獲得漢字在HZK16字庫中的字節偏移位置 printf("區碼:%d,位碼:%d\n",qh,wh); if((HZK=fopen("HZK16","rb")) ==NULL) { perror("Can't Open hzk16"); return (EXIT_FAILURE); } fseek(HZK,offset,SEEK_SET); fread(mat,32,1,HZK);//讀取漢字的16*16點陣字模 fclose(HZK); for(i = 0; i < 16; i++) //顯示點陣 { for(j = 0; j < 2; j++) for(k = 0; k < 8; k++) { if(mat[i][j] & (0x80>>k)) printf("*"); else printf(" "); } printf("\n"); } return EXIT_SUCCESS; }
運行程序,觀察一下輸出,能讓咱們更好地理解iconv函數: