1 几种常见的编码格式
为什么要编码
在计算机中存储信息的最小单元是1个字节(8bit),所以能表示的字符范围是0-255个。人类要表达的字符太多,无法用1个字节完全表示。要解决这个问题需要使用新的数据结构char,从char到byte必须编码。
编码格式
ASCII码:共128个,用一个字节的低7位表示,0-31控制字符,32-126打印字符。
ISO-8859-1:拓展自ASCII码,覆盖大多数西欧语言字符,单字节编码,共能表示256个字节。
GB2312:双字节编码,包含6763个汉字。
GBK:拓展自GB2312,和GB2312兼容,能表示21003个汉字。
GB18030:可能单字节、双字节或四字节,应用不广泛。
UTF-16:具体定义了Unicode
[统一码]字符在计算机中的存取方法。定长,使用两个字节表示任何字符。Java以UTF-16作为内存的字符存储格式。
UTF-8:采用变长技术,每个编码区域有不同的字码长度。中文一般占三个字节。
Java中的编码操作
I/O操作:
上一篇文章介绍I/O字符和字节转化的时候有提到两个类:StreamDecoder
和StreamEncoder
,InputStreamReader在I/O过程中处理读取字节到字符的转换,其委托StreamDecoder实现字节到字符的节码实现,解码过程中必须由用户指定Charset编码格式,默认使用本地环境中的默认字符集(中文环境即为GBK)。
OutputStreamWriter负责转换字符到字节,编码格式和默认编码规则与解码一致。
示例如下:
@Test
public void test(){
String file = "C:/stream.txt";
String charset = "utf-8";
try {
FileOutputStream outputStream = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(outputStream, charset);
try {
writer.write("这是要保存的中文字符");
} catch (IOException e) {
e.printStackTrace();
} finally {
writer.close();
}
//读取字节转换成字符
FileInputStream inputStream = new FileInputStream(file);
InputStreamReader reader = new InputStreamReader(inputStream);
StringBuffer buffer = new StringBuffer();
char[] chars = new char[64];
int count = 0;
try {
while((count = reader.read(chars)) != -1){
buffer.append(chars,0,count);
}
System.out.println(buffer.toString());
}finally {
reader.close();
}
} catch (IOException e) {
System.out.println("文件不存在");
}
}
内存操作
String s = "这是一段中文字符串";
byte[] b = s.getBytes("uft-8");
String n = new String(b,"uft-8");
另外Charset提供了encode和decode方法,代码如下:
String string = "中文字符串";
Charset charset = Charset.forName("UTF-8");
ByteBuffer byteBuffer = charset.encode(string);
CharBuffer charBuffer = charset.decode(byteBuffer);
在Java中如何编解码
String → ByteBuffer:Charset.encode()
ByteBuffer → String:Charset.decode().toString()
CharBuffer→ String :toString()
ByteBuffer → byte[]:array()
byte[] → ByteBuffer :ByteBuffer.wrap()
CharBuffer → char[]:array()
char[] → CharBuffer:CharBuffer.wrap()
Java Web中涉及的编解码
URL编解码
浏览器编码URL是将非ASCII字符按照某种编码格式编码成16进制数字后将每个16进制表示的字符前加上%。
URL的URI部分进行解码的字符集是在<Connector URIEncodeing="UTF-8">
里设置的。
QueryString的解析过程:GET方式HTTP请求的querystring和POST方式HTTP请求的表单参数都是作为parameters保存的,都通过request.getParameter();
queryString的解码字符集是在哪定义的呢?
是在HTTP的header的contentType或者是默认的ISO-8859-1,如果要用HTTP的header中中定义的编码需要设置<Connector URIEncoding="UTF-8" useBodyEncodingForURI='true'>
HTTP Hander的编解码
客户端发起的HTTP请求除了URL外,还可能会在Header中传递其他参数(如Cookie)。对Header中的项进行解码默认使用ISO-8859-1,且不能设置Header其他的解码格式,若设置的Header中有非ASCII字符,解码中肯定会出现乱码。若一定要传递,则调用Tomcat中的URLEncoder编码,再添加到Header中。
POST表单的编解码
POST表单的参数传递方式是通过HTTP的BODY传递到服务器的,当提交时先根据ContentType中的字符集进行解码,字符集编码可以由request.setCharacterEncoding(charset)来设置。
此外务必注意:对POST表单提交参数的解码是发生在getParameter时,所以在第一次调用request.getParameter方法之前就要先设置request.setCharacterEncoding(charset)方法。
关于上传的文件编码:也是使用ContentType定义的字符集编码,不过上传文件是以字节流的方式传输到服务器的本地临时目录,此过程尚不涉及字符编码,只有当文件内容添加到parameters时才进行编码。
HTTP BODY的编解码
编解码字符集通过response.setCharacterEncoding来设置,通过Header的Content-Type返回客户端。若Header中没有Content-Type,浏览器会根据中的charset来解码,若依然没有该属性,浏览器则使用默认编码。
常见问题分析
中文变成了看不懂的字符
解码和编码用的字符集不一样,就会出现乱码。
一个汉字变成一个问号
ISO-8859-1遇到不认识的用3F表示,是问号。
一个汉字变成两个问号
说明中文经过了多次编码