从XML文件乱码问题,探寻其背后的原理

在日常开发工作中,我们常常会使用到XML,早已成为了一种标准。它的用途很的广泛,但这些不是本文所重点讨论的。

相信大家在做開始时候常常碰到过“乱码”的问题,这是中国程序猿非常头疼的问题。我一直非常想深入研究关于“编码”的原理,无奈水平有限,那些枯燥的 理论(二进制,ASCII,Unicode,UTF-8,gb2312,ISO ...光这些就让我看的两眼发黑了),实在看不下去。也非常难真正搞懂搞明确。望各位网友多指点......

我将用工作中遇到的一个“XML文件乱码”的简单问题,解决这个问题,分析其背后的原理。

首先,我们在本地新建一个文本文件。将后缀名改为".XML”, 然后用用记事本打开,往里面加入一些符合XML文档规范的内容。

如图所看到的:

 

写好之后,按“ctrl+s”保存,然后使用IE浏览器打开该XML文件,验证该XML文档的规范及正确性。不料。竟然解析出错了,例如以下:

 

这是咋回事呢?我的XML文档定义的格式好像没问题啊。无效字符?这肯定是典型的“编码”问题了。聪明的我第一就想到了,调整IE浏览器的“编码”嘛。

但是打开“查看”“编码”。发现那些编码格式全是灰色的,好像不能选择哦。

这是由于,在定义XML文档的时候,指定了编码格式为"UTF-8",这就相当于告诉了浏览器(XML解析引擎):你必须使用"UTF-8"编码去解析我,所以无法使用其它的编码格式去查看了。

这是由于,我们在使用记事本保存该文档的时候,没有选择编码格式。默认使用的是操作系统编码(中文版的系统)。也就是相应的"GB2312”编码。 当我们的IE浏览器,再使用我们指定的UTF-8编码去解析该XML文档的时候。出现了乱码,所以造成了上面的错误。(Windows中的文件保存在硬盘 上,默认使用操作系统编码。

比方我们XML文档中定义的“中国”这两个字,保存好后,假如其相应的GB2312可能是"10001"。而在UTF-8编码 中的,“10001”相应的就不是“中国”了,要么找不到,要么是乱码。所以IE就拒绝显示了)。那我们应该怎么办呢?有两种办法能够解决。

第一,我们在xml文档定义时。指定其编码为gb2312,例如以下图所看到的:

 

保存之后,我们再使用IE浏览器打开,结果如图:

 

恭喜,这个问题攻克了。

可是这样的方法不推荐使用。

由于我们在定义XML文档时候,为了文档的通用性。我们一般使用UTF-8编码。

另外一种方法:

我们再用记事本打开该文档。点击“另存为”,发现以下会有“编码”选项,选择“UTF-8”之后再试。

 

事实上。我们在使用诸如 Eclipse 或者Microsoft Visual Studio之类的开发工具来定义XML文档,并不会碰到上面的问题。原因是这些IDE都非常“聪明”,你的XML文档指定的是那种编码格式。IDE在将 XML文档保存到硬盘的时候,就自己主动使用那种格式。所以,非常多局限于使用某种IDE开发的程序猿,事实上并不明确这些知识及其背后的原理,但他们做开发起来 一样非常顺手。早年据笔者了解,国内有非常多大牛。写代码都是用EditPlus之类的文本编辑器。而那些在Linux/unix上面的大牛。非常多都是用VI /VIM来编码。大概这就是差距吧。(呵呵。当然这不是本文讨论的重点)

 

 

 

补充一点理论知识,不晕的就继续读下去吧。

