二維碼開源庫ZBar-實現中文解碼


中文亂碼

  上篇《ZBar-windows下編譯和使用》已經成功解析了條形碼,但目標是二維碼,經測試二維碼中文會出現亂碼。
下圖二維碼的內容是“http123測試456”,解析后的內容為“http123嫻嬭瘯456”

搜索了一下關鍵詞,解決方案如下http://blog.csdn.net/zizi7/article/details/51880129

修改文件 zbar/qrcode/qrdectxt.c:

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;  
View Code

重新編譯運行后,正確輸出“http123測試456”

自己實現中文解碼

  ZBar解析后的字符原始輸出是UTF-8格式,然后使用了iconv將其轉換為相應的字符編碼,但最終目標是移植到STM32F4上,如果要直接輸出中文編碼,有幾種方案:

1. 把iconv移植到STM32F4上
2. 自己實現UTF8-8轉中文編碼
3. 把編碼工作交給上位機

字符集和編碼格式

搜索了一下字符編碼規則,覺得方案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)

下表總結了編碼規則,字母x表示可用編碼的位。

4. GB18030,Unicode,UTF-8的關系

UTF-8轉GB18030實現

了解相關字符集和編碼格式,可以開始寫轉換代碼了。

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 }
View Code

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 }
View Code

 

中文字符集和編碼轉碼函數有了,下一步就是替換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 }
View Code

重新編譯運行后

正確輸出“http123測試456”

 

坐等下班,回家過年...................

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM