字符编码与使用中的问题

作为中文环境下开发的Java程序员,UTF-8编码是我们经常使用的编码方式。

字符编码是怎么来的?为什么使用UTF-8编码?使用字符编码的时候回遇到什么坑?

这些问题你遇到过或者思考过吗。

字符编码表

在显示器上看见的文字、图片等信息在电脑里面其实并不是我们看见的样子,即使你知道所有信息都存储在硬盘里,把它拆开也看不见里面有任何东西,只有些盘片。

假设,你用显微镜把盘片放大,会看见盘片表面凹凸不平,凸起的地方被磁化,凹的地方是没有被磁化;凸起的地方代表数字1,凹的地方代表数字0。硬盘只能用0和1来表示所有文字、图片等信息。

因此,不同的字符在计算机有不同的表示,也就是所谓的编码表。

计算机中存储信息的最小单元是一个字节即8个bit,所以能表示的字符范围是 0~255 个,人类要表示的符号太多,无法用一个字节来完全表示,要解决这个矛盾必须需要一个新的数据结构 char,从 char 到 byte 必须编码。这就是编码出现的原因。

那么字母”A”在硬盘上是如何存储的呢?

可能小张计算机存储字母”A”是1100001,而小王存储字母”A”是11000010,这样双方交换信息时就会误解。比如小张把1100001发送给小王,小王并不认为1100001是字母”A”,可能认为这是字母”X”,于是小王在用记事本访问存储在硬盘上的1100001时,在屏幕上显示的就是字母”X”。也就是说,小张和小王使用了不同的编码表。

为了让大家能够互相理解,大家需要采用统一的编码表。

UTF-8编码

那么为什么使用UTF-8编码呢?

ASCII

在所有字符集中,最知名的可能要数被称为ASCII的7位字符集了。它是美国标准信息交换代码(American Standard Code for Information Interchange)的缩写, 为美国英语通信所设计。它由128个字符组成,包括大小写字母、数字0-9、标点符号、非打印字符(换行符、制表符等4个)以及控制字符(退格、响铃等)组成。但是,由于他是针对英语设计的,当处理带有音调标号(形如汉语的拼音)的亚洲文字时就会出现问题。

Unicode

Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。Unicode 只是一个符号集,它只规定了符号的二进制代码,没有规定这个二进制代码应该如何存储。

UTF-8

互联网的普及,强烈要求出现一种统一的编码方式。UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。

UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。


上面介绍了字符编码的一些知识,下面针对实际应用中遇到的两个问题的解决进行分析。


UTF-8 BOM

BOM是Byte order mark的缩写,一般作为标识出现在文本的首行。

因为在Windows平台下,默认不显示BOM,因此在读取文件、编译源码或者执行SQL的时候,很可能因为文件是UTF-8 BOM格式的,而产生错误。

例如简单的Test.java:

1
public class Test{}

如果保存为UTF-8 BOM格式,在编译的时候回出现如下错误:

1
2
3
4
5
# javac Test.java

javac: 找不到文件: Test.java
用法: javac <options> <source files>
-help 用于列出可能的选项

这就是因为文件是以Byte order mark开头导致的。

这种问题一般是怎么出现的呢?一般是由于在不同平台(例如Windows和Linux之间)、不同编辑器(例如邮件编辑、文本编辑)之间互相传文件造成的。

那么如何避免这个问题呢?答案就是通过编辑器仔细检查编码,或者用批量替换、过滤的方式,避免出现这种字符。

UTF-8 控制字符

控制字符(Control Character),出现于特定的信息文本中,表示某一控制功能的字符。

但是控制字符不能再XML中自由的转换,如果XML的内容出现了控制字符,会出现Unmarshalling Error

例如将一段用户输入的地址数据,通过Webservice接口传输的过程中,出现了如下异常,就是控制字符造成的:

1
2
3
4
5
webservice exception,
error message:
{}\norg.apache.cxf.binding.soap.SoapFault:
Unmarshalling Error:
Illegal character ((CTRL-CHAR, code 8))\n at [row,col {unknown-source}]: [201,32] \n\tat

简单的解决办法就是将控制字符进行转换:

  1. using guava:
1
return CharMatcher.JAVA_ISO_CONTROL.removeFrom(string);
  1. using regex:
1
string.replaceAll("\\p{Cntrl}", "");
  1. using apache commens
1
StringEscapeUtils.escapeXml10(string);

参考链接

  1. https://baike.baidu.com/item/%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81
  2. https://www.cnblogs.com/yuan1164345228/p/6937958.html
  3. http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
  4. https://baike.baidu.com/item/UTF-8
  5. https://en.wikipedia.org/wiki/Byte_order_mark
  6. https://www.cnblogs.com/zhangqunshi/p/6645130.html
  7. http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/StringEscapeUtils.html#escapeXml(java.io.Writer,%20java.lang.String)
  8. https://stackoverflow.com/questions/14028716/how-to-remove-control-characters-from-java-string
ijiangtao wechat
欢迎订阅我的微信公众号
打赏支持博主继续写出好作品
0%