为什么要编码?
计算机存储信息的最小单位是1byte,即8bit,所能表示的字符个数为255个,但是人类要表示的符号太多,远远不止255个,西欧字符、中文等等符号。
解决这个问题,就必须要有一个新的数据结构,在java中就是char,从char到byte必须编码(可以理解为翻译)
常见编码格式(字典)
- ASCⅡ
由于计算机最早是由美国发明的,根据他们的语言习惯,用1个字节可以表示128个字符,可以通过键盘输入并且能够显示出来 - ISO-8859-1
应用最广泛的编码格式,占用2个字节可以表示256个字符,涵盖大部分的西欧语言字符。 - 中文编码
- GB2312
包含682个字符和6763个汉字 - GBK
总共有23940个码位,能表示21003个汉字,兼容GB2312 - GB18030
国家标准,兼容GB2312,但是应用不广泛
- GB2312
- Unicode
统一编码,是计算机科学领域里的一项业界标准,包括字符集、编码方案。 - UTF-16
用两个字节,将Unicode字符转换为字节存储。Java以UTF-16作为内存的字符存储格式 - UTF-8
变长,1-6个字节
Java中的编码场景
磁盘I/O 和 网络I/O
磁盘I/O
- 字符转字节:OutputStreamWriter作为桥梁,传入字符集charset,委托StreamDecoder去做具体的字符转字节的工作
- 字节转字符:InputStreamReader 作为桥梁,传入字符集charset,委托StreamDecoder去做具体的字节转字符的工作
//字符转字节
@Test
public void charToByteByDiskIo() {
String path = CodeProgram.class.getResource("").getPath();
File file = new File(path, "字符转字节流,通过字符写入磁盘文件.txt");
FileOutputStream fos = null;
OutputStreamWriter osw = null;
try {
fos = new FileOutputStream(file);
osw = new OutputStreamWriter(fos, "utf-8");
osw.write("我爱中国,我爱香港!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(null != osw) {
osw.close();
}
if(null != fos) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//字节转字符:打印当前java类的内容到控制台(如果eclipse中的workspace文件编码未改为utf-8或者测试方法所属的java类的文件编码没有改为utf-8,则会乱码)
@Test
public void byteToCharByDiskIo() {
File directory = new File("./src");
File file = new File(directory, "CodeProgram.java");
FileInputStream fis = null;
InputStreamReader isr = null;
try {
fis = new FileInputStream(file);
isr = new InputStreamReader(fis, "utf-8");
StringBuffer sb = new StringBuffer();
char[] cbuf = new char[1024];
int length = 0;
while((length = isr.read(cbuf)) != -1) {
sb.append(cbuf, 0, length);
}
System.out.println(sb.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(null != isr) {
isr.close();
}
if(null != fis) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
网络I/O
- URL的编解码
- HTTP Header的编解码
- POST表单的编解码
- HTTP Body的编解码
- 外部引入JS文件
- JS的URL编码及服务端解码
- 其他需要编码的地方
以Tomcat服务器为例讲解
- URL的编解码
Get请求,URL的pathinfo(路径)和Query String(参数)的编码字符集不同,浏览器将URL中非ASCⅡ码字符按某种字符集转换为16进制到字符加上%。
tomcat设置URI解码字符集为UTF-8:<Connector URIEncoding="UTF-8" />
tomcat设置Query String解码字符集为UTF-8:<Connector URIEncoding="UTF-8" useBodyEncodingForURI="true" /> - HTTP Header的编解码
默认为ISO-8859-1,且不提供编码设置到方法,只能用URLEncoder编码,服务端request.getHeader时,再相应到用URLEncoder解码
- POST表单的编解码
手动指定编码格式, request.setCharacterEncoding(charset),post表单提交到数据就是以指定到编码格式编码,再调用request.getParameter(),会自动用设定到编码去解码。
一定要在调用request.getParameter()方法前调用request.setCharacterEncoding(charset),否则tomcat在解析前会检测HTTP Header中到contentType,一般请求时,这个值是null,根据tomcat源码,为空时,按默认到到字符集ISO-8859-1来编码,导致乱码 - HTTP Body的编解码
请求资源成功获取后,这些内容将通过response返回给客户端浏览器。通过response.setCharacterEncoding(charset)设置编解码字符集,通过response Header的contentType返回给客户端,浏览器根据contentType解码,如果不存在,则根据html <meta/>中到charset来解码,如果都不存在,则用默认到ISO-8859-1解码
- 外部引入JS文件
如果在一个单独到js脚本中包含中文输出,需指定字符集<script src="" charset="gbk" />
如果被一个页面引入到js脚本,则与外部页面到编码方式一致,若js文件本身到编码格式与外部页面编码格式不一致,则会乱码 - JS的URL编码及服务端解码
- encodeURI()
- encodeURIComponent()
除了特殊字符加英文字母不编码加%外,其余到都编码;后者到特殊字符范围缩小,特别是&符号不包含在内,&符号也要编码。所以后者常用来对一个URL传递一个参数值为URL的URL进行编码。后台对应到解码JAVA类为URLDecoder。前端JS两次编码,后端request.getParameter()自动解一次码,URLDecoder.decoder手动解一次码。两次编解码避免了前后端第一次编解码不一致的问题。
- 其他需要编码的地方
- 数据库连接JDBC URL传递characterEncoding=gbk,与数据库内置编码格式要一直
- XML
- JSP
网友评论