在使用Visual Studio 2005進行MFC開發的時候,發現自動添加的注釋變成了亂碼。像這樣:
還有這樣:// TODO: ÔÚ´ËÌí¼ÓרÓôúÂëºÍ/»òµ÷ÓûùÀà// TODO: ÔÚ´ËÌí¼ÓÏûÏ¢´¦Àí³ÌÐò´úÂëºÍ/»òµ÷ÓÃĬÈÏÖµ它們正確的顯示應該是
和// TODO: 在此添加專用代碼和/或調用基類// TODO: 在此添加消息處理程序代碼和/或調用默認值
當保存的時候,還出現了這樣的對話框:
網上找了各種教程,包括什么設置“自動識別不帶簽名的utf-8”什么的,都沒有用。所以考慮自己解決。下面是我的探索過程:
一,保存文件
首先,將文件以“Unicode(UTF-8帶簽名) 代碼頁:65001”的形式進行保存(帶簽名的UTF-8是指有BOM的UTF-8,至於帶BOM和不帶BOM的UTF-8有什么區別,請戳此)。如下圖:
二,查看文件的16進制代碼(就是查看文件實際上保存成什么數據了)
使用WinHex軟件打開剛剛保存的文件(當然,使用UltraEdit也可以),查看文件的16進制代碼。我們找到亂碼的地方,把它的16進制代碼找出來,如下:
為了更清楚地演示,我將亂碼單獨拷出來,一定要注意將文本保存成UTF-8(最好帶BOM,如果使用Windows自帶的文本編輯器編輯就自帶BOM)保存成這樣:
文件對應的16進制代碼為:
最前面的三個字節“EF BB BF”就是前面所述的BOM標記,從第四個字節開始,就是文件的實際內容。觀察后發現,在實際內容部分,奇數位上不是C2就是C3,偶數位的沒有規律,同時,我們找出原話“在此添加專用代碼和/或調用基類”對應的GBK編碼值,進行比較。
表一:亂碼文件中的16進制數據:
0xc3 0x94 0xc3 0x9a 0xc2 0xb4 0xc3 0x8b 0xc3 0x8c 0xc3 0xad 0xc2 0xbc 0xc3 0x93 0xc3 0x97 0xc2 0xa8 0xc3 0x93 0xc3 0x83 0xc2 0xb4 0xc3 0xba 0xc3 0x82 0xc3 0xab 0xc2 0xba 0xc3 0x8d 0x2f 0xc2 0xbb 0xc3 0xb2 0xc2 0xb5 0xc3 0xb7 0xc3 0x93 0xc3 0x83 0xc2 0xbb 0xc3 0xb9 0xc3 0x80 0xc3 0xa0
表二:“在此添加專用代碼和/或調用基類”對應的GBK編碼值,每個字符(漢字或者/)對應一行:
0xd4 0xda 0xb4 0xcb 0xcc 0xed 0xbc 0xd3 0xd7 0xa8 0xd3 0xc3 0xb4 0xfa 0xc2 0xeb 0xba 0xcd 0x2f 0xbb 0xf2 0xb5 0xf7 0xd3 0xc3 0xbb 0xf9 0xc0 0xe0
4,分析
仔細觀察上面的兩個表中的數據,我們不難發現以下規律:
1,將表一每行中奇數位置(除了’/’那行)的C2、C3的值去掉,剩下的值和表二中的數據高度相似。
2,除了’/’那行,表一每行中奇數位置為C2的,后面的偶數位數字就和表二中對應位(表一種第二列對應表二第一列,表一第四列對應表二中第二列)相同,表一每行中奇數位為C3的,后面的偶數位數字加上16進制數0x40后也與表二中對應位相同(對應法則同前)。
3,由於亂碼文件是以utf-8存儲的,但是經過轉換后得到的編碼為GBK,我們大致可以知道,出現亂碼的原因就是visual studio 2005將兩種編碼搞混了,這應該算是一個bug吧。。畢竟visual studio 2013就從來沒有碰到過。。
5,解決問題
根據上面的規律,我們使用二進制方式讀取utf-8格式編碼的文件數據后經轉化然后輸出到GBK編碼的文件中即可修正問題了。
按照以上的規律編寫一段簡單的C語言程序:
#include <stdio.h> #include <stdlib.h> int main(int argc, char const *argv[]) { FILE* fp; FILE* fp2; //打開存儲亂碼的文件,utf-8格式,二進制打開 if((fp2=fopen("BadCode.txt","rb+"))==NULL) { printf("Open Source File Failed!\n"); system("pause"); exit(1); } //打開、新建存儲處理后數據的文件 if((fp=fopen("BadCodeH.txt","w+"))==NULL) { printf("Open/Create Destination File Failed!\n"); system("pause"); exit(1); } //紀錄奇數位(高位)的數據 unsigned ch; //紀錄偶數位(低位)的數據 unsigned cl; //獲得數據 ch=fgetc(fp2); //判斷文件的格式,utf-8或者Unicode,並跳過BOM字符 if(ch==0xef) { fgetc(fp2); fgetc(fp2); ch=fgetc(fp2); } else if(ch==0xff) { fgetc(fp2); ch=fgetc(fp2); } //不達結尾 while(!feof(fp2)) { //ASCII字符,正常輸出 if(ch<=0x7f) { fputc(ch,fp); } //奇數位為0xC3,獲得偶數位后加0x40后輸出 else if(ch==0xc3) { cl=fgetc(fp2); cl+=0x40; fputc(cl,fp); } //奇數位為0xC2,獲得偶數位后直接輸出 else if(ch==0xc2) { cl=fgetc(fp2); fputc(cl,fp); } //其他情況,直接輸出 else { fputc(ch,fp); } //獲得下一個數據 ch=fgetc(fp2); } fclose(fp); fclose(fp2); system("pause"); return 0; }
操作實例結果如下圖:
6,更一般的情況(既有正確的中文字符又有亂碼)
我們必須注意到一點:上面的C語言程序只適合一種情況:就是亂碼文檔格式為utf-8且文檔中只存在中文亂碼字符與ASCII字符。但是我們很多時候是源碼中既有正確的中文字符又有亂碼字符,這時上面的程序就無效了,因為我們需要將正確中文字符的utf-8編碼轉換為GBK編碼才可以。我們嘗試修改上面的代碼來解決這個問題。
對於既有亂碼又有正常字符的文件來說,只要將正確的中文字符的utf-8編碼轉化為GBK編碼就解決問題了,所以主要問題的關鍵就是建立一個utf-8與GBK編碼的轉換表。baidu一下,我們很容易找到了這個表,然后,就寫了以下的程序。UnicodeToGBK.txt文件請戳下載地址:
#include <stdio.h> #include <stdlib.h> //將gbk編碼值存入數組中utf-8編碼對應的位置上 bool ReadTable(unsigned* mapValue2) { //聲明文件指針 FILE* fp; //打開轉換表文件,文件中第一列為漢字的GBK編碼,第二列為utf-8編碼 //以可讀寫方式打開 if (NULL == (fp = fopen("Utf8ToGBKTable.txt", "r+"))) { printf("Open Table Failed!"); system("pause"); return false; } //記錄gbk的編碼值 unsigned gbk=0; //臨時記錄各位數據 unsigned data; //記錄utf-8的編碼值 unsigned long utf=0; //循環次數記號 unsigned id=0; while(!feof(fp)) { //獲得第一列gbk的編碼值 for (int i = 0; i < 4; ++i) { data=fgetc(fp); data=data>='A'?data-'A'+10:data-'0'; gbk=gbk*16+data; } //跳過tab鍵 fgetc(fp); //獲得第二列utf-8的編碼值 for (int i = 0; i < 6; ++i) { data=fgetc(fp); data=data>='A'?data-'A'+10:data-'0'; utf=utf*16+data; } if (id>6350) { printf("%d\t%ld\n",gbk,utf ); } mapValue2[utf-14989440]=gbk; fgetc(fp); // fgetc(fp); //重置數據 gbk=0; utf=0; id++; } fclose(fp); return true; } int main(int argc, char const *argv[]) { FILE* fp; FILE* fp2; //打開存儲亂碼的文件,utf-8格式,二進制打開 if((fp2=fopen("BadCode.txt","rb+"))==NULL) { printf("Open Source File Failed!\n"); system("pause"); exit(1); } //打開、新建存儲處理后數據的文件 if((fp=fopen("BadCodeH.txt","w+"))==NULL) { printf("Open/Create Destination File Failed!\n"); system("pause"); exit(1); } unsigned mapValue2[330000]; if(!ReadTable(mapValue2))
{
printf("Convert Failed!\n");
system("pause");
exit(1);
}
//紀錄奇數位(高位)的數據 unsigned ch; //紀錄偶數位(低位)以及utf-8高位的數據 unsigned cl; //記錄utf-8末位字節的信息 unsigned cu; ////記錄正常字符的utf-8編碼 unsigned long utf; //記錄正常字符的gbk編碼 unsigned cgbk; //獲得數據 ch=fgetc(fp2); //判斷文件的格式,utf-8或者Unicode,並跳過BOM字符 if(ch==0xef) { fgetc(fp2); fgetc(fp2); ch=fgetc(fp2); } else if(ch==0xff) { fgetc(fp2); ch=fgetc(fp2); } //不達結尾 while(!feof(fp2)) { //ASCII字符,正常輸出 if(ch<=0x7f) { fputc(ch,fp); } //奇數位為0xC3,獲得偶數位后加0x40后輸出 else if(ch==0xc3) { cl=fgetc(fp2); cl+=0x40; fputc(cl,fp); } //奇數位為0xC2,獲得偶數位后直接輸出 else if(ch==0xc2) { cl=fgetc(fp2); fputc(cl,fp); } //其他情況,即正常utf-8字符,轉換為GBK字符后輸出 else { //獲得utf-8的中位字節 cl=fgetc(fp2); //獲得utf-8的末尾字節 cu=fgetc(fp2); //計算utf-8編碼 utf=ch*65536+cl*256+cu; //獲得對應的gbk編碼 cgbk=mapValue2[utf-0xe4b880]; //輸出數據 fputc(cgbk/256,fp); fputc(cgbk%256,fp); } //獲得下一個數據 ch=fgetc(fp2); } fclose(fp); fclose(fp2); system("pause"); return 0; }
使用上面的代碼進行測試,就得到下面的結果,表明此算法是有效的。
至此,雖然有點麻煩,但是問題也算解決了。