中文亂碼
上篇《ZBar-windows下編譯和使用》已經成功解析了條形碼,但目標是二維碼,經測試二維碼中文會出現亂碼。
下圖二維碼的內容是「http123測試456」,解析後的內容爲「http123嫺嬭瘯456」windows
搜索了一下關鍵詞,解決方案以下http://blog.csdn.net/zizi7/article/details/51880129數組
修改文件 zbar/qrcode/qrdectxt.c:ide
1 latin1_cd=iconv_open("GB18030","UTF-8"); 2 /*But this one is often used, as well.*/ 3 sjis_cd=iconv_open("GB2312","UTF-8"); 4 /*This is a trivial conversion just to check validity without extra code.*/ 5 utf8_cd=iconv_open("UTF-8","UTF-8"); 6 ... 7 enc_list[1]=sjis_cd; 8 enc_list[0]=latin1_cd; 9 enc_list[2]=utf8_cd;
從新編譯運行後,正確輸出「http123測試456」函數
本身實現中文解碼
ZBar解析後的字符原始輸出是UTF-8格式,而後使用了iconv將其轉換爲相應的字符編碼,但最終目標是移植到STM32F4上,若是要直接輸出中文編碼,有幾種方案:測試
1. 把iconv移植到STM32F4上
2. 本身實現UTF8-8轉中文編碼
3. 把編碼工做交給上位機ui
字符集和編碼格式
搜索了一下字符編碼規則,以爲方案2比方案1,3更容易實現。這裏先簡單介紹下與本文相關的字符集和編碼格式。
1. Unicode
Unicode是字符集,也叫萬國碼,顧名思義就是包含全部國家的文字。
2. GB18030
GB18030是中文字符集,能夠認爲是Unicode的一個子集,還有其餘GBXXXXX的中文字符集,他們的關係簡單來講就是包含的中文字符個數不同。簡單起見,這裏只是使用2個字節的GB18030,一共20902個漢字,也基本覆蓋常見的漢字了。書讀得少,4個字節的漢字也沒認識幾個。
3. UTF-8
UTF-8是Unicode字符集的一種編碼格式,還有其餘UTF-16,UTF-32,ZBar使用了 UTF-8。
UTF-8最大的一個特色,就是它是一種變長的編碼方式。它可使用1~6個字節表示一個符號,根據不一樣的符號而變化字節長度。
UTF-8的編碼規則很簡單,只有二條:
1)對於單字節的符號,字節的第一位設爲0,後面7位爲這個符號的unicode碼。所以對於英語字母,UTF-8編碼和ASCII碼是相同的。
2)對於n字節的符號(n>1),第一個字節的前n位都設爲1,第n+1位設爲0,後面字節的前兩位一概設爲10。剩下的沒有說起的二進制位,所有爲這個符號的unicode碼。
(具體例子能夠參考http://blog.csdn.net/xiaolei1021/article/details/52093706)this
下表總結了編碼規則,字母x表示可用編碼的位。編碼
4. GB18030,Unicode,UTF-8的關係spa
UTF-8轉GB18030實現
瞭解相關字符集和編碼格式,能夠開始寫轉換代碼了。.net
1. 須要一個GB18030字符集,其實就是一個數組,實現代碼以下(VS下編譯)
調用UnicodeToGB18030Table函數生成一個GB18030字符集數組。
1 char* UnicodeToGB18030String(const wchar_t* unicode_str) 2 { 3 UINT code_page = 54936 ; //GB2312 :936 GB18030: 54936 4 int len=WideCharToMultiByte(code_page,0,unicode_str,-1,NULL,0,NULL,NULL); 5 char* buf=new char[len+1]; 6 WideCharToMultiByte(code_page,0,unicode_str,-1,buf,len,NULL,NULL); 7 buf[len]=0; 8 return buf; 9 } 10 int UnicodeToGB18030Table(void) 11 { 12 FILE *table; 13 wchar_t unicode[]={0x4E00,0}; 14 char* gb18030; 15 int cnt=0; 16 table = fopen("unicode_to_gb18030_table.c","w"); 17 if(table == NULL) 18 { 19 printf("can not open unicode_to_gb18030_table.c\n"); 20 system("pause"); 21 exit(0); 22 } 23 fprintf(table, "%s", "const char unicode_to_gb18030_table1[] = {\n"); 24 for(unicode[0]=0x4E00; unicode[0]<=0x9FA5; unicode[0]++) 25 { 26 gb18030 = UnicodeToGB18030String(unicode); 27 28 if(unicode[0]==0x9FA5) 29 { 30 fprintf(table, "0x%X,0x%X ", (UINT8)gb18030[0],(UINT8)gb18030[1]); 31 } 32 else 33 { 34 fprintf(table, "0x%X,0x%X, ", (UINT8)gb18030[0],(UINT8)gb18030[1]); 35 } 36 37 cnt ++; 38 if(cnt == 16) 39 { 40 cnt = 0; 41 fprintf(table, "\n"); 42 } 43 } 44 45 fprintf(table, "\n};"); 46 fclose(table); 47 }
2. 經過查表,將UTF-8轉爲GB18030
1 int zbar_utf8_to_gb18030 (uint8_t* utf8_code, uint32_t utf8_len, uint8_t* gb18030) 2 { 3 uint8_t utf8_bytes[3];//該數組最大爲6個字節,但這裏只考慮3個字節的中文編碼 4 uint32_t i = 0, j = 0; 5 uint16_t unicode_value; 6 uint8_t* unicode = gb18030; 7 8 for(i=0; i< utf8_len; i+=3) { 9 utf8_bytes[0] = utf8_code[i+0] & 0x0F; 10 utf8_bytes[1] = utf8_code[i+1] & 0x3F; 11 utf8_bytes[2] = utf8_code[i+2] & 0x3F; 12 13 unicode[j] = (utf8_bytes[1] >> 2) | ((utf8_bytes[0]) << 4); 14 unicode[j+1] = utf8_bytes[2] | ((utf8_bytes[1] & 0x03) << 6); 15 unicode_value = (unicode[j]<<8) + unicode[j+1]; 16 if(unicode_value>=0x4E00){ 17 gb18030[j] = unicode_to_gb18030_table1[(unicode_value-0x4E00)*2]; 18 gb18030[j+1] = unicode_to_gb18030_table1[(unicode_value-0x4E00)*2 + 1]; 19 } 20 j += 2; 21 } 22 return 0; 23 }
中文字符集和編碼轉碼函數有了,下一步就是替換ZBar源碼的編碼轉換部分。
刪掉zbar/qrcode/qrdectxt.c 中iconv相關的代碼,將zbar_utf8_to_gb18030函數加入
qr_code_data_list_extract_text函數中:
1 int qr_code_data_list_extract_text(const qr_code_data_list *_qrlist, 2 zbar_image_scanner_t *iscn, 3 zbar_image_t *img) 4 { 5 .... 6 case QR_MODE_BYTE:{ 7 int gb18030_cnt = zbar_utf8_to_gb18030(entry->payload.data.buf, entry->payload.data.len, sa_text+sa_ntext); 8 sa_ntext += gb18030_cnt; 9 } 10 break; 11 .... 12 }
從新編譯運行後
正確輸出「http123測試456」
坐等下班,回家過年...................