汉字编码问题

来源:互联网 发布:淘宝答题器 编辑:程序博客网 时间:2024/06/07 01:53

摘自:

由于常常要和汉字处理打交道,因此,我常常受到汉字编码问题的困扰。在不断的打击与坚持中,也积累了一点汉字编码方面的经验,想和大家一起分享。

一、汉字编码的种类

    汉字编码中现在主要用到的有三类,包括GBK,GB2312和Big5。

    1、GB2312又称国标码,由国家标准总局发布,1981年5月1日实施,通行于大陆。新加坡等地也使用此编码。它是一个简化字的编码规范,当然也包括其他的符号、字母、日文假名等,共7445个图形字符,其中汉字占6763个。我们平时说6768个汉字,实际上里边有5个编码为空白,所以总共有6763个汉字。

      GB2312规定“对任意一个图形字符都采用两个字节表示,每个字节均采用七位编码表示”,习惯上称第一个字节为“高字节”,第二个字节为“低字节”。GB2312中汉字的编码范围为,第一字节0xB0-0xF7(对应十进制为176-247),第二个字节0xA0-0xFE(对应十进制为160-254)。

    GB2312将代码表分为94个区,对应第一字节(0xa1-0xfe);每个区94个位(0xa1-0xfe),对应第二字节,两个字节的值分别为区号值和位号值加32(2OH),因此也称为区位码。01-09区为符号、数字区,16-87区为汉字区(0xb0-0xf7),10-15区、88-94区是有待进一步标准化的空白区。

 

       2、Big5又称大五码,主要为香港与台湾使用,即是一个繁体字编码。每个汉字由两个字节构成,第一个字节的范围从0X81-0XFE(即129-255),共126种。第二个字节的范围不连续,分别为0X40-0X7E(即64-126),0XA1-0XFE(即161-254),共157种。

 

    3、GBK是GB2312的扩展,是向上兼容的,因此GB2312中的汉字的编码与GBK中汉字的相同。另外,GBK中还包含繁体字的编码,它与Big5编码之间的关系我还没有弄明白,好像是不一致的。GBK中每个汉字仍然包含两个字节,第一个字节的范围是0x81-0xFE(即129-254),第二个字节的范围是0x40-0xFE(即64-254)。GBK中有码位23940个,包含汉字21003个。

                                    

                                   表1 汉字编码范围

名称

第一字节

第二字节

GB2312

0xB0-0xF7(176-247)

0xA0-0xFE(160-254)

GBK

0x81-0xFE(129-254)

0x40-0xFE(64-254)

Big5

0x81-0xFE(129-255)

0x40-0x7E(64-126)

0xA1-0xFE(161-254)

 

 

二、对汉字进行hash

    为了处理汉字的方便,在查找汉字的时候,我们通常会用到hash的方法,那怎么来确定一个汉字位置呢?这就和每种编码的排列有关了,这里主要给出一种hash函数的策略。

    对于GB2312编码,设输入的汉字为GBword,我们可以采用公式(C1-176)*94 + (C2-161)确定GBindex。其中,C1表示第一字节,C2表示第二字节。具体如下:

    GBindex = ((unsigned char)GBword.at(0)-176)*94 + (unsigned char)GBword.at(1) - 161;

    之所以用unsigned char类型,是因为char是一个字节,如果用unsigend int,因为int是4个字节的,所以会造成扩展,导致错误。

       对于GBK编码,设输入的汉字为GBKword,则可以采用公式   index=(ch1-0x81)*190+(ch2-0x40)-(ch2/128),其中ch1是第一字节,ch2是第二字节。

    具体的,

    GBKindex = ((unsigned char)GBKword[0]-129)*190 +

               ((unsigned char)GBKword[1]-64) - (unsigned char)GBKword[1]/128;

 

三、怎样判断一个汉字的是什么编码

直接根据汉字的编码范围判断,对于GB2312和GBK可用下面两个程序实现。

1、判断是否是GB2312

bool isGBCode(const string& strIn)

{

    unsigned char ch1;

    unsigned char ch2;

   

    if (strIn.size() >= 2)

    {

        ch1 = (unsigned char)strIn.at(0);

        ch2 = (unsigned char)strIn.at(1);

        if (ch1>=176 && ch1<=247 && ch2>=160 && ch2<=254)

            return true;

        else return false;

    }

    else return false;

}

2、判断是否是GBK编码

bool isGBKCode(const string& strIn)

{

    unsigned char ch1;

    unsigned char ch2;

   

    if (strIn.size() >= 2)

    {

        ch1 = (unsigned char)strIn.at(0);

        ch2 = (unsigned char)strIn.at(1);

        if (ch1>=129 && ch1<=254 && ch2>=64 && ch2<=254)

            return true;

        else return false;

    }

    else return false;

}

 

3、对于Big5

    它的范围为:高字节从0xA0到0xFE,低字节从0x40到0x7E,和0xA1到0xFE两部分。判断一个汉字是否是BIG5编码,可以如上对字符的编码范围判断即可。如何定位呢?那么也想象所有编码排列为一个二维坐标,纵坐标是高字节,横坐标是低字节。这样一行上的汉字个数:(0x7E-0x40+1)+(0xFE-0xA1+1)=157。那么定位算法分两块,为:  

    if 0x40<=ch2<=0x7E: #is big5 char

    index=((ch1-0xA1)*157+(ch2-0x40))*2

    elif 0xA1<=ch2<=0xFE: #is big5 char

    index=((ch1-0xA1)*157+(ch2-0xA1+63))*2

 

