工作中经常遇到
java
编码问题,由于缺乏研究,总是无法给出确切的答案,这个周末在网上查了一些资料,在此做些汇总。
问题一:在
java
中读取文件时应该采用什么编码?
Java
读取文件的方式总体可以分为两类:按字节读取和按字符读取。按字节读取就是采用
InputStream.read()
方法来读取字节,然后保存到一个
byte[]
数组中,最后经常用
new String(byte[]);
把字节数组转换成
String
。在最后一步隐藏了一个编码的细节,
new String(byte[]);
会使用操作系统默认的字符集来解码字节数组,中文操作系统就是
GBK
。而我们从输入流里读取的字节很可能就不是
GBK
编码的,因为从输入流里读取的字节编码取决于被读取的文件自身的编码。举个例子:我们在
D:
盘新建一个名为
demo.txt
的文件,写入
”
我们。
”
,并保存。此时
demo.txt
编码是
ANSI
,中文操作系统下就是
GBK
。此时我们用输入字节流读取该文件所得到的字节就是使用
GBK
方式编码的字节。那么我们最终
new String(byte[]);
时采用平台默认的
GBK
来编码成
String
也是没有问题的
(
字节编码和默认解码一致
)
。试想一下,如果在保存
demo.txt
文件时,我们选择
UTF-8
编码,那么该文件的编码就不在是
ANSI
了,而变成了
UTF-8
。仍然采用输入字节流来读取,那么此时读取的字节和上一次就不一样了,这次的字节是
UTF-8
编码的字节。两次的字节显然不一样,一个很明显的区别就是:
GBK
每个汉字两个字节,而
UTF-8
每个汉字三个字节。如何我们最后还使用
new String(byte[]);
来构造
String
对象,则会出现乱码,原因很简单,因为构造时采用的默认解码
GBK
,而我们的字节是
UTF-8
字节。正确的办法就是使用
new String(byte[],”UTF-8”);
来构造
String
对象。此时我们的字节编码和构造使用的解码是一致的,不会出现乱码问题了。
说完字节输入流,再来说说字节输出流。
我们知道如果采用字节输出流把字节输出到某个文件,我们是无法指定生成文件的编码的
(
假设文件以前不存在
)
,那么生成的文件是什么编码的呢?经过测试发现,其实这取决于写入的字节编码格式。比如以下代码:
OutputStream out = new FileOutputStream("d:\\demo.txt");
out.write("
我们
".getBytes());
getBytes()
会采用操作系统默认的字符集来编码字节,这里就是
GBK
,所以我们写入
demo.txt
文件的是
GBK
编码的字节。那么这个文件的编码就是
GBK
。如果稍微修改一下程序:
out.write("
我们
".getBytes(“UTF-8”));
此时我们写入的字节就是
UTF-8
的,那么
demo.txt
文件编码就是
UTF-8
。这里还有一点,如果把
”
我们
”
换成
123
或
abc
之类的
ascii
码字符,那么无论是采用
getBytes()
或者
getBytes(“UTF-8”)
那么生成的文件都将是
GBK
编码的。
这里可以总结一下,
InputStream
中的字节编码取决文件本身的编码,而
OutputStream
生成文件的编码取决于字节的编码。
下面说说采用字符输入流来读取文件。
首先,我们需要理解一下字符流。其实字符流可以看做是一种包装流,它的底层还是采用字节流来读取字节,然后它使用指定的编码方式将读取字节解码为字符。说起字符流,不得不提的就是
InputStreamReader
。以下是
java api
对它的说明:
InputStreamReader
是字节流通向字符流的桥梁:它使用指定的
charset
读取字节并将其
解码为字符
。它使用的字符集可以由名称指定或显式给定,否则可能接受平台默认的字符集。说到这里其实很明白了,
InputStreamReader
在底层还是采用字节流来读取字节,读取字节后它需要一个编码格式来解码读取的字节,如果我们在构造
InputStreamReader
没有传入编码方式,那么会采用操作系统默认的
GBK
来解码读取的字节。还用上面
demo.txt
的例子,假设
demo.txt
编码方式为
GBK
,我们使用如下代码来读取文件:
InputStreamReader in = new InputStreamReader(new FileInputStream(“demo.txt”));
那么我们读取不会产生乱码,因为文件采用
GBK
编码,所以读出的字节也是
GBK
编码的,而
InputStreamReader
默认采用解码也是
GBK
。如果把
demo.txt
编码方式换成
UTF-8,
那么我们采用这种方式读取就会产生乱码。这是因为字节编码
(UTF-8)
和我们的解码编码
(GBK)
造成的。解决办法如下:
InputStreamReader in = new InputStreamReader(new FileInputStream(“demo.txt”),”UTF-8”);
给
InputStreamReader
指定解码编码,这样二者统一就不会出现乱码了。
下面说说字符输出流。
字符输出流的原理和字符输入流的原理一样,也可以看做是包装流,其底层还是采用字节输出流来写文件。只是字符输出流根据指定的编码将字符转换为字节的。字符输出流的主要类是:
OutputStreamWriter
。
Java api
解释如下:
OutputStreamWriter
是字符流通向字节流的桥梁:使用指定的
charset
将要向其写入的字符编码为字节。它使用的字符集可以由名称指定或显式给定,否则可能接受平台默认的字符集。说的很明白了,它需要一个编码将写入的字符转换为字节,如果没有指定则采用
GBK
编码,那么输出的字节都将是
GBK
编码,生成的文件也是
GBK
编码的。如果采用以下方式构造
OutputStreamWriter
:
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(“dd.txt”),”UTF-8”);
那么写入的字符将被编码为
UTF-8
的字节
,
生成的文件也将是
UTF-8
格式的。
问题二:
既然读文件要使用和文件编码一致的编码,那么
javac
编译文件也需要读取文件,它使用什么编码呢?
这个问题从来就没想过,也从没当做是什么问题。正是因为问题一而引发的思考,其实这里还是有东西可以挖掘的。下面分三种情况来探讨,这三种情况也是我们常用的编译
java
源文件的方法。
1.javac
在控制台编译
java
类文件。
通常我们手动建立一个
java
文件
Demo.java
,并保存。此时
Demo.java
文件的编码为
ANSI,
中文操作系统下就是
GBK.
然后使用
javac
命令来编译该源文件。
”javac Demo.java”
。
Javac
也需要读取
java
文件,那么
javac
是使用什么编码来解码我们读取的字节呢?其实
javac
采用了操作系统默认的
GBK
编码解码我们读取的字节,这个编码正好也是
Demo.java
文件的编码,二者一致,所以不会出现乱码情况。让我们来做点手脚,在保存
Demo.java
文件时,我们选择
UTF-8
保存。此时
Demo.java
文件编码就是
UTF-8
了。我们再使用
”javac Demo.java”
来编译,如果
Demo.java
里含有中文字符,此时控制台会出现警告信息,也出现了乱码。究其原因,就是因为
javac
采用了
GBK
编码解码我们读取的字节。因为我们的字节是
UTF-8
编码的,所以会出现乱码。如果不信的话你可以自己试试。那么解决办法呢?解决办法就是使用
javac
的
encoding
参数来制定我们的解码编码。如下:
javac -encoding UTF-8 Demo.java
。
这里我们指定了使用
UTF-8
来解码读取的字节,由于这个编码和
Demo.java
文件编码一致,所以不会出现乱码情况了。
2.Eclipse
中编译
java
文件。
我习惯把
Eclipse
的编码设置成
UTF-8
。那么每个项目中的
java
源文件的编码就是
UTF-8
。这样编译也从没有问题,也没有出现过乱码。正是因为这样才掩盖了使用
javac
可能出现的乱码。那么
Eclipse
是如何正确编译文件编码为
UTF-8
的
java
源文件的呢?唯一的解释就是
Eclipse
自动识别了我们
java
源文件的文件编码,然后采取了正确的
encoding
参数来编译我们的
java
源文件。功劳都归功于
IDE
的强大了。
3.
使用
Ant
来编译
java
文件。
Ant
也是我常用的编译
java
文件的工具。首先,必须知道
Ant
在后台其实也是采用
javac
来编译
java
源文件的,那么可想而知,
1
会出现的问题在
Ant
中也会存在。如果我们使用
Ant
来编译
UTF-8
编码的
java
源文件,并且不指定如何编码,那么也会出现乱码的情况。所以
Ant
的编译命令
<javac>
有一个属性
” encoding”
允许我们指定编码,如果我们要编译源文件编码为
UTF-8
的
java
文件,那么我们的命令应该如下:
<javac destdir="${classes}" target="1.4" source="1.4"
deprecation="off" debug="on" debuglevel="lines,vars,source"
optimize="off"
encoding="UTF-8"
>
指定了编码也就相当于
”javac –encoding”
了,所以不会出现乱码了。
问题三:
tomcat
中编译
jsp
的情况。
这个话题也是由问题二引出的。既然
javac
编译
java
源文件需要采用正确的编码,那么
tomcat
编译
jsp
时也要读取文件,此时
tomcat
采用什么编码来读取文件?会出现乱码情况吗?下面我们来分析。
我们通常会在
jsp
开头写上如下代码:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
我常常不写
pageEncoding
这个属于,也不明白它的作用,但是不写也没出现过乱码情况。其实这个属性就是告诉
tomcat
采用什么编码来读取
jsp
文件的。它应该和
jsp
文件本身的编码一致。比如我们新建个
jsp
文件,设置文件编码为
GBK,
那么此时我们的
pageEncoding
应该设置为
GBK,
这样我们写入文件的字符就是
GBK
编码的,
tomcat
读取文件时采用也是
GBK
编码,所以能保证正确的解码读取的字节。不会出现乱码。如果把
pageEncoding
设置为
UTF-8
,那么读取
jsp
文件过程中转码就出现了乱码。上面说我常常不写
pageEncoding
这个属性,但是也没出现过乱码,这是怎么回事呢?那是因为如果没有
pageEncoding
属性,
tomcat
会采用
contentType
中
charset
编码来读取
jsp
文件,我的
jsp
文件编码通常设置为
UTF-8,contentType
的
charset
也设置为
UTF-8,
这样
tomcat
使用
UTF-8
编码来解码读取的
jsp
文件,二者编码一致也不会出现乱码。这只是
contentType
中
charset
的一个作用,它还有两个作用,后面再说。可能有人会问:如果我既不设置
pageEncoding
属性,也不设置
contentType
的
charset
属性,那么
tomcat
会采取什么编码来解码读取的
jsp
文件呢?答案是
iso-8859-1
,这是
tomcat
读取文件采用的默认编码,如果用这种编码来读取文件显然会出现乱码。
问题四:输出。
问题二和问题三分析的过程其实就是从源文件
à
class
文件过程中的转码情况。最终的
class
文件都是以
unicode
编码的,我们前面所做的工作就是把各种不同的编码转换为
unicode
编码,比如从
GBK
转换为
unicode,
从
UTF-8
转换为
unicode
。因为只有采用正确的编码来转码才能保证不出现乱码。
Jvm
在运行时其内部都是采用
unicode
编码的,其实在输出时,又会做一次编码的转换。让我们分两种情况来讨论。
1.java
中采用
Sysout.out.println
输出。
比如:
Sysout.out.println(“
我们
”)
。经过正确的解码后
”
我们
”
是
unicode
保存在内存中的,但是在向标准输出
(
控制台
)
输出时,
jvm
又做了一次转码,它会采用操作系统默认编码
(
中文操作系统是
GBK)
,将内存中的
unicode
编码转换为
GBK
编码,然后输出到控制台。因为我们操作系统是中文系统,所以往终端显示设备上打印字符时使用的也是
GBK
编码。因为终端的编码无法手动改变,所以这个过程对我们来说是透明的,只要编译时能正确转码,最终的输出都将是正确的,不会出现乱码。在
Eclipse
中可以设置控制台的字符编码,具体位置在
Run Configuration
对话框的
Common
标签里
,
我们可以试着设置为
UTF-8,
此时的输出就是乱码了。因为输出时是采用
GBK
编码的,而显示却是使用
UTF-8
,编码不同,所以出现乱码。
2.jsp
中使用
out.println()
输出到客户端浏览器。
Jsp
编译成
class
后,如果输出到客户端,也有个转码的过程。
Java
会采用操作系统默认的编码来转码,那么
tomcat
采用什么编码来转码呢?其实
tomcat
是根据
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
中
contentType
的
charset
参数来转码的,
contentType
用来设置
tomcat
往浏览器发送
HTML
内容所使用的编码。
Tomcat
根据这个编码来转码内存中的
unicode
。经过转码后
tomcat
输出到客户端的字符编码就是
utf-8
了。那么浏览器怎么知道采取什么编码格式来显示接收到的内容呢?这就是
contentType
的
charset
属性的第三个作用了:这个编码会在
HTTP
响应头中指定以通知浏览器。浏览器使用
http
响应头的
contentType
的
charset
属性来显示接收到的内容。
总结一下
contentType charset
的三个作用:
1).
在没有
pageEncoding
属性时,
tomcat
使用它来解码读取的
jsp
文件。
2).tomcat
向客户端输出时,使用它来编码发送的内容。
3).
通知浏览器,应该以什么编码来显示接收到的内容。
为了能更好的理解上面所说的解码和转码过程,我们举一个例子。
新建一个
index.jsp
文件,该文件编码为
GBK,
在
jsp
开头我们写上如下代码:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="GBK"%>
这里的
charset
和
pageEncoding
不同,但是也不会出现乱码,我来解释一下。首先
tomcat
读取
jsp
内容,并根据
pageEncoding
指定的
GBK
编码将读取的
GBK
字节解码并转换为
unicode
字节码保存在
class
文件中。然后
tomcat
在输出时
(out.println())
使用
charset
属性将内存中的
unicode
转换为
utf-8
编码,并在响应头中通知浏览器,浏览器以
utf-8
显示接收到的内容。整个过程没有一次转码错误,所以就不会出现乱码情况。
问题五:
Properties
和
ResourceBundle
使用的解码编码。
以上两个是我们常用的类,他们在读取文件过程中并不允许我们指定解码编码,那么它们采取什么解码方式呢?查看源码后发现都是采用
iso-8859-1
编码来解码
的。这样的话我们也不难理解我们写的
properties
文件为什么都是
iso-8859-1
的了。因为采取任何一个别的编码都将产生乱码。因为
iso-8859-1
编码是没
有中文的,所以我们输入的中文要转换为
unicode
,通常我们使用插件来完成,也可以使用
jdk
自带的
native2ascii
工具。
相关推荐
然后说java架构师,那就是在java领域内解决这个问题的人员,至于做什么取决于这个项目的难点和复杂点在哪里,架构师就是通过架构去解决这个痛点。 java架构师应具备什么技能? 一、常见模式与工具 学习Java技术体系...
Java 就是用来做项目的!Java 的主要应用领域就是企业级的项目开发!要想从事企业级的项目开发,你必须...Java 的开发总要经过立项——设计——编码——测试等诸多过程,下面先来介绍一下Java 开发中的这些开发经验。
学好java,一定要掌握java字符串的操作方式,这样才能更好的了解java的编码方式。
"Java程序员笔试面试题汇总及答案.pdf知识点总结" 以下是从给定的文件中生成的相关知识点: 一、访问控制符的作用域与区别 * private 成员:缺省的成员,只能在同一类中访问 * protected 成员:可以在同一类中和...
│ J2EE综合--Struts常见错误的全面汇总.txt │ java程序员面试资料.zip │ JAVA笔试题(上海释锐).pdf │ MIME简介.txt │ SCJP试题详解.pdf │ SQL面试题_心灵深处.htm │ Struts+Hibernate+Spring轻量级J2EE...
│ J2EE综合--Struts常见错误的全面汇总.txt │ java程序员面试资料.zip │ JAVA笔试题(上海释锐).pdf │ MIME简介.txt │ SCJP试题详解.pdf │ SQL面试题_心灵深处.htm │ Struts+Hibernate+Spring轻量级J2EE...
Java程序设计实战案例教程教学课件汇总完整版电子讲义 本讲义总结了Java程序设计的实战案例教程,涵盖了Java平台安装、JDK的安装、Eclipse的安装与使用、第一个Java程序的编写等内容。 一、Java概述 Java是由SUN...
Java编程老鸟潜心写作,奉献高效率的Java学习心得 完全站在没有编程经验读者的角度,手把手教会读者学习Java 配16小时多媒体教学视频,高效、直观 一一击破Java入门可能会遇到的难点和疑惑 抽丝剥茧,层层推进,让...
│ J2EE综合--Struts常见错误的全面汇总.txt │ java程序员面试资料.zip │ JAVA笔试题(上海释锐).pdf │ MIME简介.txt │ SCJP试题详解.pdf │ SQL面试题_心灵深处.htm │ Struts+Hibernate+Spring轻量级J2EE...
以国内开源Web MVC框架EasyJWeb作系统引擎的Java Web应用系统,主要实现的功能有订单录入、打印、销售汇总、原料管理、客户管理、生产配料计算、报表打印、汇总、系统数据管理及维护等功能,是一个使用非常简单的...
该系统是一个使用Java语言开发,系统主要实现的功能有订单录入、打印,销售汇总、原料管理、客户管理、生产配料计算、报表打印、汇总、系统数据管理及维护等功能,是一个使用非常简单的编码方式实现的Web开源应用系统...
系统主要实现的功能有订单录入、打印,销售汇总、原料管理、客户管理、生产配料计算、报表打印、汇总、系统数据管理及维护等功能,是一个使用非常简单的编码方式实现的Web开源应用系统。 系统采用面向对象的设计...
JAVA 程序员笔试面试题汇总及答案 本资源摘要信息涵盖了 JAVA 程序员笔试面试题汇总及答案,涵盖了基础题、中等题、提高题等多种题型,涉及到访问控制符、ArrayList 和 Vector 的区别、HashMap 和 Hashtable 的区别...
系统主要实现的功能有订单录入、打印,销售汇总、原料管理、客户管理、生产配料计算、报表打印、汇总、系统数据管理及维护等功能,是一个使用非常简单的编码方式实现的Web开源应用系统。 系统采用面向对象的设计...
其实, Java 的文件存储/编译机制, JVM 的工作机制, 在实现中内部都采用了统一的 unicode 编码方式, 若目标平台解码方式不一致, 便会出现中文乱码(实际上不仅是中文, 多/单字符集都有可能出问题, 视目标平台而不同).
Java编程老鸟潜心写作,奉献高效率的Java学习心得 完全站在没有编程经验读者的角度,手把手教会读者学习Java 配16小时多媒体教学视频,高效、直观 一一击破Java入门可能会遇到的难点和疑惑 抽丝剥茧,层层推进,让...
系统主要实现的功能有订单录入、打印,销售汇总、原料管理、客户管理、生产配料计算、报表打印、汇总、系统数据管理及维护等功能,是一个使用非常简单的编码方式实现的Web开源应用系统。 系统采用面向对象的设计...
错误: 编码GBK的不可映射字符 重要说明:关于数据类型的默认值问题 数据类型划分——浮点形 传统bug问题 数据类型划分——字符型 数据类型划分——布尔型 数据类型划分——字符串型 9、运算符 自增、自减操作 三目...
系统主要实现的功能有订单录入、打印,销售汇总、原料管理、客户管理、生产配料计算、报表打印、汇总、系统数据管理及维护等功能,是一个使用非常简单的编码方式实现的Web开源应用系统。 系统采用面向对象的设计...