计算机编码基础----中文乱码问题分析

中文与ascii码相互转换解决乱码问题 中文转ascii码 char[] descriptionCharArray = jo.getString("alertContent").toCharArray(); StringBuffer sbDescription = new StringBuffer(); for (int j = 0; j < descriptionCharArray.length; j++) { if(j !=... 阅读详情

编码问题的例子

  在Windows自带的Notepad(记事本)程序中输入“联通”两个字,保存后再次打开,会发现“联通”不见了,代之以“��ͨ”的乱码。这是Windows平台上典型的中文编码问题。即文件保存的时候是按照ANSI编码(其实就是GB2312,后面会详细介绍)保存,打开的时候程序按照UTF-8方式对内容解释,于是就出现了乱码。避免乱码的方式很简单,在“文件”菜单中选择“打开”命令,选择保存的文件,然后选择“ANSI”编码,此时就能看到久违的“联通”两个字了。

  在Linux平台上如果使用cat等命令查看文件中的中文内容时,可能出现乱码。这也是编码的问题。简单的说是文件时按照A编码保存,但是cat命令按照当前Locale设定的B编码去查看,在B和A不兼容的时候就出现了乱码。

  为什么写这篇文章

  中文编码由于历史原因牵扯到不少标准,在不了解的时候感觉一头雾水;但其实理解编码问题并不需要你深入了解各个编码标准,只要你明白了来龙去脉,了解了关键的知识点,就能分析和解决日常开发工作中碰到的大部分编码问题。有感于我看过的资料和文章要么不够全面,要么略显枯燥,所以通过这篇文章记录下笔者在日常工作中碰到的中文编码原理相关问题,目的主要是自我总结,如果能给读者提供一些帮助那就算是意外之喜了。由于严谨的编码标准对我来说是无趣的,枯燥的,难以记忆的,本文尝试用浅显易懂的生活语言解释中文编码相关的(也可能不相关的)一些问题,这也是为什么取名杂谈的原因。本文肯定存在不规范不全面的地方,我会在参考资料里给出官方文档的链接,也欢迎读者在评论中提出更好的表达方式&指出错误,不胜感激。

  对编码问题的理解我认为分为三个层次,第一个层次:概念,知道各个编码标准的应用场景,了解之间的差异,能分析和解决常见的一些编码问题。第二个层次:标准,掌握编码的细节,如编码范围,编码转换规则,知道这些就能自行开发编码转换工具。第三个层次,使用,了解中文的编码二进制存储,在程序开发过程中选择合理的编码并处理中文。为了避免让读者陷入编码标准的黑洞无法脱身(不相信?看看unicode的规范就明白我的意思了),同时由于编码查询&转换工具等都有现成工具可以使用,本文只涉及第一个层次,不涉及第二层次,在第三层次上会做一些尝试。在本文的最后提供了相关链接供对标准细节感兴趣的同学继续学习。最后,本文不涉及具体软件的乱码问题解决,如ssh,shell,vim,screen等,这些话题留给剑豪同学专文阐述。

  一切都是因为电脑不识字

  电脑很聪明,可以帮我们做很多事情,最开始主要是科学计算,这也是为什么电脑别名计算机。电脑又很笨,在她的脑子里只有数字,即所有的数据在存储和运算时都要使用二进制数表示。这在最初电脑主要用来处理大量复杂的科学计算时不是什么大问题,但是当电脑逐步走入普通人的生活时,情况开始变糟了。办公自动化等领域最主要的需求就是文字处理,电脑如何来表示文字呢?这个问题当然难不倒聪明的计算机科学家们,用数字来代表字符呗。这就是“编码”。

  英文的终极解决方案:ASCII

  每个人都可以约定自己的一套编码,只要使用方之间了解就ok了。比如说咱俩约定0×10表示a,0×11表示b。在一开始也的确是这样的,出现了各式各样的编码。这样有两个问题:1. 各个编码的字符集不一样,有的多,有的少。2. 相同字符的编码也不一样。你这里a是0×10,他那里a可能是0×30。于是你保存的文件他就不能直接用,必须要转换编码。随着沟通范围的扩大,采用不同编码的人们互相通信就乱套了,这就是我们常说的:鸡同鸭讲。如果要避免这种混乱,那么大家就必须使用相同的编码规则,于是美国有关的标准化组织就出台了ASCII(American Standard Code for Information Interchange)编码,统一规定了英文常用符号用哪些二进制数来表示。ASCII是标准的单字节字符编码方案,用于基于文本的数据。

  ASCII最初是美国国家标准,供不同计算机在相互通信时用作共同遵守的西文字符编码标准,已被国际标准化组织(International Organization for Standardization, ISO)定为国际标准,称为ISO 646标准。适用于所有拉丁文字字母。ASCII 码使用指定的7位或8位二进制数组合来表示128或256种可能的字符。标准ASCII 码也叫基础ASCII码,使用7位二进制数来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。而最高位为1的另128个字符(80H—FFH)被称为“扩展ASCII”,一般用来存放英文的制表符、部分音标字符等等的一些其它符号。

  其中:0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),32~126(共95个)是字符(32是空格),其中48~57为0到9十个阿拉伯数字,65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。

  现在所有使用英文的电脑终于可以用同一种编码来交流了。理解了ASCII编码,其他字母型的语言编码方案就触类旁通了。

  一波三折的中文编码

  第一次尝试:GB2312

  ASCII这种字符编码规则显然用来处理英文没有什么问题,它的出现极大的促进了信息在西方尤其是美国的传播和交流。但是对于中文,常用汉字就有6000以上,ASCII 单字节编码显然是不够用。为了粉碎美帝国主义通过编码限制中国人民使用电脑的无耻阴谋,中国国家标准总局发布了GB2312码即中华人民共和国国家汉字信息交换用编码,全称《信息交换用汉字编码字符集——基本集》,1981年5月1日实施,通行于大陆。GB2312字符集中除常用简体汉字字符外还包括希腊字母、日文平假名及片假名字母、俄语西里尔字母等字符,未收录繁体中文汉字和一些生僻字。 EUC-CN可以理解为GB2312的别名,和GB2312完全相同。

  GB2312是基于区位码设计的,在区位码的区号和位号上分别加上A0H就得到了GB2312编码。这里第一次提到了“区位码”,我就连带把下面这几个让人摸不到头脑的XX码一锅端了吧:

  区位码,国标码,交换码,内码,外码

  区位码:就是把中文常用的符号,数字,汉字等分门别类进行编码。区位码把编码表分为94个区,每个区对应94个位,每个位置就放一个字符(汉字,符号,数字都属于字符)。这样每个字符的区号和位号组合起来就成为该汉字的区位码。区位码一般用10进制数来表示,如4907就表示49区7位,对应的字符是“学”。区位码中01-09区是符号、数字区,16-87区是汉字区,10-15和88-94是未定义的空白区。它将收录的汉字分成两级:第一级是常用汉字计3755个,置于16-55区,按汉语拼音字母/笔形顺序排列;第二级汉字是次常用汉字计3008个,置于56-87区,按部首/笔画顺序排列。在网上搜索“区位码查询系统”可以很方便的找到汉字和对应区位码转换的工具。为了避免广告嫌疑和死链,这里就不举例了。

  国标码: 区位码无法用于汉字通信,因为它可能与通信使用的控制码(00H~1FH)(即0~31,还记得ASCII码特殊字符的范围吗?)发生冲突。于是ISO2022规定每个汉字的区号和位号必须分别加上32(即二进制数00100000,16进制20H),得到对应的国标交换码,简称国标码,交换码,因此,“学”字的国标交换码计算为:

  00110001 00000111
+ 00100000 00100000
 -------------------
  01010001 00100111 

  用十六进制数表示即为5127H。

  交换码:即国标交换码的简称,等同上面说的国标码。

  内码:由于文本中通常混合使用汉字和西文字符,汉字信息如果不予以特别标识,就会与单字节的ASCII码混淆。此问题的解决方法之一是将一个汉字看成是两个扩展ASCII码,使表示GB2312汉字的两个字节的最高位都为1。即国标码加上128(即二进制数10000000,16进制80H)这种高位为1的双字节汉字编码即为GB2312汉字的机内码,简称为内码。20H+80H=A0H。这也就是常说的在区位码的区号和位号上分别加上A0H就得到了GB2312编码的由来。

  00110001 00000111
+ 10100000 10100000
  -------------------
  11010001 10100111 

  用十六进制数表示即为D1A7H。

  外码:机外码的简称,就是汉字输入码,是为了通过键盘字符把汉字输入计算机而设计的一种编码。 英文输入时,相输入什么字符便按什么键,外码和内码一致。汉字输入时,可能要按几个键才能输入一个汉字。 汉字输入方案有成百上千个,但是这千差万别的外码输入进计算机后都会转换成统一的内码。

  最后总结一下上面的概念。中国国家标准总局把中文常用字符编码为94个区,每个区对应94个位,每个字符的区号和位号组合起来就是该字符的区位码, 区位码用10进制数来表示,如4907就表示49区7位,对应的字符是“学”。 由于区位码的取值范围与通信使用的控制码(00H~1FH)(即0~31)发生冲突。每个汉字的区号和位号分别加上32(即16进制20H)得到国标码,交换码。“学”的国标码为5127H。由于文本中通常混合使用汉字和西文字符,为了让汉字信息不会与单字节的ASCII码混淆,将一个汉字看成是两个扩展ASCII码,即汉字的两个字节的最高位置为1,得到的编码为GB2312汉字的内码。“学”的内码为D1A7H。无论你使用什么输入法,通过什么样的按键组合把“学”输入计算机,“学”在使用GB2312(以及兼容GB2312)编码的计算机里的内码都是D1A7H。

  第二次尝试:GBK

  GB2312的出现基本满足了汉字的计算机处理需要,但由于上面提到未收录繁体字和生僻字,从而不能处理人名、古汉语等方面出现的罕用字,这导致了1995年《汉字编码扩展规范》(GBK)的出现。GBK编码是GB2312编码的超集,向下完全兼容GB2312,兼容的含义是不仅字符兼容,而且相同字符的编码也相同,同时在字汇一级支持ISO/IEC10646—1和GB 13000—1的全部中、日、韩(CJK)汉字,共计20902字。GBK还收录了GB2312不包含的汉字部首符号、竖排标点符号等字符。CP936和GBK的有些许差别,绝大多数情况下可以把CP936当作GBK的别名。

  第三次尝试:GB18030

  GB18030编码向下兼容GBK和GB2312。GB18030收录了所有Unicode3.1中的字符,包括中国少数民族字符,GBK不支持的韩文字符等等,也可以说是世界大多民族的文字符号都被收录在内。GBK和GB2312都是双字节等宽编码,如果算上和ASCII兼容所支持的单字节,也可以理解为是单字节和双字节混合的变长编码。GB18030编码是变长编码,有单字节、双字节和四字节三种方式。

  其实,这三个标准并不需要死记硬背,只需要了解是根据应用需求不断扩展编码范围即可。从GB2312到GBK再到GB18030收录的字符越来越多即可。万幸的是一直是向下兼容的,也就是说一个汉字在这三个编码标准里的编码是一模一样的。这些编码的共性是变长编码,单字节ASCII兼容,对其他字符GB2312和GBK都使用双字节等宽编码,只有GB18030还有四字节编码的方式。这些编码最大的问题是2个。1. 由于低字节的编码范围和ASCII有重合,所以不能根据一个字节的内容判断是中文的一部分还是一个独立的英文字符。2. 如果有两个汉字编码为A1A2B1B2,存在A2B1也是一个有效汉字编码的特殊情况。这样就不能直接使用标准的字符串匹配函数来判断一个字符串里是否包含某一个汉字,而需要先判断字符边界然后才能进行字符匹配判断。

  最后,提一个小插曲,上面讲的都是大陆推行的汉字编码标准,使用繁体的中文社群中最常用的电脑汉字字符集标准叫大五码(Big5),共收录13,060个中文字,其中有二字为重覆编码(实在是不应该)。Big5虽普及于中国的台湾、香港与澳门等繁体中文通行区,但长期以来并非当地的国家标准,而只是业界标准。倚天中文系统、Windows等主要系统的字符集都是以Big5为基准,但厂商又各自增删,衍生成多种不同版本。2003年,Big5被收录到台湾官方标准的附录当中,取得了较正式的地位。这个最新版本被称为Big5-2003。

  天下归一Unicode

  看了上面的多个中文编码是不是有点头晕了呢?如果把这个问题放到全世界n多个国家n多语种呢?各国和各地区自己的文字编码规则互相冲突的情况全球信息交换带来了很大的麻烦。

  要真正彻底解决这个问题,上面介绍的那些通过扩展ASCII修修补补的方式已经走不通了,而必须有一个全新的编码系统,这个系统要可以将中文、日文、法文、德文……等等所有的文字统一起来考虑,为每一个文字都分配一个单独的编码。于是,Unicode诞生了。Unicode(统一码、万国码、单一码)为地球上(以后会包括火星,金星,喵星等)每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。在Unicode里,所有的字符被一视同仁,汉字不再使用“两个扩展ASCII”,而是使用“1个Unicode”来表示,也就是说,所有的文字都按一个字符来处理,它们都有一个唯一的Unicode码。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位(码位就是可以分配给字符的数字)。

  提到Unicode不能不提UCS(通用字符集Universal Character Set)。UCS是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集。UCS-2用两个字节编码,UCS-4用4个字节编码。Unicode是由unicode.org制定的编码机制,ISO与unicode.org是两个不同的组织, 虽然最初制定了不同的标准; 但目标是一致的。所以自从Unicode 2.0开始, Unicode采用了与ISO 10646-1相同的字库和字码, ISO也承诺ISO10646将不会给超出0x10FFFF的UCS-4编码赋值, 使得两者保持一致。大家简单认为UCS等同于Unicode就可以了。

  在Unicode中:汉字“字”对应的数字是23383。在Unicode中,我们有很多方式将数字23383表示成程序中的数据,包括:UTF-8、UTF-16、UTF-32。UTF是“UCS Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。例如,“汉字”对应的数字是0x6c49和0x5b57,而编码的程序数据是:

BYTE data_utf8[] = {0xE6, 0xB1, 0x89, 0xE5, 0xAD, 0x97}; // UTF-8编码
WORD data_utf16[] = {0x6c49, 0x5b57}; // UTF-16编码
DWORD data_utf32[] = {0x6c495b57}; // UTF-32编码 

  这里用BYTE、WORD、DWORD分别表示无符号8位整数,无符号16位整数和无符号32位整数。UTF-8、UTF-16、UTF-32分别以BYTE、WORD、DWORD作为编码单位。“汉字”的UTF-8编码需要6个字节。“汉字”的UTF-16编码需要两个WORD,大小是4个字节。“汉字”的UTF-32编码需要两个DWORD,大小是8个字节。根据字节序的不同,UTF-16可以被实现为UTF-16LE或UTF-16BE,UTF-32可以被实现为UTF-32LE或UTF-32BE。

  下面介绍UTF-8、UTF-16、UTF-32、BOM。

  UTF-8

  UTF-8以字节为单位对Unicode进行编码。从Unicode到UTF-8的编码方式如下:  

Unicode编码(16进制) UTF-8 字节流(二进制)
000000 – 00007F 0xxxxxxx
000080 – 0007FF 110xxxxx 10xxxxxx
000800 – 00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000 – 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

  UTF-8的特点是对不同范围的字符使用不同长度的编码。对于0×00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。从上表可以看出,4字节模板有21个x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。总结了一下规律:UTF-8的第一个字节开始的1的个数代表了总的编码字节数,后续字节都是以10开始。由上面的规则可以清晰的看出UTF-8编码克服了中文编码的两个问题。

  例1:“汉”字的Unicode编码是0x6C49。0x6C49在0×0800-0xFFFF之间,使用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。

  例2:Unicode编码0x20C30在0×010000-0x10FFFF之间,使用用4字节模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将0x20C30写成21位二进制数字(不足21位就在前面补0):0 0010 0000 1100 0011 0000,用这个比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。

  UTF-16

  UTF-16编码以16位无符号整数为单位。我们把Unicode编码记作U。编码规则如下:如果U<0×10000,U的UTF-16编码就是U对应的16位无符号整数(为书写简便,下文将16位无符号整数记作WORD)。中文范围 4E00-9FBF,所以在UTF-16编码里中文2个字节编码。如果U≥0×10000,我们先计算U’=U-0×10000,然后将U’写成二进制形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16编码(二进制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。

  UTF-32

  UTF-32编码以32位无符号整数为单位。Unicode的UTF-32编码就是其对应的32位无符号整数。

  字节序

  根据字节序(对字节序不太了解的同学请参考http://en.wikipedia.org/wiki/Endianness)的不同,UTF-16可以被实现为UTF-16LE(Little Endian)或UTF-16BE(Big Endian),UTF-32可以被实现为UTF-32LE或UTF-32BE。例如:  

Unicode编码 UTF-16LE UTF-16BE UTF-32LE UTF-32BE
0x006C49 49 6C 6C 49 49 6C 00 00 00 00 6C 49
0x020C30 43 D8 30 DC D8 43 DC 30 30 0C 02 00 00 02 0C 30

  那么,怎么判断字节流的字节序呢?Unicode标准建议用BOM(Byte Order Mark)来区分字节序,即在传输字节流前,先传输被作为BOM的字符”零宽无中断空格”。这个字符的编码是FEFF,而反过来的FFFE(UTF-16)和FFFE0000(UTF-32)在Unicode中都是未定义的码位,不应该出现在实际传输中。下表是各种UTF编码的BOM:  

UTF编码 Byte Order Mark
UTF-8 EF BB BF
UTF-16LE FF FE
UTF-16BE FE FF
UTF-32LE FF FE 00 00
UTF-32BE 00 00 FE FF

  总结一下,ISO与unicode.org都敏锐的意识到只有为世界上每种语言中的每个字符设定统一并且唯一的二进制编码才能彻底解决计算机世界信息交流中编码冲突的问题。由此诞生了UCS和Unicode,而这两个规范是一致的。在Unicode里,所有的字符被一视同仁,也就是说,所有的文字都按一个字符来处理,它们都有一个唯一的Unicode码。UTF-8、UTF-16、UTF-32分别定义了怎样将Unicode定义的数字转换成程序数据。UTF-8以字节为单位对Unicode进行编码,一个英文字符占1个字节,汉字占3个字节;UTF-16以16位无符号整数为单位对Unicode进行编码,中文英文都占2个字节;UTF-32以32位无符号整数为单位对Unicode进行编码,中文英文都占4个字节。可以在http://www.unicode.org/charts/unihan.html 查看汉字的unicode码以及UTF-8、UTF-16、UTF-32编码。

  中文二进制存储

  介绍了这么多的编码知识,真正的文件内容是什么样子的呢?下面我们就通过实验看看在笔者Linux机器上 “中文”这两个字在不同的编码下保存的文件内容。下面是我的实验过程,有兴趣的同学可以在自己的机器上重做一下。Window平台上的情况类似这里就不赘述了。

  实验需要需要使用2个工具:

  1. od 查看文件内容:http://www.gnu.org/software/coreutils/manual/html_node/od-invocation.html
  2. iconv 编码转换工具:http://www.gnu.org/software/libiconv/
汉字 Unicode(ucs-2)10进制表示 Utf-8 Utf-16 Utf32 区位码 GB2312/GBK/GB18030
中 20013 E4 B8 AD 4E2D 00004E2D 5448 D6D0
文 25991 E6 96 87 6587 00006587 4636 CEC4

  机器环境:

  OS: Red Hat Enterprise Linux AS release 4

  CPU: Intel(R) Xeon(R) CPU

  Locale:LC_ALL=zh_CN.utf-8

//生成utf8编码下的文件
echo –n "中文" > foo.utf8
//检查foo的内容:
od -t x1 foo.utf8
0000000 e4 b8 ad e6 96 87
//转换为utf16编码
iconv -f utf-8 -t utf-16 foo.utf8 > foo.utf16
//查看foo.utf16内容
od -t x1 foo.utf16
0000000 ff fe 2d 4e 87 65
Ff fe是BOM(还记得吗?通过BOM来字节流的字节序),其余部分的确是UTF-16LE编码的内容
//转换为utf32编码
iconv -f utf-16 -t utf-32 foo.utf16 > foo.utf32
//查看foo.utf32内容
od -t x1 foo.utf32
0000000 ff fe 00 00 2d 4e 00 00 87 65 00 00
Ff fe是BOM,的确是UTF-32LE编码的内容
//转换为gb2312编码
iconv -f utf-8 -t gb2312 foo.txt > foo.gb2312
od -t x1 foo.gb2312
0000000 d6 d0 ce c4
//转换为GBK编码
iconv -f utf-8 -t gbk foo.txt > foo.gbk
od -t x1 foo.gbk
0000000 d6 d0 ce c4
//转换为GB18030编码
iconv -f utf-8 -t gb18030 foo.txt > foo.gb18030
od -t x1 foo.gb18030
0000000 d6 d0 ce c4 

  C语言中文处理

  先明确一个概念:程序内部编码和程序外部编码。程序内部编码指的是中文字符在程序运行时在内存中的编码形式。程序外部编码则是中文字符在存储或者传输时的编码形式。程序外部编码的最直观的例子就是当把中文存储到硬盘文件中时选择的编码。

  根据程序内部编码和程序外部编码是否一致,C/C++的中文处理有两种常见的方式:

  1. 内外编码相同。输入输出时不需要考虑编码转换,程序内部处理时把中文字符当做普通的2进制数据流进行处理。
  2. 内外编码不同。输入输出的时候根据应用需要选择合适的编码格式进行编码转换;程序内部统一编码处理。

  方法1的优点不言而喻,由于内外统一,不需要进行转换。不足是如果不是C标准库支持的编码方式,那么字符串处理函数需要自己实现。比如说标准strlen函数不能计算中文编码&UTF-8等的字符串长度,而需要根据编码标准自行实现。GBK等中文编码除了计算字符串长度的函数外,字符串匹配函数也要自己实现(原因看上文中文编码总结)。当需要支持的编码格式不断增多时,处理函数的开发和维护就需要付出更大的代价。

  方法2针对方法1的不足加以改进。在程序内部可以优先选择C标准库支持的编码方式,或者根据需要自己实现对某一特定编码格式的完整支持,这样任何编码都可以先转换为支持的编码,代码通用性比较好。

  那么C标准库对中文编码的支持如何呢?目前Linux平台一般使用GNU C library,内建了对单字节的char和宽字符wchar_t的支持。Char大家都很熟悉了,处理中文需要的wchar_t要重点介绍一下。从实现上来说在Linux平台上可以认为wchar_t是4byte的int,内部存储字符的UTF32编码。由于标准库已经内建了对wchar_t比较完备的支持,如使用wcslen 计算字符串长度,使用wcscmp进行字符串比较等等。所以比较简单的方式是使用上面的方法2,同时选择wchar_t作为内部字符的表示。做到这一点还是比较容易的,在输入输出的时候通过mbrtowc/wcrtomb 进行单个字符的内外编码转换,以及通过mbsrtowcs/wcsrtombs 进行字符串的内外编码转换即可。这里需要注意两点:

  1. 代码中字符串常量的表示不同。举例说明:Char c=’a’; Wchar_t wc=L’中’;
  2. 上面两组函数的转换是依赖locale设置的,即locale决定了外部编码的类型。确切的说是LC_CTYPE决定了外部编码的类型。默认情况下程序启动时使用标准“C”locale,而不是LC系列的环境变量指定的。所以需要首先调用下面的函数:setlocale (LC_ALL, “”);这样程序就使用了用户通过设置LC系列环境变量选择的Locale。

  关于locale的话题比较大,这里就不深入了,留待下一篇文章吧.

  上面的方法很完美,是吗?不是吗?得到这么多的好处不是无代价的,最明显的代价就是内存,任何一个字符,不管中文还是英文如果保持在wchar_t里就需要4个byte,就这一个理由就足以限制了这个方案在关注内存使用的应用场景下的使用。

  Python的中文处理

  对Python来说由于内建Unicode的支持,所以采用输入输出的时候进行转换,内部保持Unicode的方式使用是个不错的方案。http://docs.python.org/tutorial/introduction.html#unicode-strings这里作为起点,有兴趣的同学自学吧。

  编码选择建议:

  1. 只有英文:毫不犹豫选择内外编码都选择ASCII,通用且存储代价小。
  2. 主要存中文,对存储大小比较敏感:内外部编码根据文字使用范围选择GB2312或者GBK,自行实现使用到的字符串处理函数。
  3. 通用性第一,处理简单:外部选择UTF-8,内部可以使用UTF-8或者UTF-32(即wchar_t)
中文乱码问题解析与解决方案 本文主要介绍了Java开发过程中中文乱码问题解析与解决方案、常见字符集。 阅读详情

相关推荐

QT中出现的中文乱码问题及其解决方案

QT中出现的中文乱码问题及其解决方案 软件平台:Windows 10,Qt Creator 4.7.2,CMake 3.13.4 问题描述:运行一个含有中文输出的cmake项目时,在控制台中输出的都是中文乱码。出现中文乱码的原因在于,中文版的Windows平台支持的是GB2312的编码格式,而Qt Creator默认使用的则是Linux下通用的UTF-8编码格式,然后,基于Windows平台的程序...

江南好 1万+

中文编码杂谈

编码问题的例子   在Windows自带的Notepad(记事本)程序中输入“联通”两个字,保存后再次打开,会发现“联通”不见了,代之以“��ͨ”的乱码。这是Windows平台上典型的中文编码问题。即文件保存的时候是按照ANSI编码(其实就是GB2312,后面会详细介绍)保存,打开的时候程序按照UTF-8方式对内容解释,于是就出现了乱码。避免乱码的方式很简单,在“文件”菜单中选择“打开”命令,选...

weixin_33967071的博客 403

PHP中文乱码问题全面解决方案

HTTP头信息是HTTP协议中定义了请求和响应行为的文本信息。它们在客户端和服务器之间以键值对的形式传输,用于传递关于请求、响应或数据传输的额外信息。例如,请求头中的User-Agent标识浏览器类型,而响应头中的Content-Type告知客户端响应的内容类型,如text/html或application/json。理解HTTP头信息的作用对优化Web应用性能、确保数据正确传输及加强安全防护至关重要。

weixin_42590539的博客 1191

理清文件编码,告别中文乱码

理清文件编码,告别中文乱码 在工作中少不了碰到文件乱码的问题,面对这么多的中文汉字编码,真不知该选哪个好,不如彻底搞清楚,让乱码灰飞烟灭;以下介绍文本编码中常用到的几种:iso8859-1、GDK/GB2312、ANSI、unicode 以及UTF ; iso8859-1 iso8859-1 通常叫做Latin-1,属于单字节编码,最多能表示的字符范围是0-255,应用于英文系列。比如,...

weixin_33911824的博客 544

别再调参了!用Simulink手把手教你搞定FOC电机速度环PI控制器(附模型下载)

本文详细介绍了如何使用Simulink搭建FOC电机速度环PI控制器,通过模最优准则和系统化调试方法,避免传统手动调参的繁琐过程。文章包含从传递函数推导到参数自动整定的完整流程,并提供了实战调试技巧与异常处理指南,帮助工程师快速实现电机控制系统的稳定运行。

weixin_42519985的博客 415

阿里平头哥与华为芯片的比较

阿里平头哥与华为芯片代表了中国AI芯片领域的两种典型发展路径:平头哥通过兼容CUDA生态快速切入市场,以较低成本提供接近国际水平的AI算力;华为则坚持全栈自研路线,构建从芯片到框架的完整技术体系,更注重供应链安全和自主可控。从短期来看,平头哥PPU在单卡性能参数上全面超越昇腾910B,接近英伟达H20水平,且成本优势明显,有望在云服务和互联网应用领域取得更大市场份额。而华为昇腾则凭借全栈自研和端边云协同优势,在政企市场和科研领域更具竞争力。

shao.bing的专栏 4018

分析记事本写联通两字出现乱码的原因

现象: 当你在 windows 的记事本里新建一个文件,输入"联通"两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!   分析: 这时我们看看utf8的编码格式:     UCS-2编码(16进制) UTF-8 字节流(二进制)     0000 - 007F 0xxxxxxx     0080 - 07FF 110xxxxx 10xxxxxx    ...

ruowu的专栏 3083

Spring Boot 构建war 部署到tomcat下无法在Nacos中注册服务

因此,当使用外部容器时,如此处的 Tomcat 来部署项目,AbstractAutoServiceRegistration 就不能监听到容器启动事件了,也就不会尝试向服务注册中心注册当前这个微服务,那么注册就失败了,并且也就没有异常信息了。自定义获取获取外部容器端口的方法, 然后监听应用启动事件,当应用被启动时,获取外部容器启动的端口号,然后将这个 port 设置到 NacosAutoServiceReigistration 中。在启动类同级目录,复制下面代码即可(不需要修改,直接构建war即可)

ttyy1112的专栏 2519

txt编码更改后退出又变回原来的

按找网上的另存,把utf8换成ansi,但是关闭后重新打开有变成了utf8,之前都没这种情况的,求助怎么解决

qq_63223460的博客 2852

STM32基于软件模拟IIC进行AHT21B温湿度采集

通过学习软件IIC来进行实操AHT21B采集温湿度。

Liu-FE的博客 3700

基于FreeRedis实现的.NET Redis客户端缓存缓存键条件优雅过滤

在.NET开发中,使用Redis作为缓存解决方案是很常见的。FreeRedis是一个功能强大的Redis客户端库,它提供了丰富的功能和灵活性,使我们能够轻松地与Redis进行交互。在本文中,我们将探讨如何使用FreeRedis实现缓存键条件的优雅过滤,以提高缓存的效率和准确性。

RzuScala的博客 329

中文编码、乱码问题解析处理

中文乱码问题源于字符编码与解码规则不一致。计算机通过编码将字符转换为二进制存储,常见编码包括ASCII(英文)、GBK(中文)和UTF-8(国际通用)。乱码通常发生在:1)文件编码与打开方式不匹配;2)跨平台传输时系统默认编码不同;3)数据库字符集设置不一致。解决方案是统一使用UTF-8编码,并在文件中明确声明编码格式。现代编程语言和工具已普遍支持UTF-8,采用这一标准能从根源避免大部分乱码问题。

