Java乱码总结(二)

1.为什么要编码

首先计算机是只识别0或者1的(字节),但是我们平时使用的都是字符(英文,中文,其他...)。所以,这期间就涉及到了字符与字节之间的转化过程。而这个过程(字节->字符,字符->字节)就是编码!

2.常用的编码“工具”

ASCII编码:是目前计算机中用得最广泛的字符集及其编码。使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。标准ASCII 码也叫基础ASCII码,使用7 位二进制数来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。

ISO8859-1:可以表示的是西欧语言。看起来很单一为什么还在使用呢,由于是单字节编码,和计算机最基础的表示单位一致,所以很多时候,仍旧使用ISO-8859-1编码来表示。 而且在很多协议上,默认使用该编码。
GB2312:它是双字节编码,总的编码范围是A1~E7,其中A1~A9是符号区,总共包含682个符号;B0~F7是汉字区,包含6763个汉字。

GBK:GBK编码,是在GB2312-80标准基础上的内码扩展规范,使用了双字节编码方案,其编码范围从8140至FEFE(剔除xx7F),共23940个码位,共收录了21003个汉字,完全兼容GB2312-80标准,支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。

UTF-16:UTF-16是Unicode的其中一个使用方式。Unicode是java和xml的基础。UTF-16具体定义了Unicode的转化格式,它使用定长的表示方法,既不论什么字符都可以用两个字节来表示。两个字节就是16个bit,所以叫做UTF-16。UTF-16表示字符非常方便,每两个字节表示一个字符,这就大大简化了字符串的操作,这也是java以UTF-16作为内存的字符存储格式的很重要的原因。

UTF-8:UTF-16统一采用两个字节来表示一个字符,虽然在表示上非常简单!方便!但是也有缺点。有很大一部分字符用一个字节就可以表示的,但是现在用两个字节表示,存储放大一倍!网络带宽有多利用了一倍。所以就有了UTF-8,它是一种变长的技术,每个编码区域有不同的字码长度。不同的字符可以有1~6个字节来组成。

如果一个字节,最高位(第 8 位)为 0,表示这是一个 ASCII 字符(00 - 7F)。可见,所有 ASCII 编码已经是 UTF-8 了。
如果一个字节,以 11 开头,连续的 1 的个数暗示这个字符的字节数,例如:110xxxxx 代表它是双字节 UTF-8 字符的首字节。
如果一个字节,以 10 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节

3.java中编码

(1)IO流操作

(2)内存操作

(3)网络传输

4.编码测试

package com.test;
import java.io.UnsupportedEncodingException;
public class TestEncode {
 public static void main(String[] args) throws UnsupportedEncodingException {
  String name = "田躲躲";
  char c[] = name.toCharArray();
  byte[] bs2 = new byte[c.length];// 定义一个长度与需要转换的char数组相同的byte数组
  for (int i = 0; i < c.length; i++) {// 循环将char数组的每一个元素转换为byte并存在上面定义的byte数组中
   byte b = (byte) c[i];// 将每一个char转换成byte
   bs2[i] = b;// 保存到数组中
  }
  System.out.println("原始数据:");
  printHexString(bs2);
  System.out.println();
  
  System.out.println("ISO-8859-1:");
  byte[] iso8859 = name.getBytes("ISO-8859-1");    
  printHexString(iso8859); 
  System.out.println();
  
  System.out.println("GBK:");
  byte[] gbk = name.getBytes("GBK");    
  printHexString(gbk);   
  System.out.println();
  
  System.out.println("GB2312:");
  byte[] gbk2312 = name.getBytes("GB2312");    
  printHexString(gbk2312);   
  System.out.println();
  
  System.out.println("UTF-16:");
        byte[] utf16 = name.getBytes("UTF-16");    
        printHexString(utf16);   
        System.out.println();
        
        System.out.println("UTF-8:");
        byte[] utf8 = name.getBytes("UTF-8");    
        printHexString(utf8);    
  
 }
 public static String bytes2HexString(byte[] b) {
  String ret = "";
  for (int i = 0; i < b.length; i++) {
   String hex = Integer.toHexString(b[i] & 0xFF);
   if (hex.length() == 1) {
    hex = '0' + hex;
   }
   ret += hex.toUpperCase();
  }
  return ret;
 }
 public static String bytesToHexString(byte[] src) {
  StringBuilder stringBuilder = new StringBuilder("");  
     if (src == null || src.length <= 0) {  
         return null;  
     }  
     for (int i = 0; i < src.length; i++) {  
         int v = src[i] & 0xFF;  
         String hv = Integer.toHexString(v);  
         if (hv.length() < 2) {  
             stringBuilder.append(0);  
         }  
         stringBuilder.append(hv);  
     }  
     return stringBuilder.toString();  
 }
 private static byte charToByte(char c) {
  return (byte) "0123456789ABCDEF".indexOf(c);
 }
 public static void printHexString(byte[] b) {
  for (int i = 0; i < b.length; i++) {
   String hex = Integer.toHexString(b[i] & 0xFF);
   if (hex.length() == 1) {
    hex = '0' + hex;
   }
   
   System.out.print(hex.toUpperCase());
  }
 }
}

