唐吉诃德

  C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  5 Posts :: 75 Stories :: 3 Comments :: 0 Trackbacks

常用链接

留言簿(2)

我参与的团队

搜索

  •  

最新评论

阅读排行榜

评论排行榜

字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。字符集是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集名称:ASCII字符集、GB2312字符集、BIG5字符集、 GB 18030字符集、Unicode字符集等。计算机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。

中文文字数目大,而且还分为简体中文和繁体中文两种不同书写规则的文字,而计算机最初是按英语单字节字符设计的,因此,对中文字符进行编码,是中文信息交流的技术基础。本文将按照字符集的时间顺序讨论几种典型的字符集,选取几种代表性的中文字符集,研究历史由来、特点、技术特征。

 

汉字编码范围

名称 第一字节 第二字节

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)

 

ASCII 字符集

1.名称的由来

ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是基于罗马字母表的一套电脑编码系统。

2.特点

它主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统,并等同于国际标准ISO 646。

3.包含内容

控制字符:回车键、退格、换行键等。

可显示字符:英文大小写字符、阿拉伯数字和西文符号

4.技术特征

7位(bits)表示一个字符,共128字符

5.ASCII扩展字符集

7位编码的字符集只能支持128个字符,为了表示更多的欧洲常用字符对ASCII进行了扩展,ASCII扩展字符集使用8位(bits)表示一个字符,共256字符。

ASCII扩展字符集比ASCII字符集扩充出来的符号包括表格符号、计算符号、希腊字母和特殊的拉丁符号。

 

GB2312 字符集

 1.名称的由来

GB2312又称为GB2312-80字符集,全称为《信息交换用汉字编码字符集·基本集》,由原中国国家标准总局发布,1981年5月1日实施。

2.特点

GB2312是中国国家标准的简体中文字符集。它所收录的汉字已经覆盖99.75%的使用频率,基本满足了汉字的计算机处理需要。在中国大陆和新加坡获广泛使用。

3.包含内容

GB2312收录简化汉字及一般符号、序号、数字、拉丁字母、日文假名、希腊字母、俄文字母、汉语拼音符号、汉语注音字母,共 7445 个图形字符。其中包括6763个汉字,其中一级汉字3755个,二级汉字3008个;包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。

4.技术特征

(1)分区表示:

GB2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。

各区包含的字符如下:01-09区为特殊符号;16-55区为一级汉字,按拼音排序;56-87区为二级汉字,按部首/笔画排序;10-15区及88-94区则未有编码。

(2)双字节表示

两个字节中前面的字节为第一字节,后面的字节为第二字节。习惯上称第一字节为“高字节” ,而称第二字节为“低字节”。

“高位字节”使用了0xA1-0xF7 (把01-87区(88-94区未有编码)的区号加上0xA0),“低位字节”使用了0xA1-0xFE (把01-94加上0xA0)。

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

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

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

5.编码举例

以GB2312字符集的第一个汉字“啊”字为例,它的区号16,位号01,则区位码是1601,在大多数计算机程序中,高字节和低字节分别加0xA0得到程序的汉字处理编码0xB0A1。计算公式是:0xB0=0xA0+16, 0xA1=0xA0+1。“北京”的区位码分别是:17 17 30 09 所以分别加上0xA0 0xA0 0xA0 0xA0,在内存中显示的16进制是 B1 B1 BE A9

 

GBK 字符集

1.名称的由来

GBK是GB2312的扩展,是向上兼容的,因此GB2312中的汉字的编码与GBK中汉字的相同。另外,GBK中还包含繁体字的编码,它与Big5编码之间的关系我还没有弄明白,好像是不一致的。

2. 特点

GBK中每个汉字仍然包含两个字节,第一个字节的范围是0x81-0xFE(即129-254),第二个字节的范围是0x40-0xFE(即64-254)。GBK中有码位23940个,包含汉字21003个。

BIG5 字符集

1.名称的由来

又称大五码或五大码,1984年由台湾财团法人信息工业策进会和五间软件公司宏碁 (Acer)、神通 (MiTAC)、佳佳、零壹 (Zero One)、大众 (FIC)创立,故称大五码。

Big5码的产生,是因为当时台湾不同厂商各自推出不同的编码,如倚天码、IBM PS55、王安码等,彼此不能兼容;另一方面,台湾政府当时尚未推出官方的汉字编码,而中国大陆的GB2312编码亦未有收录繁体中文字。

2.特点