cnds123的专栏 1万+

一文让你轻松理解Wi-Fi理论速率计算方式

如上所示,在使用FDM的802.11协议上,最小子载波带宽为312.5KHz,那一个信道频宽为20MHz的信道可以分成64个子载波,但是由于技术原因,有效使用的是52个子载波,实际用于数据传输的只有48个,有4个子载波充当导频载波。载波:交流信号或者直流信号本身不具备数据传输能力,但是如果信号发生很小的波动或者变化,从而实现数据的收发。所谓子载波,就是将信号划为为很多的小信道,这些小信道可以实现数据的并发传输,子载波我们英文称为subcarrier,也称为音调(tone)。Symbol time如下所示。

1042484520@qq.com(邮箱) 3450

CSV上传转Excel中文乱码根因排查与编码归一化实践

在计算机系统中,字符编码是数据正确显示的基础,不同编码规则(如UTF-8、GBK)决定了字符与字节的映射方式。当文本以A编码生成,却用B编码解释时,便会出现中文乱码,例如“锟斤拷”这类典型错误。掌握编码转换原理,有助于在数据链路中快速定位问题,避免因平台默认编码差异导致生产事故。这一能力在文件上传下载、数据交换、多语言系统等场景中尤为重要。本文从一次CSV上传后转Excel出现中文乱码的真实问题出发,详细分析乱码根因,并给出了基于编码探测与归一化的工程化修复方案,帮助开发者系统性预防乱码。

