NingG +

java的编码问题:byte、char、string

关注点

要解决几个问题:

乱码

java中,对String进行编码、解码的基本过程,见上图;简要解释一下:

编码的基本解释

几点:

java 中 String 的编解码

java 中 File 的编解码

java 读写 file,有两种方式:

何时出现乱码?如何解决?

依照前文分析的编码解码基本过程,产生乱码,本质是输入的char[]与最终输出的char[]不一致,有如下几种:

因此,解决乱码问题的思路也是清晰的:

思考:上数的讨论都是基于原始输入String为正常String,没有携带乱码,但有一种情况:原始String中自身就携带乱码,此时,如何处理? RE:特别是,针对ASCII中的控制字符(contorl code),例如:^M^C,此类字符,会产生转码后的换行问题,如何提前对此类字符进行处理?具体需要认真分析此类字符产生的原因,初步分析,两类场景:

疑问:ASCII的control code是如何输入到File中的?可以直接通过键盘输入吗?还有哪些场景?

UPDATE 2015-03-18

中文编码问题:

参考下文的示例代码即可。

字符串的编解码

几点:

测试代码如下:

package com.github.ningg;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class ByteAndString {

	public static void main(String[] args) throws UnsupportedEncodingException{
		
		String inputStr = "你好, Hello";
		String charset = "UTF-8";
		String outputStr = null;
		byte[] firstLayerByteArray = null;
		
		// default
		firstLayerByteArray = inputStr.getBytes();
		outputStr = new String(firstLayerByteArray);

		System.out.println("default:");
		System.out.println(Arrays.toString(firstLayerByteArray));
		System.out.println(outputStr);
		System.out.println("-------------");
		
		// UTF-8
		charset = "UTF-8";
		firstLayerByteArray = inputStr.getBytes(charset);
		outputStr = new String(firstLayerByteArray, charset);
		
		System.out.println(charset + ":");	
		System.out.println(Arrays.toString(firstLayerByteArray));
		System.out.println(outputStr);
		System.out.println("-------------");

		// GBK
		charset = "GBK";
		firstLayerByteArray = inputStr.getBytes(charset);
		outputStr = new String(firstLayerByteArray, charset);
		
		System.out.println(charset + ":");
		System.out.println(Arrays.toString(firstLayerByteArray));
		System.out.println(outputStr);
		System.out.println("-------------");

		// GB2312
		charset = "GB2312";
		firstLayerByteArray = inputStr.getBytes(charset);
		outputStr = new String(firstLayerByteArray, charset);
		
		System.out.println(charset + ":");
		System.out.println(Arrays.toString(firstLayerByteArray));
		System.out.println(outputStr);
		System.out.println("-------------");

		// ISO-8859-1
		charset = "ISO-8859-1";
		firstLayerByteArray = inputStr.getBytes(charset);
		outputStr = new String(firstLayerByteArray, charset);
		
		System.out.println(charset + ":");
		System.out.println(Arrays.toString(firstLayerByteArray));
		System.out.println(outputStr);
		System.out.println("-------------");
		
	}
}

输出内容,如下:

default:
[-28, -67, -96, -27, -91, -67, 44, 32, 72, 101, 108, 108, 111]
你好, Hello
-------------
UTF-8:
[-28, -67, -96, -27, -91, -67, 44, 32, 72, 101, 108, 108, 111]
你好, Hello
-------------
GBK:
[-60, -29, -70, -61, 44, 32, 72, 101, 108, 108, 111]
你好, Hello
-------------
GB2312:
[-60, -29, -70, -61, 44, 32, 72, 101, 108, 108, 111]
你好, Hello
-------------
ISO-8859-1:
[63, 63, 44, 32, 72, 101, 108, 108, 111]
??, Hello
-------------

备注:之前写过,一个java中数组的博文,可以参考一下;

文件内容的编解码

下面示例代码,简要说明,以某一指定charsetIn读取文件,再以指定charsetOut写入文件即可;完整示例代码如下:

package com.github.ningg;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class FileAndCharset {

	public static void main(String[] args) throws IOException {
		
		String srcFile = "E:/1.log";
		String destFile = "E:/1utf8.log";
		
		String charsetIn = "GBK";
		String charsetOut = "UTF-8";
		
		FileInputStream fileInputStream = new FileInputStream(srcFile);
		FileOutputStream fileOutputStream = new FileOutputStream(destFile);
		
		InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, charsetIn);
		OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, charsetOut);
		
		BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
		BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
		
		String singleLine = null;
		
		while( (singleLine = bufferedReader.readLine()) != null ){
			bufferedWriter.write(singleLine);
			bufferedWriter.newLine();
		}
		
		bufferedWriter.flush();
		bufferedWriter.close();
		bufferedReader.close();
		
	}
}

备注:之前写过一篇Java读写File的博客,可以参考一下。

编码方式

思考:几个小疑问:

几种编码方式之间的联系:

ASCII

ASCII:American Standard Code for Information Interchange(信息交换,美国标准码);简单说几点:

US-ASCII:原始的ASCII

但是,计算机中一个byte有8bit,因此,就打起了剩余1-bit的主意:

一个byte,8 bit,并不能满足所有character encodings的需要,因此出现了multi-byte character encodings,整体上两类:

GB2312

整体上说一下GB2312、GBK、GB18030几种编码之间的关系:

备注GBGuojia Biaozhun(国标);

几点:

疑问EUC,官网提到,其是ISO-2022标准的实现,多字节编码,最大可包含G0`G1\G2\G3,4个字节;其中G0通常兼容ISO-646标准(US-ASCII`),但GB2312为 2 byte的定长字符集,这可如何是好?

RE

相对于UTF-8GB2312效率更高,几点:

思考

建议:推荐使用大字符集,GBK、GB18030;

GBK

几点:

GB18030

几点:

更多阅读:

Unicode

几点:

UTF-8

几点:

具体:

网络上数据编码的演进趋势:(网络上传输的byte,是char通过哪种字符集映射过来的?)

附录

Huffman coding

Huffman coding,霍夫曼编码:

更多阅读,参考Huffman coding

字母体系

世界三大字母体系:

拉丁字母(下图):

备注:中国的汉语拼音方案,也是以拉丁字母为基础的。

斯拉夫字母(下图):

阿拉伯字母(下图):

utf_unicode_ci和utf8_general_ci

在数据库系统MySQL中有多种字符集,其中utf8_unicode_ci和utf8_general_ci是最常用的,但是utf8_general_ci对某些语言的支持有一些小问题,如果可以接受,那最好使用utf8_general_ci,因为它速度快。否则,请使用较为精确的utf8_unicode_ci,不过速度会慢一些。

多语言无差错,首选“utf_unicode_ci”。

全角 & 半角

信息交换用汉字编码字符集·基本集对应的是GB2312,其中也指定了拉丁字母(英文字母)对应的 2 byte编码,这个只有在全角情况下,才输入GB2312字符集中的拉丁字母,而半角时,输入的为ASCII下的拉丁字母编码;

备注:上述查询文档中,字符对应的二进制表示时,借助工具UltraEdit中的十六进制模式

关于全角`半角`,几点:

参考来源

推荐:此次编码相关的中文wiki内容;

Top