工作中经常遇到
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开发乱码问题解决方法,希望能够为读者提供帮助。 1. ...
### Java常见问题集锦 #### 1. Java2 (JDK 1.2) 的环境配置 - **问题描述**:如何在不同操作系统上正确配置Java2(即JDK 1.2)的环境变量? - **解决方案**: - **Solaris平台**: - 设置`JAVA_HOME`为Java2的...
在Java开发过程中,遇到的问题多种多样,从环境配置到代码编写,每个环节都有可能出现问题。以下是一些关于Java开发的常见问题及其解决方法。 首先,确保Java开发环境的正确安装和配置。在Windows系统中,可以通过...
Java路径问题在编程中是一个常见的挑战,特别是在处理文件读写、资源加载等操作时。Java的路径处理涉及多个类和方法,如`File`, `URL`, `URI`等,它们在处理路径时有不同的规则和行为。 1. **URL与空格问题**: 当...
Java的路径处理涉及到URL、URI、File等核心类的交互,而这些类在处理包含空格、特殊字符和编码的问题时会有特定的行为。 首先,Java的URL类在表示路径时会自动对空格、特殊字符进行编码。例如,空格会被转换为"%20...
本资料"Java项目经验汇总(简历项目素材)"提供了丰富的实例和指导,帮助Java开发者构建出引人注目的简历项目部分。 首先,理解Java项目经验的重要性。Java是一种广泛应用于企业级应用开发的编程语言,其稳定性和...
【JAVA面试题总汇】 1. **final, finally, finalize的区别** - `final` 关键字用于声明不可变的变量、无法重写的类或方法。对于变量,一旦赋值后不可更改;对于类,表示该类不能被继承;对于方法,表示该方法不能...
Java面试资料汇总的知识点可以从多个方面来总结: 面向对象的编程特性包括封装、继承、多态和抽象,它们是...以上是Java面试资料汇总中提及的部分知识点总结,更多详细内容需要结合具体问题和实际场景进行深入探讨。
Java是一种广泛使用的面向对象的编程语言...这些知识点涵盖了Java的基础语法、面向对象特性、内存管理、并发编程和异常处理等方面,是Java面试中常见的问题。理解并掌握这些概念对于成为一名优秀的Java开发者至关重要。
Java英文单词汇总 Java 英文单词汇总是 Java 语言中广泛使用的一些英文单词的总结,这些单词涵盖了 Java 中的大部分方法的名字。了解这些单词的含义可以帮助开发者更好地理解 Java 语言,并提高编程效率。 ...
### JAVA开发工具大汇总 #### 1. JDK (Java Development Kit) **简介**: JDK是由Sun Microsystems提供的免费Java开发工具包,随着Oracle收购Sun后继续维护和发展。它为开发者提供了丰富的语言特性和运行环境,同时...
- **面向过程**:强调的是执行流程,通过一系列步骤解决问题。适用于需要精确控制执行流程的应用场景,如嵌入式开发。 - **面向对象**:关注对象及其行为,利用封装、继承和多态等特性构建灵活且可扩展的系统结构。...
二进制数据转十进制数据采用的是8421编码方式,而十进制转二进制则通过不断除以2并获取余数来完成。此外,讲义中还介绍了计算机存储单位以及常用的DOS命令,这些命令是管理和操作文件系统时必不可少的工具。 关于...
【标题】:“北京java面试题汇总” 这是一份专门针对北京地区Java开发岗位面试的题库,涵盖了众多Java公司的常见面试问题。对于准备应聘Java工程师的求职者来说,这是一个宝贵的资源,可以帮助他们全面了解和复习...
8. **unicode** 和 **ASCII**:字符编码标准,Unicode包含更多字符,ASCII是其子集。 9. **true** 和 **false**:布尔值,在条件判断和逻辑运算中使用。 10. **variable** 和 **constant**:变量是可以改变的值,...
《Java规约和汇总》这份资料涵盖了这些方面的内容,让我们深入探讨其中的关键知识点。 首先,日志编码的最佳实践是确保其高效且可读。例如,使用`log.isLoggable()`进行条件判断,避免不必要的计算。在给定的例子中...
本文将对2014年最新Java面试题总汇中的内容进行详细知识点解析。 首先,Java面向对象的核心概念,包括super()与this()的区别,作用域的public、protected、private,以及不写时的区别。super()用于调用父类的构造...
**问题描述**:在项目开发中,由于Java默认使用UTF-8编码,但在实际操作中,如使用Struts框架时,往往需要选择UTF-8作为默认编码来避免乱码问题。此外,在使用Eclipse等IDE进行编辑时,应确保default text editor...