weixin_30899023的博客 179

孔雀优化算法(POA)详解:从孔雀智能到优化引擎

孔雀优化算法(POA)是一种新型元启发式优化算法,灵感来源于孔雀的社会行为和智能狩猎策略。该算法将孔雀群体分为雄孔雀、雌孔雀和幼孔雀三个阶层,分别对应全局探索、平衡搜索和局部开发三种搜索策略。通过模拟孔雀的求偶展示、自适应接近和Levy飞行等行为,POA实现了探索与开发的动态平衡。算法采用角色分配机制和位置更新方程,结合参数自适应调整,展现出优于传统算法的全局搜索能力和收敛性能。POA已成功应用于多个优化问题,其生物启发机制为解决复杂优化问题提供了新思路。

qq_42212808的博客 137

基于CutLSTM-ATT 模型的刀具磨损预测-【一个月之内发一篇中文核心辅导】

基于CutLSTM-ATT 模型的刀具磨损预测-【一个月之内发一篇中文核心辅导】

317

bat批处理脚本中文乱码问题解决

解决bat批处理脚本中文乱码问题

极乐谷2的专栏 8万+

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

从底层原理出发解决所有 Python 中文乱码(字符编码问题)

本文从 Python2.7 中出现的一些乱码案例出发,从字符编码、Python2.7字符原理分析了中文乱码、编码问题导致的原因并给出相应的解决方案,实际上在编码乱码分析这一块在其它语言上也是共通的,可以作为一定的参考依据。

