当前位置:C++技术网 > 精选软件 > 字符集、字符编码、C语言字符编码、宽字符之间的关系全面分析

字符集、字符编码、C语言字符编码、宽字符之间的关系全面分析

更新时间:2015-12-04 12:13:55浏览次数:1+次

    为了清楚的说明这个问题,我已经制作了一个字符集、字符编码、C语言字符编码、宽字符之间的关系对比图,让你先快速有一个印象,后面再详细解释。图来了:

  字符集、字符编码、C语言字符编码、宽字符之间的关系

    你可以在C++技术网搜索“字符集”关键词,可以搜索到相关文章,对于字符集等做了详细的解释哈。这里只解释这几个的关系而已,让你从整体上有个清晰的把握。

    在《DBCS双字节字符集和MBCS多字节字符集的关系总结》中,我总结了下DBCS和MBCS的关系。CJK是中国、日本和韩国的英文单词的单词的首字母的组合,表示的是东方的字符的系列。中国的包含了简体中文和繁体中文两种。这四套字符才会用上DBCS,欧美等国家都不需要。那么CJK就代表了这系列的字符,而就中国而言,也有很多字符集标准,比如GB2312、GBK等,其他的国家我们就不关心了。中国具体的字符集种类,在网站搜索“字符集”来阅读相关文章。

   那么这里总的来说,字符集就是一类字符的集合,我们把需要用的所有字符放在一起存放,就组成了一个字符集。为了方便查找字符,就将字符做成表格形式排布好,然后横向竖向编一个号,和excel表格一样,格子上就放了所有的字符,然后找字符的时候,就可以根据横竖方向的编号组合来定位都这个字符。这个编号组合就是字符的编码。

    ASCII字符集是美国使用的字符集合,后来扩展了,支持更多的欧洲等国家的字符。但是对于中国这些文字来说是不够的。所以就出现了CJK这类字符集。世界上那么多国家,使用的字符都不一样,因此为了能够兼容全球的字符,就使用多字节字符集编码方式编码MBCS,前面提到的文章讲过一点。

    就美国而言,ASCII就够了。ASCII字符集用的是ASCII字符集编码,是一个字节表示一个字符,属于单字节编码。后面就是为了兼容全球字符,所以使用了DBCS来编码表示CJK的字符集。因为中文中也会混合使用英文字母,比如“this is 中国 People”。所以,要支持就要同时表示英文和中文,这样就是DBCS出现的必要。DBCS在表示英文字符时用一个字节,如果表示中文等字符时,则使用两个字节。第一个字节确定属于哪个国家的字符集,然后和第二个字节组合起来确定具体是哪个字符。

    因为DBCS是单字节和双字节混合表示的,因此在处理时很麻烦。ASCII一个字节对应一个字符,统计字符串占用的字节数就可以知道字符数了。然而DBCS你就头痛了。

    所以,后来就统一成双字节的字符集和字符编码,这就是Unicode。Unicode使用16位来编码表示,可以表示全球所有的字符,所以就非常方便。

    那么总的来说,我们可以理顺下,字符集中,ASCII字符集是单字节字符集,Unicode字符集是双字节字符集,而CJK系列字符集则是双字节字符集。编码也是对应的。而在VS中,将DBCS看成是MBCS,目前来讲,没有什么区别。

    那么现在让人疑惑的就是编写代码时经常涉及的编码问题。首先我要说一下宽字符。宽字符其实指的是Unicode字符集编码的字符,即用两个字节表示的字符。而所谓的窄字符,则是ASCII编码的字符,用一个字节表示。

    你到底如何确定程序中使用的编码呢?这个就和语言有关系了。

    所谓的编码,就是对字符的一种解释方式。ASCII将一个字节解释为一个字符,Unicode将两个字节解释为一个字符,最终解释成什么字符,则去查字符集表就知道了。而DBCS则是单双字节混合解释的,前面解释了。

    在C语言中,使用char来表示单字节的字符数据类型,那么使用char数组是表示用单字节形式存储字符的。但是这个实际上和编码没有直接关系。存储和解释不是一回事。但是存储是对字符集编码的支持,用单字节存储ASCII编码的字符,就可以很好的操作和解释ASCII字符,因为一个char就是一个字符。Unicode也同理。只不过,在C语言中,使用wchar_t数据类型来存储Unicode字符,两个字节。

    而最麻烦的就是多字节字符集了。多字节字符集也是基于单字节来解释的。因为他要分析每一个字节,来确定当前字符是用一个字节表示的还是用两个字节表示的,所以,多字节字符集编码,在C语言中支持的字符类型也是char。

    在VS中的项目属性里,有一个设置字符集编码的,只有Unicode和多字节字符集。为什么没有ASCII呢?这个字符集设置到底在哪起作用呢?这两个问题确实值得思考,也非常有必要了解清楚。

    为什么没有ASCII?前面说了,多字节字符集也是用char类型的单字节作为基础支持的,因此和ASCII就一致了。所以,选择多字节字符集,也就代表选择了ASCII字符集编码。如果你只使用英文字符的话,则在处理字符时,比如保存到内存以及读取字符时,编译器是按照多字节字符集来检查每一个字符编码值,如果都在ASCII编码表内,自然就是ASCII编码了。这就是为什么不需要单独添加一个ASCII字符集的原因。当然,如果要是单独添加了ASCII,那么也就是说,选择了ASCII,则只将一个字节处理成一个字符,就不需要检查每一个字节的编码值了,就可以直接到ASCII字符编码表查询对应的字符即可。这种情况下,如果字符串出现了中文,则在ASCII编码表中找不到,就会出现乱码。所以,就没有单独提供ASCII字符集的选项,而是直接使用多字节字符集来兼容ASCII模式了。可能在中文版的VS才没有提供,可能纯英文版就只有ASCII和Unicode,美国人不需要使用中文字符。或者其他IDE可能就提供了三种选项,各家实现不太一样,你只要知道这个道理就行了。

    那么字符集设置后到底在哪起作用呢?我们在写代码时,只有在涉及到字符串时,才会有字符集的问题,其他没有影响。比如我们的win32 API函数的涉及到字符串参数的函数,就会有字符集的问题。微软提供了ASCII版本和Unicode版本一共两个版本的函数,ASCII版本的函数名以A结尾,Unicode版本的函数以W结尾。比如MessageBoxA和MessageBoxW。

    而更为经常出现的字符集问题就是常量字符串。如下代码所示:

char d[]="www.cjjjs.comC++技术网";
wchar_t dd[]="www.cjjjs.comC++技术网";
    无论你如何更改项目的字符集,dd的后面的字符串始终提示错误,d数组的则始终都是对的。因为在IDE中,直接写的常量字符串,始终都是以多字节字符集来解释和存储的,这个和你设置字符集类型无关。所以,wchar_t是两个字节表示一个字符,与Unicode编码方式一致。然而直接写的字符串是多字节字符集表示的,也就是单字节表示一个字符。所以在VS中会提示数据类型不一致。而d数组的就是一致的。所以要将常量字符串表示为宽字符也就是双字节表示一个字符,就与wchar_t一致,你可以在字符串前面加上大写字母L,如
wchar_t dd[]=L"www.cjjjs.comC++技术网";

   这样就可以了。L和双引号之间不能有空格。那么设置的字符集有对编码还有什么影响呢?当你在使用char,wchar_t,MessageBoxA和MessageBoxW这样确定成具体的形式的时候,字符集设置没有卵用。不管是函数、数据类型还是常量字符串都不受影响。

    我们在Win32编程中或MFC编程中,一般都使用通用方式,比如数据类型则使用TCHAR类型,函数MessageBox,字符串使用_T("www.cjjjs.comC++技术网")。那么这类通用形式具体是什么样的数据类型或者函数版本,则不确定的。这种通用形式叫做中性版本。他们的具体版本确定就是根据项目属性的字符集。所以,设置项目字符集影响的就是这些地方。如果设置的字符集是Unicode,那么TCHAR就是wchar_t类型,MessageBox就是MessageBoxW版本,_T("www.cjjjs.comC++技术网")就是L"www.cjjjscnC++技术网"。否则就是ASCII版本咯,就不列举了。

    我想到此就讲清楚了这些关系了,如果还有问题,请留言。如果有不对的地方,请指正。