美文网首页程序员
Java中的字符编码

Java中的字符编码

作者: 齐云先生 | 来源:发表于2016-10-16 15:13 被阅读0次

字符与编码

人类使用的是字符,计算机存储的是比特,要想在计算机中存储字符就必须进行编码。从字符到比特序列的过程为编码,比特序列到字符的过程为解码。

字符集与编码格式

字符集是字符的集合。可以认为是一个数字到字符的映射表,比如1对应‘大’,2对应‘家’,3对应‘好’,那么“大家好”编码后就为1 2 3,1 2 3解码后就是“大家好”,只要根据这张表就能完成编解码。
编码格式是字符集的具体实现,如上面的字符集,1、2、3具体怎么存储在计算机中呢,用一个字节吗?如果此字符集有5000个字符,怎么存储呢?按照简单可以直接每个字符表示为两个字节,还可以利用压缩的原理,将使用频率最高的128个字符编号为0-127,然后存储在一个字节中,其他的用2个字节,这样可以提高存储效率。这两种具体实现就是同一种字符集的两种编码格式。
字符集与编码格式是一对多关系,可以认为是抽象和实现的关系。

常见编码格式

ASCII

美国信息交换标准代码,用于显示现代英语。也是大家最熟悉的编码格式,编码范围0x00-0x7F,每个字符存储1个字节。


ascii表.png

ISO-8859-1

它以ASCII为基础,在空置的0xA0-0xFF的范围内,加入96个字母及符号,以适应西欧国家的使用,在西欧非常流行。

GBK

中文常用编码格式,在ascii码的基础上加入2w多个汉字,每个汉字占两个字节。

上述这些即是字符集也是编码格式,Unicode字符集拥有多种编码格式。

Unicode

每种语言都有自己的字符集,这让互联网上的交流充满了乱码,为了使世界上大部分字符统一字符集,于是Unicode就诞生了。Unicode包含了10w多个字符,达到了字符集统一的目的。Unicode拥有多种编码格式。

UTF-8

Unicode的一种编码格式,主要针对存储、传输设计,每个字符用1-4个字节表示,节省存储空间,广泛应用与网络通信。

UTF-16

Unicode的另一种编码格式,每个字符用两个字节表示,后来加入了一些新字符后有一些字符需要用多个字符表示。

Unicode的常用编码格式的比较
unicode不同编码格式.jpg

编码的兼容性与不可逆性

字符在编码和解码时必须使用相同的编码格式才能还原,否则可能出现乱码。编码后的是字节流,要想解码后得到原字符,则必须保证解码格式按此字节流能恢复成原字符,也就是编码格式要能兼容编码格式。
如果编码后的字节流在解码的编码格式中此字节流有效,就会出现乱码但可逆,无效且更改了字节流则会出现乱码并且不可逆。
上面的常见编码格式中,除了UTF-16不兼容ASCII,其他的都兼容。汉字编码格式。

Java中的编码

java中各阶段编码格式要求:


java编码概括.jpg

可以看出Java源文件不限编码格式,Class字节码文件采用UTF-8编码格式,在虚拟机内存中采用UTF-16编码,输出的时候按照需要获得各种编码格式。Class文件采用UTF-8编码是因为UTF-8存储效率更高,适合存储与传输,运行时采用UTF-16是因为以前UTF-16还是定长编码,读取效率更高,现在这个优势已经消失了。

JVM外乱码

这类乱码出现在jvm运行时之前,具体来说是编译阶段中的输入阶段。
从Java源码文件到Java Class文件,中间会经过Java源码编译器(例如javac或ECJ)的编译。
也就是说,是Java源码编译器负责将Java源码文件的编码转换为最终的UTF-8。
导致乱码的不是Java源码编译器的“编码”(写出UTF-8)的过程,而是“解码”(读入Java源码内容)的过程。
这类乱码只需要将源码格式统一为UTF-8一般就能避免。

JVM运行时乱码

在java程序运行时对字符的编码解码所用的编码格式不兼容所导致的乱码现象,这也是主要的乱码原因。

Java 中String的编码转换

首先需要说明的是,Java中的String都是UTF-16编码的。
看String的两个方法:

public byte[] getBytes()
Encodes this String into a sequence of bytes using the platform's default charset, storing the result into a new byte array.
 
new String(byte[], charsetName)
Constructs a new String by decoding the specified array of bytes using the specified charset. The length of the new String is a function of the charset, and hence may not be equal to the length of the byte array.