在最初的时候,Internet上仅仅有一种字符集——ANSI的ASCII字符集,它使用7 bits来表示一个字符,总共表示128个字符。当中包含了英文字母、数字、标点符号等经常使用字符。之后。又进行扩展,使用8 bits表示一个字符,能够表示256个字符。主要在原来的7 bits字符集的基础上增加了一些特殊符号比如制表符。


  后来,因为各国语言的增加,ASCII已经不能满足信息交流的须要,因此,为了可以表示其他国家的文字。各国在ASCII的基础上制定了自己的字符 集,这些从ANSI标准派生的字符集被习惯的统称为ANSI字符集。它们正式的名称应该是MBCS(Multi-Byte Chactacter System,即多字节字符系统)。这些派生字符集的特点是以ASCII 127 bits为基础,兼容ASCII 127,他们使用大于128的编码作为一个Leading Byte。紧跟在Leading Byte后的第二(甚至第三)个字符与Leading Byte一起作为实际的编码。这种字符集有非常多。我们常见的GB-2312就是当中之中的一个。
  比如在GB-2312字符集中,“连通”的编码为C1 AC CD A8,当中C1和CD就是Leading Byte。前127个编码为标准ASCII保留,比如“0”的编码是30H(30H表示十六进制的30)。软件在读取时。假设看到30H,知道它小于 128就是标准ASCII,表示“0”。看到C1大于128就知道它后面有一个另外的编码。因此C1 AC一同构成一个整个的编码,在GB-2312字符集中表示“连”。
  因为每种语言都制定了自己的字符集。导致最后存在的各种字符集实在太多,在国际交流中要经常转换字符集很不便。因此,提出了 Unicode字符集,它固定使用16 bits(两个字节、一个字)来表示一个字符,共可以表示65536个字符。将世界上差点儿全部语言的经常使用字符收录当中,方便了信息交流。

标准的 Unicode称为UTF-16。

后来为了双字节的Unicode可以在现存的处理单字节的系统上正确传输,出现了UTF-8,使用类似MBCS的方式对 Unicode进行编码。

注意UTF-8是编码,它属于Unicode字符集。

Unicode字符集有多种编码形式,而ASCII仅仅有一种,大多数 MBCS(包含GB-2312)也仅仅有一种。

  比如“连通”两个字的Unicode标准编码UTF-16 (big endian)为:DE 8F 1A 90
而其UTF-8编码为:E8 BF 9E E9 80 9A
  最后。当一个软件打开一个文本时,它要做的第一件事是决定这个文本到底是使用哪种字符集的哪种编码保存的。软件有三种途径来决定文本的字符集和编码:
  最标准的途径是检測文本最开头的几个字节,例如以下表:

开头字节 Charset/encoding
EF BB BF    UTF-8
FE FF     UTF-16/UCS-2, little endian
FF FE     UTF-16/UCS-2, big endian
FF FE 00 00  UTF-32/UCS-4, little endian.
00 00 FE FF  UTF-32/UCS-4, big-endian.

  比如插入标记后,连通”两个字的UTF-16 (big endian)和UTF-8码分别为:
FF FE DE 8F 1A 90
EF BB BF E8 BF 9E E9 80 9A

  可是MBCS文本没有这些位于开头的字符集标记,更不幸的是,一些早期的和一些设计不良的软件在保存Unicode文本时不插入这些位于开头的 字符集标记。

因此。软件不能依赖于这样的途径。

这时,软件能够採取一种比較安全的方式来决定字符集及其编码,那就是弹出一个对话框来请示用户。比如将那个 “连通”文件拖到MS Word中。Word就会弹出一个对话框。

  假设软件不想麻烦用户。或者它不方便向用户请示。那它仅仅能採取自己“猜”的方法,软件能够依据整个文本的特征来推測它可能属于哪个charset,这就非常可能不准了。使用记事本打开那个“连通”文件就属于这样的情况。

  我们能够证明这一点:在记事本中键入“连通”后,选择“Save As”,会看到最后一个下拉框中显示有“ANSI”。这时保存。当再当打开“连通”文件出现乱码后,再点击“File”->“Save As”,会看到最后一个下拉框中显示有“UTF-8”,这说明记事本觉得当前打开的这个文本是一个UTF-8编码的文本。

而我们刚才保存时是用ANSI字 符集保存的。这说明,记事本推測了“连通”文件的字符集。觉得它更像一个UTF-8编码文本。这是由于“连通”两个字的GB-2312编码看起来更像 UTF-8编码导致的。这是一个巧合,不是全部文字都这样。能够使用记事本的打开功能,在打开“连通”文件时在最后一个下拉框中选择ANSI,就能正常显 示了。反过来,假设之前保存时保存为UTF-8编码,则直接打开也不会出现故障。


  假设将“连通”文件放入MS Word中,Word也会觉得它是一个UTF-8编码的文件,但它不能确定,因此会弹出一个对话框询问用户,这时选择“中文简体(GB2312)”,就能正常打开了。

记事本在这一点上做得比較简化罢了。这与这个程序的定位是一致的。

原文地址:https://www.cnblogs.com/brucemengbm/p/6700000.html