Java---IO---编码和解码

常见的编码表

ASCII:美国标准信息交换码。
      用一个字节的7位可以表示。
ISO8859-1:拉丁码表。欧洲码表
           用一个字节的8位表示。
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
Unicode:国际标准码,融合了多种文字。
           所有文字都用两个字节来表示,Java语言使用的就是unicode
UTF-8:一种变长的unicode码的实现方式,由1~4个字节表示。

字符编码

编码:字符串-->字节数组
解码:字节数组-->字符串

编码编错必挂,解码解错可以补救!

Unicode和UTF-8的关系

 Unicode

    世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。

    可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode,就像它的名字都表示的,这是一种所有符号的编码。

    Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

UTF-8

    UTF-8是在互联网上使用最广的一种unicode的实现方式。其他实现方式还包括UTF-16和UTF-32,不过在互联网上基本不用。UTF-8是Unicode的实现方式之一。

UTF-8的编码格式

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

UTF-8的编码规则很简单,只有二条:

    1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
    2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。


代码演示:

import java.io.UnsupportedEncodingException;

import org.junit.Test;

/**
 * 2018年5月6日 上午8:11:54
 * 
 * @author <a href="mailto:[email protected]">宋进宇</a> 
 * 	演示编码和解码
 */
public class EncodeAndDecode {
	///////本次演示是在  UTF-8  环境下的///////
	// 演示编码--把字符串编译成字节码
	@Test
	public void encode() throws UnsupportedEncodingException {
		String str = "中国";
		print( str.getBytes() ); // 采用默认码表
		print( str.getBytes( "GBK" ) ); //采用指定码表进行编码,会有异常,为了逻辑看起来清晰直接抛
		//观察结果 可以发现 同一个字符串 通过不同码表 进行编码,最后的 码值 是不一样的
		//////////演示编码出错能否补救//////////
		byte[] bytes = str.getBytes( "ISO8859-1" );//西欧码表又称   拉丁1 
		print( bytes );
		/* 输出结果: 63 63 
		 * 可以发现 不同的 汉字 通过 拉丁1 码表 进行编码 得出的字节码 是两个相同的 码值
		 * 即使把得出的码值又通过 拉丁1 码表 进行解码,也是解析不出来的
		 */
		System.out.println( new String( bytes, "ISO8859-1" ) );
		//输出结果:??
		//综上:  编码     出错是   无法补救   的!!!
	}

	/**
	 * 打印字节数组
	 * 
	 * @param bytes
	 *            字节数组
	 */
	private void print(byte[] bytes) {
		for (byte b : bytes) {
			System.out.print( b + " " );
		}
		System.out.println();
	}

	// 演示解码--把解析字节码,把字节数组转换成字符串
	@Test
	public void decode() throws UnsupportedEncodingException {
		// 通过上面演示编码的打印结果,进行解码
		byte[] b = { -28, -72, -83, -27, -101, -67 }; //"中国"在UTF-8码表中对应的字节码
		System.out.println( new String( b ) ); // 采用默认码表
		System.out.println( new String( b, "GBK" ) ); //采用指定码表进行解码,会有异常,为了逻辑看起来清晰直接抛
		/*
		 * 输出结果:
		 * 中国
		 * 涓浗
		 * 
		 * 观察发现出现乱码了
		 */
		///////////演示解码出错能否补救//////////
		String str = "涓浗"; 
		byte[] bytes = str.getBytes( "GBK" );
		System.out.println( new String( bytes ) );
		/* 观察输出结果发现,采用指定码表去解码时,
		 * 当 解码 的 码表 与 编码 的 码表 不同时,
		 * 虽然 解析 结果错误,但是可以通过 错误的结果以解码时的码表,进行从新编码,
		 * 生成 字节码 ,然后 通过 生成的字节码 以最原先 编码的码表 进行 解码,
		 * 在输出结果就可以知道 这样是还原的,可以补救。
		 */
	}
}

字符串截取

    在java中,字符串“abcd”与字符串“ab你好”的长度是一样,都是四个字符。
    但对应的字节数不同,一个汉字占两个字节。
    定义一个方法,按照指定的字节数来取子串。
    如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个,那么半个就要舍弃。如果取四个字节就是“ab你”,取五个字节还是“ab你”。

import java.io.UnsupportedEncodingException;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import org.junit.Test;

/**
 * 2018年5月6日 上午8:55:51
 * @author <a href="mailto:[email protected]">宋进宇</a>
 *	练习:字符串截取
		在java中,字符串“abcd”与字符串“ab你好”的长度是一样,都是四个字符。
		但对应的字节数不同,一个汉字占两个字节。
		定义一个方法,按照指定的字节数来取子串。
		如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个,
		那么半个就要舍弃。如果取四个字节就是“ab你”,取五个字节还是“ab你”。
 */