Lorin 洛林的后端技术小站 3827

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

python 中文编码乱码问题原理分析及解决思路

python 中文编码乱码问题原理分析及解决思路

沙漠之花的博客 1万+

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

中文编码问题分析及几种乱码场景

1、为什么要编码   编码的原因可以总结为: ◎ 计算机中存储信息的最小单元是一个字节(byte),即8个bit,所以能表示的字符范围是0~255个。 ◎ 人类要表示的符号太多,无法用一个字节来完全表示。 要解决这个矛盾必须要有一个新的数据结构char,从char到byte必须编码。 2、几种编码格式 1.ASCII码 学过计算机的人都知道ASCII码,总共有128个,用一个字节的...

聚沙成塔 6007

第十课:Qt 字符编码和中文乱码相关问题

文章详细介绍了字符编码的相关知识,然后对 Qt 中文乱码原因进行分析,最终给出中文乱码的解决办法

Mr-Gui的博客 2530

解决爬虫中文数据乱码问题

常见的问题 在爬虫爬取数据过程中直接爬取数据中文字符出现乱码现象 经过查找资料发现,网页返回的字符集和爬取下来后转换编码不一致,这必然会引起来乱码,当源网页编码和抓取下来后程序直接使用处理编码一致时,则不会出现乱码,此时在进行统一的编码则可以完美的显示。 print(response.encoding) #查看网页返回的字符集类型 print(response.apparent_encoding) #自动判断字符集类型 常见的编码 最早的编码是iso8859-1和ascii编码类似,iso8859-

