最近在用C++做XML解析的小实验,其他语言,比如JAVA,PY都有判断文件编码的类,所以我就想用C++做个类似的功能。弄了几天终于做出来了,代码如下:
bool Text::IsUTF8(const char* szBuffer)
{
//判断高位到低位有多少个1,UTF-8的字符是有规律的
//在UTF8中,如果中文字符占3位,那么首字节的编码一定是十六进制的E开头,过3个字节又是E开头
//可以通过这个规律来判断文件的字符编码,在ANSI编码中,中文是固定两个字节的,没有规律,
//同时windows的简体中文都是采用的ANSI中的GB2312编码,通过cmd查看代码页可以看出
//对于英文这种单字节的编码,UTF8和GB2312编码一样
//windows是根据操作系统的语言来判断使用什么编码来解析文件,当然我现在使用的win10记事本默认编码
//是UTF8,以前的记事本默认是ANSI,所以过去会出现从网络上下载的txt文件乱码
bool IsUtf8 = true;
//先读取文件中的英文字符的个数
//就是那每个字节的编码与0x80相与(&)判断最高位是不是0,如果是0就是英文字符,如果是1的话肯定是中文字符
int start = 0;
int end = m_length; //这个长度没有带上char字符组后面的'\0'
int EngNums = 0;
int ChineseNums = 0; //这统计的是字节不是字符个数,因为现在还不清楚中文所占的字节数
vector<int> posChinese;//存放中文字节位置
while (start < end)
{
int out = m_binaryStr[start] & 0x80;
int isEng = this->Num(out);//判断高位是1还是0
if (isEng == 0)
EngNums++;
else
{
ChineseNums++;
posChinese.push_back(start); //保存中文字符位置
}
start++;
}
//cout << "中文字符所占的字节有" << ChineseNums << "个"<<endl;
//cout << "英文字符所占的字节有" << EngNums << "个" << endl;
//接下来先把英文字符减掉,不管在UTF8和ANSI英文都只占一个字节
int newLength = m_length - EngNums;
//剩下的字节用来除以3和2,如果能整除3那就说明是UTF8编码,否则就是ANSI编码
//这有一个问题,那就是如果出现3,2的公倍数那就无法判断,比如说两个文件除去英文字符后都是6字节
//当出现这种情况后,则需要判断中文字符的字节特点,特点如上
bool isTrue = false; //给个标志位,防止重复判断
if (newLength % 3 == 0)
{
if (newLength % 2 == 0)
{
isTrue = true; //已经判断了,下面判断整除2的就不用了
//使用0xE0(11100000)进行判断
start = 0;
while (start < posChinese.size())
{
int out = m_binaryStr[posChinese.at(start)] & 0xE0;//如果是UTF8的中文字符,这个的结果是11100000就是0xE0
if (out == 0xE0)
{
start += 3;//向后移动3个字节
}
else//一旦发现中文字符的首字节中前3位没有111就是ANSI编码
{
return false;
}
}
}
return true;
}
if (newLength % 2 == 0&&isTrue==false)//只能被2整除的话
{
//cout << "ANSI" << endl;
return false;
}
return IsUtf8;
}
由于我是将他写在一个类中,所以函数中的m_length和m_binaryStr字符数组是我通过C++的库函数中的filebuf获得的,Num函数是统计十进制转到二进制后从高位开始有多少个连续的1,这个函数学过C的都知道。其他的一些方法我都在注释里写的很清楚了。欢迎大家来一起交流。