对于第二块,计算偏移量时因为有两块数值,所以在计算后面一段值时,不要忘了前面还有一段值。0x7E-0x40+1=63。

 

四、如果判断一个字符是西文字符还是中文字符

    大家知道西文字符主要是指ASCII码,它用一个字节表示。且这个字符转换成数字之后,该数字是大于0的,而汉字是两个字节的,第一个字节的转化为数字之后应该是小于0的,因此可以根据每个字节转化为数字之后是否小于0,判断它是否是汉字。

    例如,设输入字为strin,则,

     If (strin.at(0) < 0)

       cout << ”是汉字” << endl;

     else cout << ”不是汉字” << endl;

五、编码表下载

   GBK编码表,下载

   GB2312编码表,下载


摘自:http://www.cnweblog.com/fly2700/archive/2010/05/10/311354.html

#include <iostream>
#include 
<string>
#include 
<Windows.h>
using namespace std;

void unicodeToUTF8(const wstring &src, string& result)
{
    
int n = WideCharToMultiByte( CP_UTF8, 0, src.c_str(), -10000 );
    result.resize(n);
    ::WideCharToMultiByte( CP_UTF8, 
0, src.c_str(), -1, (char*)result.c_str(), result.length(), 00 );
}


void unicodeToGB2312(const wstring& wstr , string& result)
{
    
int n = WideCharToMultiByte( CP_ACP, 0, wstr.c_str(), -10000 );
    result.resize(n);
    ::WideCharToMultiByte(CP_ACP, 
0, wstr.c_str(), -1, (char*)result.c_str(), n, 00 );
}


void utf8ToUnicode(const string& src, wstring& result)
{
    
int n = MultiByteToWideChar( CP_UTF8, 0, src.c_str(), -1, NULL, 0 );
    result.resize(n);
    ::MultiByteToWideChar( CP_UTF8, 
0, src.c_str(), -1, (LPWSTR)result.c_str(), result.length());
}


void gb2312ToUnicode(const string& src, wstring& result)
{
    
int n = MultiByteToWideChar( CP_ACP, 0, src.c_str(), -1, NULL, 0 );
    result.resize(n);
    ::MultiByteToWideChar( CP_ACP, 
0, src.c_str(), -1, (LPWSTR)result.c_str(), result.length());
}


void printByte(string str)
{
    
int i=0;
    
for (i=0; i<str.length(); i++)
    
{
        printf(
"%X ",(unsigned char)str.at(i));
    }

    printf(
"\n");
}


void wprintByte(wstring str)
{
    
int i=0;
    
for (i=0; i<str.length()*sizeof(wchar_t); i++)
    
{
        printf(
"%X ",*((unsigned char*)str.c_str()+i));
    }

    printf(
"\n");
}


int main()
{
    
string strText = "AB汉字";
    
string strUTF8;
    wstring wstrUnicode;
    
string strGB2312;

    gb2312ToUnicode(strText, wstrUnicode);
    printf(
"Unicode=");
    wprintByte(wstrUnicode);

    unicodeToUTF8(wstrUnicode, strUTF8);
    printf(
"UTF-8  =");
    printByte(strUTF8);
    
    utf8ToUnicode(strUTF8,wstrUnicode);
    printf(
"Unicode=");
    wprintByte(wstrUnicode);
    
    unicodeToGB2312(wstrUnicode,strGB2312);
    printf(
"GB2312 =");
    printByte(strGB2312);

    
return 0;      
}


摘自:http://blog.csdn.net/gxj1680/article/details/6369809

//注:GB2312是GBK字符集中的子集,是GBK中的简体子集;而GBK包含了简体,繁体及其他特殊符号;

//Big5是不同于GBK的繁体字库编码表

//关键转换函数:LCMapString

 

 

 

//GBK繁体 --> GB2312简体(GBK简体)

 

void CStatisticsDlg::OnBnClickedButtonLpt2lps()

{

CString strSrc;

GetDlgItemText(IDC_EDIT_LPT2LPS, strSrc);//得到strSrc:中國共產黨

 

if (strSrc.IsEmpty())

return ;

 

//如果字符集来源是ASCII等其他转换方法时,获取应分配内存的大小时或许也可以使用MultiByteToWideChar 函数

DWORD wLCID = MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED),不知道在繁体系统要改成什么参数SORT_CHINESE_PRC/*SORT_CHINESE_BIG5*/);//最后一个参数也可用SORT_CHINESE_PRCP

int nLength = LCMapString(wLCID, LCMAP_SIMPLIFIED_CHINESE, strSrc, strSrc.GetLength(), NULL, 0);//获取应分配内存的个数

if (!nLength)

return ;

 

TCHAR *tchDest = new TCHAR[nLength + 1];

memset(tchDest, 0, (nLength + 1) * sizeof(TCHAR));

{

LCMapString(wLCID, LCMAP_SIMPLIFIED_CHINESE, strSrc, strSrc.GetLength(), tchDest, nLength + 1);//转换

 

CString strTemp(tchDest);

strTemp = strSrc + _T("/r/n") + strTemp;

SetDlgItemText(IDC_EDIT_LPT2LPS, strTemp);

}

 

delete []tchDest;

}

 

 

 

//GBK简体转换为GBK繁体:

只需将上面的代码中LCMapString函数的参数LCMAP_SIMPLIFIED_CHINESE改为LCMAP_TRADITIONAL_CHINESE即可,注意MAKELANGID中的参数SUBLANG_CHINESE_SIMPLIFIED不要改


原创粉丝点击