Big5字符集共收录13,053个中文字,该字符集在中国台湾使用。耐人寻味的是该字符集重复地收录了两个相同的字:“兀”(0xA461及0xC94A)、“嗀”(0xDCD1及0xDDFC)。

3.字符编码方法

Big5码使用了双字节储存方法,以两个字节来编码一个字。第一个字节称为“高位字节”,第二个字节称为“低位字节”。高位字节的编码范围0xA1-0xF9,低位字节的编码范围0x40-0x7E及0xA1-0xFE。

各编码范围对应的字符类型如下:0xA140-0xA3BF为标点符号、希腊字母及特殊符号,另外于0xA259-0xA261,存放了双音节度量衡单位用字:兙兛兞兝兡兣嗧瓩糎;0xA440-0xC67E为常用汉字,先按笔划再按部首排序;0xC940-0xF9D5为次常用汉字,亦是先按笔划再按部首排序。

4.Big5 的局限性

尽管Big5码内包含一万多个字符,但是没有考虑社会上流通的人名、地名用字、方言用字、化学及生物科等用字,没有包含日文平假名及片假名字母。

例如台湾视“着”为“著”的异体字,故没有收录“着”字。康熙字典中的一些部首用字(如“亠”、“疒”、“辵”、“癶”等)、常见的人名用字(如“堃”、“煊”、“栢”、“喆”等) 也没有收录到Big5之中。

 

GB18030 字符集

1.名称的由来

GB 18030的全称是GB18030-2000《信息交换用汉字编码字符集基本集的扩充》,是我国政府于2000年3月17日发布的新的汉字编码国家标准,2001年8月31日后在中国市场上发布的软件必须符合本标准

2.特点

GB 18030字符集标准的出台经过广泛参与和论证,来自国内外知名信息技术行业的公司,信息产业部和原国家质量技术监督局联合实施。

GB 18030字符集标准解决汉字、日文假名、朝鲜语和中国少数民族文字组成的大字符集计算机编码问题。该标准的字符总编码空间超过150万个编码位,收录了27484个汉字,覆盖中文、日文、朝鲜语和中国少数民族文字。满足中国大陆、香港、台湾、日本和韩国等东亚地区信息交换多文种、大字量、多用途、统一编码格式的要求。并且与Unicode 3.0版本兼容,填补Unicode扩展字符字汇“统一汉字扩展A”的内容。并且与以前的国家字符编码标准(GB2312,GB13000.1)兼容。

3.编码方法

GB 18030标准采用单字节、双字节和四字节三种方式对字符编码。单字节部分使用0×00至0×7F码(对应于ASCII码的相应码)。双字节部分,首字节码从0×81至0×FE,尾字节码位分别是0×40至0×7E和0×80至0×FE。四字节部分采用GB/T 11383未采用的0×30到0×39作为对双字节编码扩充的后缀,这样扩充的四字节编码,其范围为0×81308130到0×FE39FE39。其中第一、三个字节编码码位均为0×81至0×FE,第二、四个字节编码码位均为0×30至0×39。

4.包含的内容

双字节部分收录内容主要包括GB13000.1全部CJK汉字20902个、有关标点符号、表意文字描述符13个、增补的汉字和部首/构件80个、双字节编码的欧元符号等。  四字节部分收录了上述双字节字符之外的,包括CJK统一汉字扩充A在内的GB 13000.1中的全部字符。

 

对汉字进行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;

 

编码表

 

 

Unicode字符集

1.名称的由来

Unicode字符集编码是Universal Multiple-Octet Coded Character Set 通用多八位编码字符集的简称,是由一个名为 Unicode 学术学会(Unicode Consortium)的机构制订的字符编码系统,支持现今世界各种不同语言的书面文本的交换、处理及显示。该编码于1990年开始研发,1994年正式公布,最新版本是2005年3月31日的Unicode 4.1.0。

2.特征

Unicode是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

3.编码方法

Unicode 标准始终使用十六进制数字,而且在书写时在前面加上前缀“U+”,例如字母“A”的编码为 004116 和字符“?”的编码为 20AC16。所以“A”的编码书写为“U+0041”。

4.UTF-8 编码

UTF-8是Unicode的其中一个使用方式。 UTF是 Unicode Translation Format,即把Unicode转做某种格式的意思。

UTF-8便于不同的计算机之间使用网络传输不同语言和编码的文字,使得双字节的Unicode能够在现存的处理单字节的系统上正确传输。

UTF-8使用可变长度字节来储存 Unicode字符,例如ASCII字母继续使用1字节储存,重音文字、希腊字母或西里尔字母等使用2字节来储存,而常用的汉字就要使用3字节。辅助平面字符则使用4字节。

