在使用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();
}
//打开、新建存储处理后数据的文件
if((fp=fopen("BadCodeH.txt","w+"))==NULL)
{
printf("Open/Create Destination File Failed!\n");
system("pause");
exit();
}
//纪录奇数位(高位)的数据
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 ;
}

操作实例结果如下图:

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=;
//临时记录各位数据
unsigned data;
//记录utf-8的编码值
unsigned long utf=;
//循环次数记号
unsigned id=;
while(!feof(fp))
{
//获得第一列gbk的编码值
for (int i = ; i < ; ++i)
{
data=fgetc(fp);
data=data>='A'?data-'A'+:data-'';
gbk=gbk*+data;
}
//跳过tab键
fgetc(fp);
//获得第二列utf-8的编码值
for (int i = ; i < ; ++i)
{
data=fgetc(fp);
data=data>='A'?data-'A'+:data-'';
utf=utf*+data;
}
if (id>)
{
printf("%d\t%ld\n",gbk,utf );
}
mapValue2[utf-]=gbk;
fgetc(fp);
// fgetc(fp);
//重置数据
gbk=;
utf=;
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();
}
//打开、新建存储处理后数据的文件
if((fp=fopen("BadCodeH.txt","w+"))==NULL)
{
printf("Open/Create Destination File Failed!\n");
system("pause");
exit();
}
unsigned mapValue2[];
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*+cl*+cu;
//获得对应的gbk编码
cgbk=mapValue2[utf-0xe4b880];
//输出数据
fputc(cgbk/,fp);
fputc(cgbk%,fp);
}
//获得下一个数据
ch=fgetc(fp2);
} fclose(fp);
fclose(fp2);
system("pause");
return ;
}

使用上面的代码进行测试,就得到下面的结果,表明此算法是有效的。

  至此,虽然有点麻烦,但是问题也算解决了。

关于解决乱码问题的一点探索之一(涉及utf-8和GBK)的更多相关文章

  1. 关于解决乱码问题的一点探索之二(涉及Unicode(utf-16)和GBK)

        在上篇日志中(链接),我们讨论了utf-8编码和GBK编码之间转化的乱码问题,这一篇我们讨论Unicode(utf-16编码方式)与GBK编码之间转换的乱码问题.     在Windows系统 ...

  2. mysql 使用set names 解决乱码问题的原理

    解决乱码的方法,我们经常使用“set names utf8”,那么为什么加上这句代码就可以解决了呢?下面跟着我一起来深入set names utf8的内部执行原理 先说MySQL的字符集问题.Wind ...

  3. servlet 解决乱码问题

    对于servlet大家应该都很熟悉了,今天再复习一下,如果有哪里写的不好或不对的地点希望广大的网友批评指正.今天只讨论get和post两w种方式,他们之间有很多的不同点,所以解决编码的方式也会不一样, ...

  4. 关于Mysql中文乱码问题该如何解决(乱码问题完美解决方案)(转)

    这篇文章给大家介绍关于Mysql中文乱码问题该如何解决(乱码问题完美解决方案)的相关资料,还给大家收集些关于MySQL会出现中文乱码原因常见的几点,小伙伴快来看看吧   最近两天做项目总是被乱码问题困 ...

  5. SpringMVC解决乱码

    SpringMVC解决乱码 在web.xml中配置如下代码

  6. http get/post解决乱码问题

    <form method="默认为get"-> <s:form mothod="默认为post"-> ================= ...

  7. 上传Text文档并转换为PDF(解决乱码)

    前些日子,Insus.NET有分享一篇<上传Text文档并转换为PDF>http://www.cnblogs.com/insus/p/4313092.html 它是按最简单与默认方式来处理 ...

  8. php 解决乱码的通用方法

    一,出现乱码的原因分析 1,保存文件时候,文件有自己的文件编码,就是汉字,或者其他国语言,以什么编码来存储 2,输出的时候,要给内容指定编码,如以网页的形势输入时<meta http-equiv ...

  9. 为sublime安装package control 解决乱码问题 Mac版

    为sublime安装package control   Mac版参考 https://sublime.wbond.net/installation 防止中文乱码其实只需要2个东东  一个GBK enc ...

随机推荐

  1. pyhton3解决"tuple parameter unpacking is not supported"问题

    准备将键值对中的键与值对调,结果第10行出了bug,显示"tuple parameter unpacking is not supported" 解决方法:将map(lambda( ...

  2. 嵌入式C语言自我修养 11:有一种函数,叫内建函数

    11.1 什么是内建函数 内建函数,顾名思义,就是编译器内部实现的函数.这些函数跟关键字一样,可以直接使用,无须像标准库函数那样,要 #include 对应的头文件才能使用. 内建函数的函数命名,通常 ...

  3. 每天一个Linux命令之less

    之前一下子看过好多Linux命令,当初记得但是一直没有使用就忘了,现在仿这别人写一下争取能记得时间久一点233333 我使用的是ubuntu Less 这是一个查看文件的命令 进行翻页的命令有一下几个 ...

  4. stm32串口中断总结

    串口文件uart.c需要被用到; 串口通信是对GPIO端口引脚的功能复用,因此需要用到gpio.c; 因为中断的产生,因此中断文件也是需要用到的: 中断响应函数需要自己编写: 接收中断:在接收移位寄存 ...

  5. android studio 调试技巧(简直太好用)

    android studio 调试技巧(简直太好用) 说到android studio的调试,很多人可能会说,这有什么可讲的不就是一个断点调试么,刚开始我也是这么认为的,直到我了解之后,才发现,调试原 ...

  6. 欧几里得算法/欧几里得扩展算法-python

    说在开头. 出于对欧几里得的尊重,先简单介(cou)绍(ge)一(zi)下(shu).. 欧几里得,古希腊人,数学家.他活跃于托勒密一世时期的亚历山大里亚,被称为“几何之父”. 他最著名的著作< ...

  7. 防360TAB页面的样式页面

    今天给朋友做了一个仿照360新tab页面的效果,主要就是一些样式和JQUERY的应用,超级简单,现在把源码放出来 源码下载

  8. DotNetty学习笔记

    DotNetty项目本身的示例很容易运行起来,但是具体到真实的应用场景,还是需要进一步理解DotNetty的通道处理细节,这样才能够在实际项目应用中处理具体的问题. 简单的场景下会有以下几个问题,第一 ...

  9. (三)虚拟机与Linux新尝试——20155306白皎

    (三)虚拟机与Linux新尝试--20155306白皎 一.关于虚拟机的安装 在选择虚拟机的类型和版本时,Ubuntu只有32位,没有64位 解决:通过百度,后来也发现同学们好多遇到了这个问题,因此通 ...

  10. BZOJ1010_玩具装箱toy_KEY

    题目传送门 这道题可以很快想到暴力DP的做法: f[i]=min(f[i],f[j]+(C[i]-C[j]+i-j--L)^); 但是数据范围有50000,这就需要用斜率优化了. 我们设S[i]=C[ ...