getBytes()就是编码过程,将字符按照编码格式转换成字节流,String(byte[], charsetName)就是将byte字节流按照charsetName指定的编码格式转换成字符。注意charsetName是指byte[]字节流的编码格式,而不是把byte[]转换成charsetName编码

默认编码格式

getBytes()不传字符集参数时是按操作系统的默认编码格式获取字节流。
默认编码格式:

System.out.println(Charset.defaultCharset());

可能是UTF-8、GBK等,取决于操作系统,并且是jvm已启动就确定的,不可更改。

编码转换

字符串编码转换函数:

/**
    * 将一个字符串由一种编码格式转换到另一种编码格式
    * @param oldStr 待转换的字符串
    * @param oldCharset oldStr对应的编码格式,null表示用默认编码格式
    * @param newCharset 新的编码格式,null表示用默认编码格式
    * @return 新的字符串
    * @throws UnsupportedEncodingException
    */
String changeCharset(String oldStr,String oldCharset,String newCharset) throws UnsupportedEncodingException {
    if(oldCharset==null)
        oldCharset=Charset.defaultCharset().name();
    if(newCharset==null)
        newCharset=Charset.defaultCharset().name();
    return new String(oldStr.getBytes(oldCharset), newCharset);
}

乱码示例:

    @Test
    public void defaultChartTest() throws UnsupportedEncodingException {
        System.out.println("默认字符集:"+Charset.defaultCharset().name());
        String s="你好,大家好";
        String sIso=changeCharset(s,null,"ISO-8859-1");
        System.out.println(sIso);

        String sDef=changeCharset(sIso,"ISO-8859-1",null);
        System.out.println(sDef);
    }

输出:

默认字符集:UTF-8
ä½ å¥½,大家好
你好,大家好

本人机器的默认字符集是UTF-8,首先取得字符串的UTF-8编码的字节流,然后按照ISO-8859-1进行解码,两者不兼容所以得到的是乱码,要想将此乱码恢复,必须按照ISO-8859-1进行解码还原字节流(可能还原不成功,就是出现信息丢失,不可逆),因为此字节流的有意义的编码是UTF-8,所以按照UTF-8解码就能得到原字符串。
总的来说,注意编码格式和解码格式保持一致,就能避免乱码问题,重点在字符集、编码格式和乱码产生的原因。

参考资料

1.java编译器编码和JVM编码问题
2.深入分析 Java 中的中文编码问题
3.Java编码浅析(注意区分三个概念)
4.Java字符编码根本原理

相关文章

  • Java字符串编码原理以及乱码解决方法

    1 Java字符串编码原理   在Java中,字符的数据类型是char,而char类型的编码是 Unicode 编...

  • 编码

    参考:字符编码常识及问题解析深入分析 Java 中的中文编码问题 关于字符编码,你所需要知道的 编码是什么? 编码...

  • java 中的IO(字符流)

    以字符的方式读取 java中的 中文 在不同的编码中 占据不同的字节,java中提供了 字符流的方式读取中文 字符...

  • Java中的字符编码

    字符与编码 人类使用的是字符,计算机存储的是比特,要想在计算机中存储字符就必须进行编码。从字符到比特序列的过程为编...

  • JAVA基本数据类型

    java基本数据类型 类型表 Unicode码点:即编码表中字符代表的编码值,比如字符A为U+0041。字符?为U...

  • 字符、编码和Java中的编码

    字符是用户可以读写的最小单位。计算机所能支持的字符组成的集合,就叫做字符集。字符集通常以二维表的形式存在。二维表的...

  • JNI编程四:处理java跟JNI字符串传递乱码问题

    java中的中文字符使用的编码方式是根据系统默认编码方式,一般使用的是GB2312的编码方式,jni中的中文字符采...

  • JavaWeb开发之编码格式

    编码格式 Java语言在内存当中默认使用的字符集 默认会用“Unicode”编码格式(字符集)来保存字符。 编码 ...

  • 技术文章系列汇总(csdn转载)

    Java基础 Java干货整理 Java代码是怎么运行的 Unicode字符编码—就这么回事 面试题:在Java中...

  • Java程序员遇上字符乱码9-你理解“java中字符都是unic

    我们经常从书本上看到“java中字符都是unicode编码”。可是我们从没有看到过“java中字符都是UTF-8编...

网友评论

    本文标题:Java中的字符编码

    本文链接:https://www.haomeiwen.com/subject/xhloyttx.html