查看此文章需要对字符集编码有一定的认识: 任意门: 字符集编码基础 https://www.cnblogs.com/TvvT-kevin/p/10322273.html
一, 字符串的内部表示?
重点: 字符串在 java(指在 JVM 中, 在内存中)中统一用 unicode 表示( 即 utf-16 LE) , 下面解释:
对于 String s = "你好哦!";
如果源码文件 (java 文件) 是 GBK 编码, 操作系统 (Windows) 默认的环境编码为 GBK, 那么编译时, JVM 将 按照 GBK 编码将字节数组解析成字符(系统文件本质就是二进制流), 然后将字符转换为 unicode 格式的字节数组, 作为内部存储.
当打印这个字符串时, JVM 根据操作系统本地的语言环境, 将 unicode 转换为 GBK, 然后操作系统将 GBK 格式的内容显示出来.
当源码文件是 UTF-8, 我们需要通知编译器源码的格式, javac -encoding utf-8 ... , 编译时, JVM 按照 utf-8 解析成字符, 然后转换为 unicode 格式的字节数组, 所以不论源码文件是什么格式, 同样的字符串, 最后得到的 unicode 字节数组是完全一致的, 显示的时候, 也是转成 GBK 来显示(跟 OS 环境有关: OS 表示操作系统)
二, 乱码如何产生? 本质上都是由于 字符串原本的编码格式 与 读取时解析用的编码格式不一致导致的.
例如:
- String s = "你好哦!";
- System.out.println(new String(s.getBytes(),"UTF-8")); // 错误, 因为 getBytes()默认使用 GBK 编码, 而解析时使用 UTF-8 编码, 肯定出错.
其中 getBytes() 是将 unicode 转换为操作系统默认的格式的字节数组, 即 "你好哦" 的 GBK 格式,
new String (bytes, Charset) 中的 charset 是指定读取 bytes 的方式, 这里指定为 UTF-8, 即把 bytes 的内容当做 UTF-8 格式对待.
如下两种方式都会有正确的结果, 因为他们的源内容编码和解析用的编码是一致的.
- System.out.println( new String(s.getBytes(),"GBK"));
- System.out.println( new String(s.getBytes("UTF-8"),"UTF-8"));
三, 如何利用 getBytes 和 new String() 来进行编码转换呢?
网上流传着一种错误的方法:
GBK--> UTF-8: new String( s.getBytes("GBK") , "UTF-8); , 这种方式是完全错误的, 因为 getBytes 的编码与 UTF-8 不一致, 肯定是乱码.
但是为什么在 tomcat 下, 使用 new String(s.getBytes("iso-8859-1") ,"GBK") 却可以用呢? 答案是:
tomcat 默认使用 iso-8859-1 编码, 也就是说, 如果原本字符串是 GBK 的, tomcat 传输过程中, 将 GBK 转成 iso-8859-1 了,
默认情况下, 使用 iso-8859-1 读取中文肯定是有问题的, 那么我们需要将 iso-8859-1 再转成 GBK, 而 iso-8859-1 是单字节编码的,
即他认为一个字节是一个字符, 那么这种转换不会对原来的字节数组做任何改变, 因为字节数组本来就是由单个字节组成的,
如果之前用 GBK 编码, 那么转成 iso-8859-1 后编码内容完全没变, 则 s.getBytes("iso-8859-1") 实际上还是原来 GBK 的编码内容
则 new String(s.getBytes("iso-8859-1") ,"GBK") 就可以正确解码了. 所以说这是一种巧合.
四, 如何正确的将 GBK 转 UTF-8 ? (实际上是 unicode 转 UTF-8)
- String gbkStr = "你好哦!"; // 源码文件是 GBK 格式, 或者这个字符串是从 GBK 文件中读取出来的, JVM 会把字符转化成 unicode 格式
- // 利用 getBytes 将 unicode 字符串转成 UTF-8 格式的字节数组
- byte[] utf8Bytes = gbkStr.getBytes("UTF-8");
- // 然后用 utf-8 对这个字节数组解码成新的字符串
- String utf8Str = new String(utf8Bytes, "UTF-8");
简化后就是:
- unicodeToUtf8 (String s) {
- return new String( s.getBytes("utf-8") , "utf-8");
- }
UTF-8 转 GBK 原理也是一样
return new String( s.getBytes("GBK") , "GBK");
其实核心工作都由 getBytes(charset) 做了.
getBytes 的 JDK 描述: Encodes this String into a sequence of bytes using the named charset, storing the result into a new byte array.
另外对于读写文件,
- OutputStreamWriter w1 = new OutputStreamWriter(new FileOutputStream("D:\\file1.txt"),"UTF-8");
- InputStreamReader( stream, charset)
可以帮助我们轻松的按照指定编码读写文件.
来源: http://www.bubuko.com/infodetail-2934073.html