public class CutString {
	//先测试一下 在不同 码表 下汉字对应的 字节码
	//包含 汉字 的 常用码表 有: "GBK" 和  "UTF-8" 两种
	@Test
	public void testCode() throws UnsupportedEncodingException{
		String str = "ab你好";
		print( str.getBytes( "GBK" ) ); //一个汉字两字节,且码值全为负
		print( str.getBytes( "UTF-8" ) ); //一个汉字三个字节,且码值全为负
		//测试非常陌生的 汉字 
		print( "鯡".getBytes( "GBK" ) ); //一个汉字两字节,第一字节为负,第二个为正
		print( "鯡".getBytes( "UTF-8" ) ); //一个汉字三个字节,且码值全为负
		/* 综上:GBK码表中一个汉字两字节,第一个字节为负,第二个字节不一定
		 * 	   UTF-8码表中一个汉字三个字节,且码值全为负
		 */
		
	}
	/**
	 * 打印字节数组
	 * @param bytes 字节数组
	 */
	private void print(byte[] bytes) {
		for (byte b : bytes) {
			System.out.print( b + " " );
		}
		System.out.println();
	}
	
	@Test
	public void testGBK() throws UnsupportedEncodingException{
		String str = "ab你好鯡a汉字12";
		for (int i = 0; i <= str.getBytes( "GBK" ).length; i++) {
			System.out.println( cutStringInGBK( str, i ) );
		}
	}
	private static String cutStringInGBK(String str,int len){
		//如果被切割的字符串为null 就返回 null
		if ( str == null ) {
			return null;
		}
		//先把字符串编译成字节码
		try {
			byte[] bytes = str.getBytes( "GBK" );
			/* 同观察 GBK 码表 的字节码 规律可以得出 :
			 * 可以从字节数组的len-1开始找,并且统计字节值负个数,
			 * 找到第一个字节值为非负数时就停止,如果统计的个数为奇数 说明 要舍弃最后一个字节,否则就不用舍弃
			 */
			//如果 切割的长度小于0 则返回"";
			if ( len < 0 ) {
				return "";
			}
			//如果 切割的长度大于 字节数组 则返回原字符串
			if ( len > bytes.length ) {
				return str;
			}
			int count = 0;
			for (int i = len-1; i >= 0; i--) {
				if (bytes[i]<0) {
					count++;
				} else {
					break;
				}
			}
			return new String( bytes, 0, len-(count%2), "GBK" );
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException( "该字符串不支持通过GBK码表进行编码", e );
		}
	}
	
	private static String cutStringInUTF8(String str,int len){
		//过程跟 GBK码表下 差不多
		//如果被切割的字符串为null 就返回 null
		if ( str == null ) {
			return null;
		}
		//先把字符串编译成字节码
		try {
			byte[] bytes = str.getBytes( "UTF-8" );
			//如果 切割的长度小于0 则返回"";
			if ( len < 0 ) {
				return "";
			}
			//如果 切割的长度大于 字节数组 则返回原字符串
			if ( len > bytes.length ) {
				return str;
			}
			int count = 0;
			for (int i = len-1; i >= 0; i--) {
				if (bytes[i]<0) {
					count++;
				} else {
					break;
				}
			}
			return new String( bytes, 0, len-(count%3), "UTF-8" );
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException( "该字符串不支持通过UTF-8码表进行编码", e );
		}
	}
	//查看 系统 配置信息
	@Test
	public void testSystemPropertys(){
		Properties properties = System.getProperties();
		Set<Entry<Object, Object>> entrySet = properties.entrySet();
		for (Entry<Object, Object> entry : entrySet) {
			System.out.println( entry );
		}
	}
	/**
	 * 字符串截取 ,如果不是完整的汉字就舍弃
	 * @param str 被截取的字符串
	 * @param len 截取的长度
	 * @return 截取后的字符串
	 */
	public static String cutString(String str,int len){
		//防护 空指针异常
		if ( str == null ) {
			return null;
		}
		
		if ( System.getProperty( "file.encoding" ).equalsIgnoreCase( "GBK" ) ) {
			 return cutStringInGBK( str, len );
		} else if ( System.getProperty( "file.encoding" ).equalsIgnoreCase( "UTF-8" ) ) {
			return cutStringInUTF8( str, len );
		}
		return "";
	}
	
	public static void main(String[] args) {
		String str = "ab你好鯡asd中文1223汉字sadsa";
		for (int i = 0; i < str.getBytes().length; i++) {
			System.out.println( "len=" + i + ":" + cutString( str, i ) );
		}
	}
}



猜你喜欢

转载自blog.csdn.net/qq_34928644/article/details/80212052