原始数据:
30B2B2
ISO-8859-1:
3F3F3F
GBK:
CCEFB6E3B6E3
GB2312:
CCEFB6E3B6E3
UTF-16:
FEFF75308EB28EB2
UTF-8:
E794B0E8BAB2E8BAB2

 通过比较可以知道:对中文字符后面四种编码格式都能处理,GB2312 与 GBK 编码规则类似,但是 GBK 范围更大,它能处理所有汉字字符,所以 GB2312 与 GBK 比较应该选择 GBK。UTF-16 与 UTF-8 都是处理 Unicode 编码,它们的编码规则不太相同,相对来说 UTF-16 编码效率最高,字符到字节相互转换更简单,进行字符串操作也更好。它适合在本地磁盘和内存之间使用,可以进行字符和字节之间快速切换,如 Java 的内存编码就是采用 UTF-16 编码。但是它不适合在网络之间传输,因为网络传输容易损坏字节流,一旦字节流损坏将很难恢复,想比较而言 UTF-8 更适合网络传输,对 ASCII 字符采用单字节存储,另外单个字符损坏也不会影响后面其它字符,在编码效率上介于 GBK 和 UTF-16 之间,所以 UTF-8 在编码效率上和编码安全性上做了平衡,是理想的中文编码方式。

 5.乱码原因

字符在保存时的编码格式如果和要显示的编码格式不一样的话,就会出现乱码问题。我们在做开发的时候,始终提倡开发环境编码一致。如页面编码,数据库编码等。
Java在运行期一律以Unicode来存储字符,这样有利的支持了多语言环境。我们在开发过程中经常会用到文件读取。Java读文件的时候会用到系统默认的编码来解码文件。所以在用FileInputStream类读取文件可以指定编码读取。
JSP页面乱码通常只要在页面开始地方用下面代码指定字符集编码即可。如果还不行,那么请用下面这句话来转换 str=new String(str.getBytes("ISO-8859-1"),"页面编码方式"); 
JAVA在网络传输中使用的编码是"ISO-8859-1",故在输出时需要进行转化,如:
String str=new String(str.getBytes("开发环境编码"),"ISO-8859-1");
经过网络编码后的中文,要正确显示在页面上必须要用类似于
Stirng str=new String(str.getBytes("ISO-8859-1"),"开发环境编码");这样的方式来解码

最后,为了避免不必要的java中文乱码,我们首先要从源头做起。
什么意思呢?这就是说,我们在项目开发中,首先要让我们开发环境统一编码。比如我用的开发环境是Eclipse+Tomcat5.0+SQLServer 2005。Eclipse的项目属性中可以设置项目的编码,可以针对JSP页面的设置统一的编码。

转载于:https://my.oschina.net/tdd/blog/345156

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值