怎么将mbcshex编码转换为字符串的字符串转换成UTF8hex编码转换为字符串

温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
good good study!
day day up!
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
阅读(1246)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_095075',
blogTitle:'[转]关于unicode,mbcs,utf8,charset,encoding等相关概念的说明',
blogAbstract:'转自:/2945.html好像第一次遇到跟字符集有关的问题大概应该是在7年前,第一次写java,总是会出现编码转换的问题,动不动就乱码,基本上,本着实用主义的态度,以盲人\r\n摸象的手法,总算是能够解决问题的。这些年来,不停的会遇到编码方面的问题,随着每一次解决问题,都感觉多揭开了一点笼罩在这一堆乱七八糟的东西上面的迷\r\n雾,然而,直到去年年中,我仍然没有完全搞明白这堆乱七八糟的名词和概念之间,究竟是怎样的关系。\r\n\r\n去年年中开始的项目,需要用c++来处理文档,不可避免的遇到了编码转换的问题,在用c++处理的时候,我不得不仔细的探究在不同的编码转换的时\r\n候究竟发生了什么事情,终于,总算是搞明白了这些东西。下面,我会试着解释一下这些概念本身以及他们之间的关系,部分解释来自维基百科或者msdn,版权\r\n不属于我。\r\n',
blogTag:'',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:0,
publishTime:7,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'good good study!\r\nday day up!',
hmcon:'0',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}一 预备知识
1,字符:字符是抽象的最小文本单位。它没有固定的形状(可能是一个字形),而且没有值。“A”是一个字符,“EUR”(德国、法国和许多其他欧洲国家通用货币的标志)也是一个字符。“中”“国”这是两个汉字字符。字符仅仅代表一个符号,没有任何实际值的意义。
2,字符集:字符集是字符的集合。例如,汉字字符是中国人最先发明的字符,在中文、日文、韩文和越南文的书写中使用。这也说明了字符和字符集之间的关系,字符组成字符集(iso8859-1,GB2312/GBK,unicode)。
3,代码点:字符集中的每个字符都被分配到一个“代码点”。每个代码点都有一个特定的唯一数值,称为标值。该标量值通常用十六进制表示。
4,代码单元: 在每种编码形式中,代码点被映射到一个或多个代码单元。“代码单元”是各个编码方式中的单个单元。代码单元的大小等效于特定编码方式的位数: UTF-8 :UTF-8 中的代码单元由 8 位组成;在 UTF-8 中,因为代码单元较小的缘故,每个代码点常常被映射到多个代码单元。代码点将被映射到一个、两个、三个或四个代码单元;
UTF-16 :UTF-16 中的代码单元由 16 位组成;UTF-16 的代码单元大小是 8 位代码单元的两倍。所以,标量值小于 U+10000 的代码点被编码到单个代码单元中; UTF-32:UTF-32& 中的代码单元由 32 位组成; UTF-32 中使用的 32 位代码单元足够大,每个代码点都可编码为单个代码单元; GB18030:GB18030& 中的代码单元由 8 位组成;在 GB18030 中,因为代码单元较小的缘故,每个代码点常常被映射到多个代码单元。代码点将被映射到一个、两个或四个代码单元。
5,举例: “中国北京香蕉是个大笨蛋”这是我定义的aka字符集;
各字符对应代码点为:
下面是我定义的 zixia 编码方案(8位),可以看到它的编码中表示了aka字符集的所有字符对应的 代码单元;
所谓文本文件 就是我们按一定编码方式将二进制数据表示为对应的文本如 这样的文件。我用一个支持 zixia编码和aka字符集的记事本打开,它就按照编码方案显示为& “香蕉是个大笨蛋 ” 如果我把这些字符按照GBK另存一个文件,那么则肯定不是这个,而是
11 11 01010
二,字符集
1, 常用字符集分类&ASCII及其扩展字符集 作用:表语英语及西欧语言。 位数:ASCII是用7位表示的,能表示128个字符;其扩展使用8位表示,表示256个字符。 范围:ASCII从00到7F,扩展从00到FF。&ISO-8859-1字符集
作用:扩展ASCII,表示西欧、希腊语等。 位数:8位, 范围:从00到FF,兼容ASCII字符集。&GB2312字符集 作用:国家简体中文字符集,兼容ASCII。 位数:使用2个字节表示,能表示7445个符号,包括6763个汉字,几乎覆盖所有高频率汉字。 范围:高字节从A1到F7, 低字节从A1到FE。将高字节和低字节分别加上0XA0即可得到编码。&BIG5字符集
作用:统一繁体字编码。 位数:使用2个字节表示,表示13053个汉字。 范围:高字节从A1到F9,低字节从40到7E,A1到FE。&GBK字符集 作用:它是GB2312的扩展,加入对繁体字的支持,兼容GB2312。 位数:使用2个字节表示,可表示21886个字符。 范围:高字节从81到FE,低字节从40到FE。&GB18030字符集
作用:它解决了中文、日文、朝鲜语等的编码,兼容GBK。 位数:它采用变字节表示(1 ASCII,2,4字节)。可表示27484个文字。 范围:1字节从00到7F; 2字节高字节从81到FE,低字节从40到7E和80到FE;4字节第一三字节从81到FE,第二四字节从30到39。&UCS字符集 作用:国际标准 ISO 10646 定义了通用字符集 (Universal Character Set)。它是与UNICODE同类的组织,UCS-2和UNICODE兼容。
位数:它有UCS-2和UCS-4两种格式,分别是2字节和4字节。 范围:目前,UCS-4只是在UCS-2前面加了0×0000。&UNICODE字符集 作用:为世界650种语言进行统一编码,兼容ISO-8859-1。 位数:UNICODE字符集有多个编码方式,分别是UTF-8,UTF-16和UTF-32。
2 ,按所表示的文字分类 语言&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 字符集&&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&&&&& 正式名称 英语、西欧语&&&&&&&&&&&&&&&&&&&&
ASCII,ISO-8859-1&&&&&&&&&&&&&& MBCS 多字节 简体中文&&&&&&&&&&&&&&&&&&&&&&&&&&&& GB2312&&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&&&& MBCS 多字节 繁体中文&&&&&&&&&&&&&&&&&&&&&&&&&&&& BIG5&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&& MBCS 多字节 简繁中文&&&&&&&&&&&&&&&&&&&&&&&&&&&& GBK&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&& MBCS 多字节 中文、日文及朝鲜语&&&&&&&& GB18030&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&& MBCS 多字节 各国语言&&&&&&&&&&&&&&&&&&&&&&&&&&&& UNICODE,UCS&& &&&&&&&&&&&&&&&& DBCS 宽字节&三
,编码&UTF-8:采用变长字节 (1 ASCII, 2 希腊字母, 3 汉字, 4 平面符号) 表示,网络传输, 即使错了一个字节,不影响其他字节,而双字节只要一个错了,其他也错了,具体如下: 如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的字节数,其余各字节均以10开头。UTF-8最多可用到6个字节。
UTF-16:采用2字节,Unicode中不同部分的字符都同样基于现有的标准。这是为了便于转换。从 0×7F是ASCII字符,从0×FF是ISO-8859-1对ASCII的扩展。希腊字母表使用从0×0370到 0×03FF 的代码,斯拉夫语使用从0×FF的代码,美国使用从0×8F的代码,希伯来语使用从0×FF的代码。中国、日本和韩国的象形文字(总称为CJK)占用了从0×FFF的代码;由于0×00在c语言及操作系统文件名等中有特殊意义,故很多情况下需要UTF-8编码保存文本,去掉这个0×00。举例如下:
UTF-16: 0×0080& = 00 0000 UTF-8:&& 0xC280 = 00 0000 UTF-32:采用4字节。 优缺点 UTF-8、UTF-16和UTF-32都可以表示有效编码空间 (U+000000-U+10FFFF) 内的所有Unicode字符。 使用UTF-8编码时ASCII字符只占1个字节,存储效率比较高,适用于拉丁字符较多的场合以节省空间。 对于大多数非拉丁字符(如中文和日文)来说,UTF-16所需存储空间最小,每个字符只占2个字节。
Windows NT内核是Unicode(UTF-16),采用UTF-16编码在调用系统API时无需转换,处理速度也比较快。 采用UTF-16和UTF-32会有Big Endian和Little Endian之分,而UTF-8则没有字节顺序问题,所以UTF-8适合传输和通信。 UTF-32采用4字节编码,一方面处理速度比较快,但另一方面也浪费了大量空间,影响传输速度,因而很少使用。
四,如何判断字符集&1,字节序 首先说一下字节序对编码的影响,字节序分为Big Endian字节序和Little Endian字节序。不同的处理器可能不一样。所以,传输时需要告诉处理器当时的编码字节序。对于前者而言,高位字节存在低地址,低字节存于高地址;后者相反。例如,0X03AB,
Big Endian字节序
0001: AB Little Endian字节序是 0000: AB
2,编码识别 UNICODE,根据前几个字节可以判断UNICODE字符集的各种编码,叫做Byte Order Mask方法BOM: UTF-8: EFBBBF (符合UTF-8格式,请看上面。但没有含义在UCS即UNICODE中) UTF-16 Big Endian:FEFF (没有含义在UCS-2中) UTF-16 Little Endian:FFFE (没有含义在UCS-2中)
UTF-32 Big Endian:0000FEFF (没有含义在UCS-4中) UTF-32 Little Endian:FFFE0000 (没有含义在UCS-4中) GB2312:高字节和低字节的第1位都是1。 BIG5,GBK&GB18030:高字节的第1位为1。操作系统有默认的编码,常为GBK,可以下载别的并升级。 通过判断高字节的第1位从而知道是ASCII或者汉字编码。
#include &stdio.h&
#include &windows.h&
//GBK编码转换到UTF8编码
int GBKToUTF8(unsigned char * lpGBKStr,unsigned char * lpUTF8Str,int nUTF8StrLen)
wchar_t * lpUnicodeStr = NULL;
int nRetLen = 0;
if(!lpGBKStr)
//如果GBK字符串为NULL则出错退出
nRetLen = ::MultiByteToWideChar(CP_ACP,0,(char *)lpGBKStr,-1,NULL,NULL);
//获取转换到Unicode编码后所需要的字符空间长度
lpUnicodeStr = new WCHAR[nRetLen + 1];
//为Unicode字符串空间
nRetLen = ::MultiByteToWideChar(CP_ACP,0,(char *)lpGBKStr,-1,lpUnicodeStr,nRetLen);
//转换到Unicode编码
if(!nRetLen)
//转换失败则出错退出
nRetLen = ::WideCharToMultiByte(CP_UTF8,0,lpUnicodeStr,-1,NULL,0,NULL,NULL);
//获取转换到UTF8编码后所需要的字符空间长度
if(!lpUTF8Str)
//输出缓冲区为空则返回转换后需要的空间大小
if(lpUnicodeStr)
delete []lpUnicodeS
return nRetL
if(nUTF8StrLen & nRetLen)
//如果输出缓冲区长度不够则退出
if(lpUnicodeStr)
delete []lpUnicodeS
nRetLen = ::WideCharToMultiByte(CP_UTF8,0,lpUnicodeStr,-1,(char *)lpUTF8Str,nUTF8StrLen,NULL,NULL);
//转换到UTF8编码
if(lpUnicodeStr)
delete []lpUnicodeS
return nRetL
//使用这两个函数的例子
int main()
char cGBKStr[] = &我是中国人!&;
char * lpGBKStr = NULL;
char * lpUTF8Str = NULL;
FILE * fp = NULL;
int nRetLen = 0;
nRetLen = GBKToUTF8((unsigned char *) cGBKStr,NULL,NULL);
printf(&转换后的字符串需要的空间长度为:%d &,nRetLen);
lpUTF8Str = new char[nRetLen + 1];
nRetLen = GBKToUTF8((unsigned char *)cGBKStr,(unsigned char *)lpUTF8Str,nRetLen);
if(nRetLen)
printf(&GBKToUTF8转换成功!&);
printf(&GBKToUTF8转换失败!&);
goto Ret0;
fp = fopen(&C:\\GBKtoUTF8.txt&,&wb&);
//保存到文本文件
fwrite(lpUTF8Str,nRetLen,1,fp);
fclose(fp);
getchar();
//先去打开那个文本文件看看,单击记事本的“文件”-“另存为”菜单,在对话框中看到编码框变为了“UTF-8”说明转换成功了
if(lpGBKStr)
delete []lpGBKS
if(lpUTF8Str)
delete []lpUTF8S
}Karlson, 13:39:57
class CChineseCode
static void UTF_8ToUnicode(wchar_t* pOut,char *pText);
// 把UTF-8转换成Unicode
static void UnicodeToUTF_8(char* pOut,wchar_t* pText);
//Unicode 转换成UTF-8
static void UnicodeToGB2312(char* pOut,wchar_t uData);
// 把Unicode 转换成 GB2312
static void Gb2312ToUnicode(wchar_t* pOut,char *gbBuffer);// GB2312 转换成 Unicode
static void GB2312ToUTF_8(string& pOut,char *pText, int pLen);//GB2312 转为 UTF-8
static void UTF_8ToGB2312(string &pOut, char *pText, int pLen);//UTF-8 转为 GB2312
void CChineseCode::UTF_8ToUnicode(wchar_t* pOut,char *pText)
char* uchar = (char *)pO
uchar[1] = ((pText[0] & 0x0F) && 4) + ((pText[1] && 2) & 0x0F);
uchar[0] = ((pText[1] & 0x03) && 6) + (pText[2] & 0x3F);
void CChineseCode::UnicodeToUTF_8(char* pOut,wchar_t* pText)
// 注意 WCHAR高低字的顺序,低字节在前,高字节在后
char* pchar = (char *)pT
pOut[0] = (0xE0 | ((pchar[1] & 0xF0) && 4));
pOut[1] = (0x80 | ((pchar[1] & 0x0F) && 2)) + ((pchar[0] & 0xC0) && 6);
pOut[2] = (0x80 | (pchar[0] & 0x3F));
void CChineseCode::UnicodeToGB2312(char* pOut,wchar_t uData)
WideCharToMultiByte(CP_ACP,NULL,&uData,1,pOut,sizeof(wchar_t),NULL,NULL);
void CChineseCode::Gb2312ToUnicode(wchar_t* pOut,char *gbBuffer)
::MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,gbBuffer,2,pOut,1);
void CChineseCode::GB2312ToUTF_8(string& pOut,char *pText, int pLen)
char buf[4];
int nLength = pLen* 3;
char* rst = new char[nLength];
memset(buf,0,4);
memset(rst,0,nLength);
int i = 0;
int j = 0;
while(i & pLen)
//如果是英文直接复制就可以
if( *(pText + i) &= 0)
rst[j++] = pText[i++];
Gb2312ToUnicode(&pbuffer,pText+i);
UnicodeToUTF_8(buf,&pbuffer);
unsigned short int tmp = 0;
tmp = rst[j] = buf[0];
tmp = rst[j+1] = buf[1];
tmp = rst[j+2] = buf[2];
rst[j] = '';
//返回结果
void CChineseCode::UTF_8ToGB2312(string &pOut, char *pText, int pLen)
char * newBuf = new char[pLen];
char Ctemp[4];
memset(Ctemp,0,4);
int j = 0;
while(i & pLen)
if(pText & 0)
newBuf[j++] = pText[i++];
UTF_8ToUnicode(&Wtemp,pText + i);
UnicodeToGB2312(Ctemp,Wtemp);
newBuf[j] = Ctemp[0];
newBuf[j + 1] = Ctemp[1];
newBuf[j] = '';
pOut = newB
delete []newB
}1、将GBK转换成UTF8
string GBKToUTF8(const std::string& strGBK)
{ string strOutUTF8 = &&;
WCHAR * str1;
int n = MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0);
str1 = new WCHAR[n];
MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, str1, n); n = WideCharToMultiByte(CP_UTF8, 0, str1, -1, NULL, 0, NULL, NULL);
char * str2 = new char[n];
WideCharToMultiByte(CP_UTF8, 0, str1, -1, str2, n, NULL, NULL);
strOutUTF8 = str2;
delete[]str1;
str1 = NULL;
delete[]str2;
str2 = NULL;
return strOutUTF8;
&Wi d e C h a r To M u l t i B y t e把U N I C O D E转换成A S C I I码。
2、将UTF8转换成GBK
string UTF8ToGBK(const std::string& strUTF8)
int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, NULL, 0);
unsigned short * wszGBK = new unsigned short[len + 1]; memset(wszGBK, 0, len * 2 + 2);
MultiByteToWideChar(CP_UTF8, 0, (LPCTSTR)strUTF8.c_str(), -1, wszGBK, len);
len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
char *szGBK = new char[len + 1];
memset(szGBK, 0, len + 1);
WideCharToMultiByte(CP_ACP,0, wszGBK, -1, szGBK, len, NULL, NULL); //strUTF8 = szGBK;
std::string strTemp(szGBK);
delete[]szGBK;
delete[]wszGBK;
return strT
本文已收录于以下专栏:
相关文章推荐
UTF-8 CPP的使用!
独此一家,别无分店
#include &stdafx.h&
#include &windows.h&
using namespace s...
在跨平台的开发中,字符串的转码显得相当重要,稍有不慎,将会出现乱码的情况,在这里,首先说一下Qt的QString中几个关于转码的函数:
(1)QByteArray toUtf8() const;
踩过的坑1:怎么把存储UTF-8编码的字符串转换成实际字符首先说明下问题。之前用爬虫从网上down数据的时候因为没有思考到位,结果出现了一个很奇葩的问题。一般来说中文采用UTF8编码后写成byte[]...
1、简述最近在发送网络请求时遇到了中文字符乱码的问题,在代码中调试字符正常,用抓包工具抓的包中文字符显示正常,就是发送到服务器就显示乱码了,那就要将客户端和服务器设置统一的编码(UTF-8),而我们程...
string、UTF8相互转换方法
std::string ofDewarServer::string_To_UTF8(const std::string & str)
std::stri...
写代码时经常会遇到各种编码转换问题,因此记录下来以便日后对各种平台下不同编码转换作整理。
GBK(或GB2312)转UTF-8实现:
他的最新文章
讲师:钟钦成
讲师:宋宝华
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)用户名:polaris1119
文章数:53
评论数:323
访问量:974004
注册日期:
阅读量:1297
阅读量:3317
阅读量:587004
阅读量:473503
51CTO推荐博文
&每一个程序员都不可避免的遇到字符编码的问题,特别是做Web开发的程序员,&乱码问题&一直是让人头疼的问题,也许您已经很少遇到&乱码&问题,然而,对解决乱码的方法的内在原理,您是否明白?本人作为一个程序员,在字符编码方面同样遇到不少问题,而且一直对各种编码懵懵懂懂、不清不楚;在工作中也曾经遇到一个很烦人的编码问题。这两天在网上收集了大量编码方面的资料,对字符编码算是理解的比较清楚了。下面把我认为比较重要的知识点记录下来,一方面方便以后复习;另一方面也希望给跟我一样懵懵懂懂的人一个参考。不对或不妥之处,请批评指正。
在此之前,先了解一些有用概念:&字符集&、&字符编码&和&内码&。
1、字符集与字符编码
字符是各种文字和符号的总称,包括各个国家文字、标点符号、图形符号、数字等。字符集是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集有:ASCII字符集、ISO 8859字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。计算机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。&
编码(encoding)和字符集不同。字符集只是字符的集合,不一定适合作网络传送、处理,有时须经编码(encode)后才能应用。如Unicode可依不同需要以UTF-8、UTF-16、UTF-32等方式编码。
字符编码就是以二进制的数字来对应字符集的字符。
因此,对字符进行编码,是信息交流的技术基础。
使用哪些字符。也就是说哪些汉字,字母和符号会被收入标准中。所包含&字符&的集合就叫做&字符集&。
规定每个&字符&分别用一个字节还是多个字节存储,用哪些字节来存储,这个规定就叫做&编码&。
各个国家和地区在制定编码标准的时候,&字符的集合&和&编码&一般都是同时制定的。因此,平常我们所说的&字符集&,比如:GB2312, GBK, JIS 等,除了有&字符的集合&这层含义外,同时也包含了&编码&的含义。
注意:Unicode字符集有多种编码方式,如UTF-8、UTF-16等;ASCII只有一种;大多数MBCS(包括GB2312)也只有一种。
2、什么是内码?
2.1 维基百科的解释
在计算机科学及相关领域当中,内码指的是&将资讯编码后,透过某种方式储存在特定记忆装置时,装置内部的编码形式&。在不同的系统中,会有不同的内码。
在以往的英文系统中,内码为ASCII。在繁体中文系统中,目前常用的内码为大五码(Big5)。在简体中文系统中,内码则为国标码(国家标准代码:现在强制要求使用GB18030标准;较旧计算机仍然使用GB2312)。而统一码(Unicode)则为另一常见内码。
2.2 百度百科的解释
内码是指整机系统中使用的二进制字符编码,是沟通输入、输出与系统平台之间的交换码,通过内码可以达到通用和高效率传输文本的目的。比如MS Word中所存储和调用的就是内码而非图形文字。英文ASCII字符采用一个字节的内码表示,中文字符如国标字符集中,GB2312、GB12345、GB13000皆用双字节内码,GB1汉字)双字节内码汉字为20,902个,其余6,631个汉字用四字节内码。
3、字符编码分类总结
下面从计算机对多国语言支持的角度来总结字符编码。
3.1 ASCII编码
以下来自&维基百科&:
ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本EASCII则可以勉强显示其他西欧语言。它是现今最通用的单字节编码系统(但是有被UniCode追上的迹象),并等同于国际标准ISO/IEC 646。
ASCII第一次以规范标准的型态发表是在1967年,最后一次更新则是在1986年,至今为止共定义了128个字符;其中33个字符无法显示(这是以现今操作系统为依归,但在DOS模式下可显示出一些诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符。控制字符的用途主要是用来操控已经处理过的文字。在33个字符之外的是95个可显示的字符,包含用键盘敲下空白键所产生的空白字符也算1个可显示字符(显示为空白)。
ASCII表:见http://zh.wikipedia.org/zh-cn/ASCII
ASCII缺点:
ASCII的最大缺点是只能显示26个基本拉丁字母、阿拉伯数目字和英式标点符号,因此只能用于显示现代美国英语(而且在处理英语当中的外来词如na&ve、caf&、&lite等等时,所有重音符号都不得不去掉,即使这样做会违反拼写规则)。而EASCII虽然解决了部份西欧语言的显示问题,但对更多其他语言依然无能为力。因此现在的苹果电脑已经抛弃ASCII而转用Unicode。
最早的英文DOS操作系统的系统内码是:ASCII。计算机这时候只支持英语,其他语言不能够在计算机存储和显示。
在该阶段,单字节字符串使用一个字节存放一个字符(SBCS,Single Byte Character System)。如:&Bob123&占6个字节。
3.2 ANSI编码
为使计算机支持更多语言,通常使用0x800~xFF范围的2个字节来表示1个字符。比如:汉字 '中' 在中文操作系统中,使用 [0xD6,0xD0]这两个字节存储。
不同的国家和地区制定了不同的标准,由此产生了GB2312,BIG5,JIS等各自的编码标准。这些使用2个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。
不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。
中文DOS、中文/日文Windows 95/98时代系统内码使用的是ANSI编码(本地化)
在使用ANSI编码支持多语言阶段,每个字符使用一个字节或多个字节来表示(MBCS,Multi-Byte Character System),因此,这种方式存放的字符也被称作多字节字符。比如,&中文123& 在中文 Windows 95 内存中为7个字节,每个汉字占2个字节,每个英文和数字字符占1个字节。
在非 Unicode 环境下,由于不同国家和地区采用的字符集不一致,很可能出现无法正常显示所有字符的情况。微软公司使用了代码页(Codepage)转换表的技术来过渡性的部分解决这一问题,即通过指定的转换表将非 Unicode 的字符编码转换为同一字符对应的系统内部使用的 Unicode 编码。可以在&语言与区域设置&中选择一个代码页作为非 Unicode 编码所采用的默认编码方式,如936为简体中文GBK,950为正体中文Big5(皆指PC上使用的)。在这种情况下,一些非英语的欧洲语言编写的软件和文档很可能出现乱码。而将代码页设置为相应语言中文处理又会出现问题,这一情况无法避免。从根本上说,完全采用统一编码才是解决之道,但目前尚无法做到这一点。
  代码页技术现在广泛为各种平台所采用。UTF-7 的代码页是65000,UTF-8 的代码页是65001。
3.3 Unicode编码
为了使国际间信息交流更加方便,国际组织制定了 UNICODE 字符集,为各种语言中的每一个字符设定了统一并且唯一的数字编号,以满足跨语言、跨平台进行文本转换、处理的要求。
Unicode字符集可以简写为UCS(Unicode Character Set)。早期的unicodeUnicode标准有UCS-2、UCS-4的说法。UCS-2用两个字节编码,UCS-4用4个字节编码。
在 UNICODE 被采用之后,计算机存放字符串时,改为存放每个字符在 UNICODE 字符集中的序号。目前计算机一般使用 2 个字节(16 位)来存放一个序号(DBCS,Double Byte Character System),因此,这种方式存放的字符也被称作宽字节字符。比如,字符串 &中文123& 在 Windows 2000 下,内存中实际存放的是 5 个序号,一共10个字节。
Unicode字符集包含了各种语言中使用到的所有&字符&。用来给 UNICODE 字符集编码的标准有很多种,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。
4、常用编码规则
4.1 单字节字符编码
(1)编码标准:ISO-8859-1。
(2)说明:最简单的编码规则,每一个字节直接作为一个 UNICODE 字符。比如,[0xD6, 0xD0] 这两个字节,通过 iso-8859-1 转化为字符串时,将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符,即 &&OÐ&。
反之,将 UNICODE 字符串通过 iso-8859-1 转化为字节串时,只能正常转化 0~255 范围的字符。
4.2 ANSI编码
(1)GB2312, BIG5, Shift_JIS, ISO-8859-2。
(2)把 UNICODE 字符串通过 ANSI 编码转化为&字节串&时,根据各自编码的规定,一个 UNICODE 字符可能转化成一个字节或多个字节。
反之,将字节串转化成字符串时,也可能多个字节转化成一个字符。比如,[0xD6, 0xD0] 这两个字节,通过 GB2312 转化为字符串时,将得到 [0x4E2D] 一个字符,即 '中' 字。
&ANSI 编码&的特点:
(1)这些&ANSI 编码标准&都只能处理各自语言范围之内的 UNICODE 字符。
(2)&UNICODE 字符&与&转换出来的字节&之间的关系是人为规定的。
4.3 UNICODE编码
(1)编码标准:UTF-8, UTF-16, UnicodeBig。
(2)与&ANSI 编码&类似的,把字符串通过 UNICODE 编码转化成&字节串&时,一个 UNICODE 字符可能转化成一个字节或多个字节。
与&ANSI 编码&不同的是:
(1)这些&UNICODE 编码&能够处理所有的 UNICODE 字符。
(2)&UNICODE 字符&与&转换出来的字节&之间是可以通过计算得到的。
我们实际上没有必要去深究每一种编码具体把某一个字符编码成了哪几个字节,我们只需要知道&编码&的概念就是把&字符&转化成&字节&就可以了。对于&UNICODE 编码&,由于它们是可以通过计算得到的,因此,在特殊的场合,我们可以去了解某一种&UNICODE 编码&是怎样的规则。
5、编码的区别
5.1 GB2312、GBK和GB18030
(1)GB2312&
当中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存,于是想到把那些ASCII码中127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的&全角&字符,而原来在127号以下的那些就叫&半角&字符了。这种汉字方案叫做 &GB2312&。GB2312 是对 ASCII 的中文扩展。兼容ASCII。
但是中国的汉字太多了,我们很快就就发现有许多人的人名没有办法在这里打出来,不得不继续把 GB2312 没有用到的码位找出来用上。后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 &GBK& 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。
(3)GB18030&
后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK 扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。&
中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 &DBCS&(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。在这种情况下,&一个汉字算两个英文字符!&。然而,在Unicode环境下却并非总是如此。&
5.1 Unicode和BigEndianUnicode
这两个指示存储顺序不同,如&A&的Unicode编码为6500,而BigEndianUnicode编码为0065。
5.2 UTF-7、UTF-8和UTF-16
在Unicode里,所有的字符被一视同仁。汉字不再使用&两个扩展ASCII&,而是使用&1个Unicode&,注意,现在的汉字是&一个字符&了,于是,拆字、统计字数这些问题也就自然而然的解决了。
但是,这个世界不是理想的,不可能在一夜之间所有的系统都使用Unicode来处理字符,所以Unicode在诞生之日,就必须考虑一个严峻的问题:和ASCII字符集之间的不兼容问题。&
我们知道,ASCII字符是单个字节的,比如&A&的ASCII是65。而Unicode是双字节的,比如&A&的Unicode是0065,这就造成了一个非常大的问题:以前处理ASCII的那套机制不能被用来处理Unicode了。
另一个更加严重的问题是,C语言使用'\0'作为字符串结尾,而Unicode里恰恰有很多字符都有一个字节为0,这样一来,C语言的字符串函数将无法正常处理Unicode,除非把世界上所有用C写的程序以及他们所用的函数库全部换掉。
于是,比Unicode更伟大的东东诞生了,之所以说它更伟大是因为它让Unicode不再存在于纸上,而是真实的存在于我们大家的电脑中。那就是:UTF。
UTF= UCS Transformation Format,即UCS转换(传输)格式。
它是将Unicode编码规则和计算机的实际编码对应起来的一个规则。现在流行的UTF有2种:UTF-8和UTF-16。
这两种都是Unicode的编码实现。
5.2.1 UTF-8
UCS-2编码(16进制) & UTF-8 字节流(二进制)
0000 - 007F & & & & 0xxxxxxx
0080 - 07FF & & & & 110xxxxx 10xxxxxx
0800 - FFFF & & & & 1110xxxx 10xxxxxx 10xxxxxx&
例如&汉&字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是: 001001,用这个比特流依次代替模板中的x,得到:01,即E6 B1 89。
可见UTF-8是变长的,将Unicode编码为0007F的字符,用单个字节来表示; 007FF的字符用两个字节表示;0FFFF的字符用3字节表示。因为目前为止Unicode-16规范没有指定FFFF以上的字符,所以UTF-8最多是使用3个字节来表示一个字符。但理论上来说,UTF-8最多需要用6字节表示一个字符。&
UTF-8兼容ASCII。
5.2.2 UTF-16(标准的Unicode成为UTF-16)
UTF-16和上面提到的Unicode本身的编码规范是一致的。
UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。
UTF-16不兼容ASCII。
5.2.3 UTF-7
UTF-7 (7-位元 Unicode 转换格式(Unicode Transformation Format,简写成 UTF)) 是一种可变长度字元编码方式,用以将 Unicode 字元以 ASCII 编码的字元串来呈现,可以应用在电子邮件传输之类的应用。
UTF-7并非Unicode标准之一。想要详细了解的可以查阅相关资料。
6、Unicode与UTF
Unicode是内存编码表示方案(是规范),而UTF是如何保存和传输Unicode的方案(是实现)。
6.1 UTF的字节序和BOM
6.1.1 字节序
UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个&奎&的Unicode编码是594E,&乙&的Unicode编码是4E59。如果我们收到UTF-16字节流&594E&,那么这是&奎&还是&乙&?
Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是&Bill Of Material&的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法:
在UCS编码中有一个叫做&ZERO WIDTH NO-BREAK SPACE&的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符&ZERO WIDTH NO-BREAK SPACE&。
这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符&ZERO WIDTH NO-BREAK SPACE&又被称作BOM。
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符&ZERO WIDTH NO-BREAK SPACE&的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
(1)BOM的来历&
为了识别 Unicode 文件,Microsoft 建议所有的 Unicode 文件应该以 ZERO WIDTH NOBREAK SPACE(U+FEFF)字符开头。这作为一个&特征符&或&字节顺序标记(byte-order mark,BOM)&来识别文件中使用的编码和字节顺序。
(2)不同的系统对BOM的支持&
因为一些系统或程序不支持BOM,因此带有BOM的Unicode文件有时会带来一些问题。
①JDK1.5以及之前的Reader都不能处理带有BOM的UTF-8编码的文件,解析这种格式的xml文件时,会抛出异常:Content is not allowed in prolog。&对于解决方法,之后我会写篇文章专门讨论该问题。&
②Linux/UNIX 并没有使用 BOM,因为它会破坏现有的 ASCII 文件的语法约定。
③不同的编辑工具对BOM的处理也各不相同。使用Windows自带的记事本将文件保存为UTF-8编码的时候,记事本会自动在文件开头插入BOM(虽然BOM对UTF-8来说并不是必须的)。而其它很多编辑器用不用BOM是可以选择的。UTF-8、UTF-16都是如此。
(3)BOM与XML&
XML解析读取XML文档时,W3C定义了3条规则:
①如果文档中有BOM,就定义了文件编码;
②如果文档中没有BOM,就查看XML声明中的编码属性;
③如果上述两者都没有,就假定XML文档采用UTF-8编码。
6.2 决定文本的字符集与编码
软件通常有三种途径来决定文本的字符集和编码。
(1)对于Unicode文本最标准的途径是检测文本最开头的几个字节。如:
开头字节 & & & &Charset/encoding
&EF BB BF    UTF-8
&FE FF     UTF-16/UCS-2, little endian(UTF-16LE)
&FF FE     UTF-16/UCS-2, big endian(UTF-16BE)
&FF FE 00 00  UTF-32/UCS-4, little endian.
&00 00 FE FF  UTF-32/UCS-4, big-endia
(2)采取一种比较安全的方式来决定字符集及其编码,那就是弹出一个对话框来请示用户。
然而MBCS文本(ANSI)没有这些位于开头的字符集标记,现在很多软件保存文本为Unicode时,可以选择是否保存这些位于开头的字符集标记。因此,软件不应该依赖于这种途径。这时,软件可以采取一种比较安全的方式来决定字符集及其编码,那就是弹出一个对话框来请示用户。
(3)采取自己&猜&的方法。
如果软件不想麻烦用户,或者它不方便向用户请示,那它只能采取自己&猜&的方法,软件可以根据整个文本的特征来猜测它可能属于哪个charset,这就很可能不准了。使用记事本打开那个&联通&文件就属于这种情况。(把原本属于ANSI编码的文件当成UTF-8处理,详细说明见:http://blog.csdn.net/omohe/archive//1630186.aspx)
6.3 记事本的几种编码
(1)ANSI编码&
记事本默认保存的编码格式是:ANSI,即本地操作系统默认的内码,简体中文一般为GB2312。这个怎么验证呢?用记事本保存后,使用EmEditor、EditPlus和UltraEdit之类的文本编辑器打开。推荐使用EmEditor,打开后,在又下角会显示编码:GB2312。
(2)Unicode编码&
用记事本另存为时,编码选择&Unicode&,用EmEditor打开该文件,发现编码格式是:UTF-16LE+BOM(有签名)。用十六进制方式查看,发现开头两字节为:FF FE。这就是BOM。
(3)Unicode big endian&
用记事本另存为时,编码选择&Unicode&,用EmEditor打开该文件,发现编码格式是:UTF-16BE+BOM(有签名)。用十六进制方式查看,发现开头两字节为:FE FF。这就是BOM。
(4)UTF-8&
用记事本另存为时,编码选择&UTF-8&,用EmEditor打开该文件,发现编码格式是:UTF-8(有签名)。用十六进制方式查看,发现开头三个字节为:EF BB BF。这就是BOM。
7、几种误解,以及乱码产生的原因和解决办法
7.1 误解一
在将&字节串&转化成&UNICODE 字符串&时,比如在读取文本文件时,或者通过网络传输文本时,容易将&字节串&简单地作为单字节字符串,采用每&一个字节&就是&一个字符&的方法进行转化。
而实际上,在非英文的环境中,应该将&字节串&作为 ANSI 字符串,采用适当的编码来得到 UNICODE 字符串,有可能&多个字节&才能得到&一个字符&。
通常,一直在英文环境下做开发的程序员们,容易有这种误解。
7.2 误解二
在 DOS,Windows 98 等非 UNICODE 环境下,字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串,必须知道是哪种编码才能被正确地使用。这使我们形成了一个惯性思维:&字符串的编码&。
当 UNICODE 被支持后,Java 中的 String 是以字符的&序号&来存储的,不是以&某种编码的字节&来存储的,因此已经不存在&字符串的编码&这个概念了。只有在&字符串&与&字节串&转化时,或者,将一个&字节串&当成一个 ANSI 字符串时,才有编码的概念。
不少的人都有这个误解。
7.3 分析与解决
第一种误解,往往是导致乱码产生的原因。第二种误解,往往导致本来容易纠正的乱码问题变得更复杂。
在这里,我们可以看到,其中所讲的&误解一&,即采用每&一个字节&就是&一个字符&的转化方法,实际上也就等同于采用 iso-8859-1 进行转化。因此,我们常常使用 bytes = string.getBytes(&iso-8859-1&) 来进行逆向操作,得到原始的&字节串&。然后再使用正确的 ANSI 编码,比如 string = new String(bytes, &GB2312&),来得到正确的&UNICODE 字符串&。
8、参考与深入阅读学习资料
8.1 (强烈推介)
&本文出自 “” 博客,请务必保留此出处
了这篇文章
类别:┆阅读(0)┆评论(0)
10:52:34 13:31:12 09:53:05 12:28:55 11:57:21 17:12:33 09:41:44

我要回帖

更多关于 java字符串编码转换 的文章

 

随机推荐