李昂的博客 1万+

【Java】深入分析Java中的中文编码问题

文章目录一、几种常见的编码格式1、为什么要编码2、如何“翻译”二、Java 中需要编码的场景1、I/O 操作中存在的编码2、内存中操作中的编码三、Java 中如何编解码1、按照 ISO-8859-1 编码2、按照 GB2312 编码3、按照 GBK 编码4、按照 UTF-16 编码5、按照 UTF-8 编码6、几种编码格式的比较四、Java Web 涉及到的编码1、URL 的编解码2、HTTP Header 的编解码3、POST 表单的编解码4、HTTP BODY 的编解码5、其它需要编码的地方五、常见问题

qgnczmnmn的博客 3643

计算机中的乱码问题以及什么是字符集和编码

计算机中的乱码问题交流

weixin_52379434的博客 2061

计算机无法识别ANSI编码文件里的中文导致乱码

问题现象 最近远程协助一个用户的电脑(TeamView竟然连接不上,只好用QQ远程啦),原来ANSI编码的ini文件里的中文是正常的;用着用着,某一天就乱码了,无法识别ANSI编码文件里的中文。创建ANSI编码的ini配置文件,输入中文后保存会提示“…该文件有Unicode格式的字符,当文件保存为ANSI编码的文本时,该字符将丢失…”的问题。 原因 Unicode和ANSI是两种不同的字符编...

weixin_42804574的博客 2万+

中文乱码——编码方式

这样各搞一套,在本地使用没有问题,一旦出现在网络中,由于不兼容,互相访问就出现了乱码现象。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,也是一种前缀码。UTF-8编码中一个文字占3个字节。

西西弗的博客 902
上一篇: 十条不错的编程观点
下一篇: 心态和想法,是提高编程水平的关键
dw_java08
博客等级 码龄15年 122粉丝 66原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值