5.UTF-16 和 UTF-32 编码

UTF-32、UTF-16 和 UTF-8 是 Unicode 标准的编码字符集的字符编码方案,UTF-16 使用一个或两个未分配的 16 位代码单元的序列对 Unicode 代码点进行编码;UTF-32 即将每一个 Unicode 代码点表示为相同值的 32 位整数

Ansi、Unicode、UTF8字符串之间的转换和写入文本文件


Ansi字符串我们最熟悉,英文占一个字节,汉字2个字节,以一个\0结尾,常用于txt文本文件
Unicode 字符串,每个字符(汉字、英文字母)都占2个字节,以2个连续的\0结尾,NT操作系统内核用的是这种字符串,常被定义为typedef unsigned short wchar_t;所以我们有时常会见到什么char*无法转换为unsigned short*之类的错误,其实就是unicode
UTF8是Unicode一种压缩形式,英文A在unicode中表示为0x0041,老外觉得这种存储方式太浪费,因为浪费了50%的空间,于是就把英文压缩成1个字节,成了utf8编码,但是汉字在utf8中占3个字节,显然用做中文不如 ansi合算,这就是中国的网页用作ansi编码而老外的网页常用utf8的原因。
UTF8在还游戏里运用的很广泛,比如WOW的lua脚本等

下面来说一下转换,主要用代码来说明吧
写文件我用了CFile类,其实用FILE*之类的也是一样,写文件和字符串什么类别没有关系,硬件只关心数据和长度
bool IsUTF8(const void* pBuffer, long size)  
{  
    bool IsUTF8 = true;  
    unsigned char* start = (unsigned char*)pBuffer;  
    unsigned char* end = (unsigned char*)pBuffer + size;  
    while (start < end)  
    {  
        if (*start < 0x80) // (10000000): 值小于0x80的为ASCII字符  
        {  
            start++;  
        }  
        else if (*start < (0xC0)) // (11000000): 值介于0x80与0xC0之间的为无效UTF-8字符  
        {  
            IsUTF8 = false;  
            break;  
        }  
        else if (*start < (0xE0)) // (11100000): 此范围内为2字节UTF-8字符  
        {  
            if (start >= end - 1)   
                break;  
            if ((start[1] & (0xC0)) != 0x80)  
            {  
                IsUTF8 = false;  
                break;  
            }  
            start += 2;  
        }   
        else if (*start < (0xF0)) // (11110000): 此范围内为3字节UTF-8字符  
        {  
            if (start >= end - 2)   
                break;  
            if ((start[1] & (0xC0)) != 0x80 || (start[2] & (0xC0)) != 0x80)  
            {  
                IsUTF8 = false;  
                break;  
            }  
            start += 3;  
        }   
        else 
        {  
            IsUTF8 = false;  
            break;  
        }  
    }  
    return IsUTF8;  


Ansi转Unicode
介绍2种方法

void CConvertDlg::OnBnClickedButtonAnsiToUnicode()
{
    
// ansi to unicode

    char* szAnsi = "abcd1234你我他";
    
//预转换,得到所需空间的大小

    int wcsLen = ::MultiByteToWideChar(CP_ACP, NULL, szAnsi, strlen(szAnsi), NULL, 0);
    
//分配空间要给'\0'留个空间,MultiByteToWideChar不会给'\0'空间

    wchar_t* wszString = new wchar_t[wcsLen + 1];
    
//转换

    ::MultiByteToWideChar(CP_ACP, NULL, szAnsi, strlen(szAnsi), wszString, wcsLen);
    
//最后加上'\0'

    wszString[wcsLen] = '\0';
    
//unicode版的MessageBox API

    ::MessageBoxW(GetSafeHwnd(), wszString, wszString, MB_OK);

    
//
接下来写入文本
    
//写文本文件,头2个字节0xfeff,低位0xff写在前

    CFile cFile;
    cFile.Open(_T(
"1.txt"), CFile::modeWrite |
 CFile::modeCreate);
    
//文件开头

    cFile.SeekToBegin();
    cFile.Write(
"\xff\xfe"2
);
    
//写入内容

    cFile.Write(wszString, wcsLen * sizeof(wchar_t));
    cFile.Flush();
    cFile.Close();
    delete[] wszString;
    wszString 
=
NULL;


    
//
方法2
    
//
设置当前地域信息,不设置的话,使用这种方法,中文不会正确显示
    
//需要#include<locale.h>

    setlocale(LC_CTYPE, "chs"); 
    wchar_t wcsStr[
100
];
    
//
注意下面是大写S,在unicode中,代表后面是ansi字符串
    
//
swprintf是sprintf的unicode版本
    
//格式的前面要加大写L,代表是unicode

    swprintf(wcsStr, L"%S", szAnsi);
    ::MessageBoxW(GetSafeHwnd(), wcsStr, wcsStr, MB_OK);

}



Unicode转Ansi
也是2种方法

void CConvertDlg::OnBnClickedButtonUnicodeToAnsi()
{
    
// unicode to ansi

    wchar_t* wszString = L"abcd1234你我他";
    
//预转换,得到所需空间的大小,这次用的函数和上面名字相反

    int ansiLen = ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);
    
//同上,分配空间要给'\0'留个空间

    char* szAnsi = new char[ansiLen + 1];
    
//
转换
    
//unicode版对应的strlen是wcslen

    ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), szAnsi, ansiLen, NULL, NULL);
    
//最后加上'\0'

    szAnsi[ansiLen] = '\0';
    
//Ansi版的MessageBox API

    ::MessageBoxA(GetSafeHwnd(), szAnsi, szAnsi, MB_OK);

    
//
接下来写入文本
    
//写文本文件,ANSI文件没有BOM

    CFile cFile;
    cFile.Open(_T(
"1.txt"), CFile::modeWrite |
 CFile::modeCreate);
    
//文件开头

    cFile.SeekToBegin();
    
//写入内容

    cFile.Write(szAnsi, ansiLen * sizeof(char));
    cFile.Flush();
    cFile.Close();
    delete[] szAnsi;
    szAnsi 
=
NULL;


    
//
方法2
    
//和上面一样有另一种方法

    setlocale(LC_CTYPE, "chs"); 
    
char szStr[100
];
    
//
注意下面是大写,在ansi中,代表后面是unicode字符串
    
//sprintf

    sprintf(szStr, "%S", wszString);
    ::MessageBoxA(GetSafeHwnd(), szStr, szStr, MB_OK);
}



Unicode转UTF8

void CConvertDlg::OnBnClickedButtonUnicodeToU8()
{
    
// unicode to UTF8

    wchar_t* wszString = L"abcd1234你我他";
    
//预转换,得到所需空间的大小,这次用的函数和上面名字相反

    int u8Len = ::WideCharToMultiByte(CP_UTF8, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);
    
//
同上,分配空间要给'\0'留个空间
    
//UTF8虽然是Unicode的压缩形式,但也是多字节字符串,所以可以以char的形式保存

    char* szU8 = new char[u8Len + 1];
    
//
转换
    
//unicode版对应的strlen是wcslen

    ::WideCharToMultiByte(CP_UTF8, NULL, wszString, wcslen(wszString), szU8, u8Len, NULL, NULL);
    
//最后加上'\0'

    szU8[u8Len] = '\0';
    
//
MessageBox不支持UTF8,所以只能写文件

    
//
接下来写入文本
    
//写文本文件,UTF8的BOM是0xbfbbef

    CFile cFile;
    cFile.Open(_T(
"1.txt"), CFile::modeWrite |
 CFile::modeCreate);
    
//文件开头

    cFile.SeekToBegin();
    
//写BOM,同样低位写在前

    cFile.Write("\xef\xbb\xbf"3);
    
//写入内容

    cFile.Write(szU8, u8Len * sizeof(char));
    cFile.Flush();
    cFile.Close();
    delete[] szU8;
    szU8 
=
NULL;

}


UTF8转UNICODE

void CConvertDlg::OnBnClickedButtonU8ToUnicode()
{
    
//
UTF8 to Unicode
    
//由于中文直接复制过来会成乱码,编译器有时会报错,故采用16进制形式

    char* szU8 = "abcd1234\xe4\xbd\xa0\xe6\x88\x91\xe4\xbb\x96\x00";
    
//预转换,得到所需空间的大小

    int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), NULL, 0);
    
//分配空间要给'\0'留个空间,MultiByteToWideChar不会给'\0'空间

    wchar_t* wszString = new wchar_t[wcsLen + 1];
    
//转换

    ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), wszString, wcsLen);
    
//最后加上'\0'

    wszString[wcsLen] = '\0';
    
//unicode版的MessageBox API

    ::MessageBoxW(GetSafeHwnd(), wszString, wszString, MB_OK);

    
//写文本同ansi to unicode

}

posted on 2010-10-08 16:15 心羽 阅读(906) 评论(0)  编辑 收藏 引用 所属分类: 编码

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理