FileReader采用的默认编码
很久以前听教学视频,里面讲到Java采用的默认编码是ISO-8859-1,一直记着。
但是最近重新看IO流的时候,惊讶地发现,在不指定字符编码的情况下,FileReader居然可以读取内容为中文的文本文件。要知道ISO-8859-1可是西欧字符集,怎么能包含中文呢?于是百度了一下关键词“IOS-8859-1显示中文”,结果很多人都有这个疑惑。
代码如下:
-
package day170903;
-
-
import java.io.*;
-
-
public class TestDecoder {
-
public static void main(String[] args) {
-
FileReader fr = null;
-
try {
-
fr = new FileReader("G:/io/hello.txt");
-
int len = 0;
-
while((len=fr.read())!=-1) {
-
System.out.println((char)len);
-
}
-
} catch (FileNotFoundException e) {
-
e.printStackTrace();
-
} catch (IOException e) {
-
e.printStackTrace();
-
} finally {
-
try {
-
if(fr!=null) {
-
fr.close();
-
}
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
}
事情的真相是什么呢?
编码一般是在构造方法处指定的,于是查看一下FileReader的构造方法。也是奇葩,以前没怎么注意过,FileReader竟然没有可以指定字符编码的构造方法。而且仅仅是简单地从InputStreamReader继承,并没有重写或扩展任何方法。这可能是历史上最吝啬的子类,完全就是啃老族。
不过好在Java的文档注释写得很给力,在FileReader这个类的开头有下面一段文档注释(中文部分为我劣质的翻译):
/**
* Convenience class for reading character files. The constructors of this
* class assume that the default character encoding and the default byte-buffer
* size are appropriate. To specify these values yourself, construct an
* InputStreamReader on a FileInputStream.
*
*这是一个很方便的读取字符文件(文本文件)的类。
*这个类的构造方法假设默认的字符编码和默认的缓存数组大小是合适的(满足需要的)。
*假如你想自己指定字符编码和缓存数组的大小,
*请使用基于FileInputStream的InputStreamReader类。
* <p><code>FileReader</code> is meant for reading streams of characters.
* For reading streams of raw bytes, consider using a
* <code>FileInputStream</code>.
*
*FileReader是设计为用来读取字符流的。
*想要读取原始的字节流的话,可以考虑使用FileInputStream
* @see InputStreamReader
* @see FileInputStream
*
* @author Mark Reinhold
* @since JDK1.1
*/
所以,设计者已经在文档注释中讲明白了这么设计的原因。但是对于我们来说,现在比较重要的是这个所谓的默认的字符编码是什么。
这个时候我们来看一下我们使用的FileReader中的那个构造方法的具体内容。
-
public FileReader(String fileName) throws FileNotFoundException {
-
super(new FileInputStream(fileName));
-
}
FileReader继承自InputStreamReader,调用了InputStreamReader的接受InputStream类型的形参的构造方法,也就是下面这个。
-
public InputStreamReader(InputStream in) {
-
super(in);
-
try {
-
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
-
} catch (UnsupportedEncodingException e) {
-
// The default encoding should always be available
-
throw new Error(e);
-
}
-
}
当然InputStreamReader的这个构造方法又调用了其父类Reader的下面的构造方法。
-
protected Reader(Object lock) {
-
if (lock == null) {
-
throw new NullPointerException();
-
}
-
this.lock = lock;
-
}
在这里,它只是把得到的InputStream对象赋值给成员变量lock(看lock这个成员变量的文档注释的话,大概知道它是用来保证同步的),并没有说到字符编码的事。
既然通过super(in)向上查找到父类Reader的构造方法也没有发现默认字符编码的踪迹,那么这条道就到头了。接下来应该看的是super(in)下面的代码,也就是那个异常捕捉语句块。主体语句只有下面一行内容。
sd = StreamDecoder.forInputStreamReader(in, this, (String)null);
仔细看FileReader和其它IO流的代码的话会发现,很多输入流的读取功能(read及其重载方法)都是通过这个StreamDecoder完成的,这是后话。在Eclipse里面直接查看这个
StreamDecoder的源码是不行的,需要去openjdk上找。
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/nio/cs/StreamDecoder.java
上面异常捕捉语句块主体部分调用的是StreamDecoder的forInputStreamReader方法,对应的代码如下:
-
public static StreamDecoder forInputStreamReader(InputStream in,
-
Object lock,
-
String charsetName)
-
throws UnsupportedEncodingException
-
{
-
String csn = charsetName;
-
if (csn == null)
-
csn = Charset.defaultCharset().name();
-
try {
-
if (Charset.isSupported(csn))
-
return new StreamDecoder(in, lock, Charset.forName(csn));
-
} catch (IllegalCharsetNameException x) { }
-
throw new UnsupportedEncodingException (csn);
-
}
其实调用的时候,传递的第三个参数是字符串形式的null,这个其实就是我们要找的默认字符编码。
我们要找的是默认字符编码,其它代码不必深究。第一行是说把接收到的第三个参数赋值给csn(局部变量:字符编码),当然了,这个是被InputStreamReader的带字符编码参数的构造方法调用的时候才有意义的。没有指定字符编码的构造方法调用StreamDecoder的forInputStreamReader的时候传递是null。所以接下来的if语句判断就成立了,那么csn这个变量得到的就是Charset.defaultCharset().name(),见名知意,即默认字符编码。
接下来就要看Charset这个类的defaultCharset方法的返回值——Charset对象的name()方法的返回值是什么了。说起来有点绕,其实就是找里面的默认字符编码。
-
public static Charset defaultCharset() {
-
if (defaultCharset == null) {
-
synchronized (Charset.class) {
-
String csn = AccessController.doPrivileged(
-
new GetPropertyAction("file.encoding"));
-
Charset cs = lookup(csn);
-
if (cs != null)
-
defaultCharset = cs;
-
else
-
defaultCharset = forName("UTF-8");
-
}
-
}
-
return defaultCharset;
-
}
这代码看起来很费劲,而且接着又要看其它代码。最终结果是这个所谓的默认字符编码,其实就是JVM启动时候的本地编码。
这个要查看的话,就在对应的项目上点击右键,选择Properties选项,在弹出的属性窗口中,可以看到当前项目在JVM中运行时候的默认字符编码。对于咱们中国人来说,一般都是“GBK”,不过可以根据需要从下拉框选择。
所以开头那个疑问,完全是因为不知道默认的编码其实是GBK而产生的误解。反过来测试一下就好了,先用OutputStreamWriter往文件中写入下面一句法语
Est-ce possible que tu sois en train de penser à moi lorsque tu me manques?
我在想你的时候,你会不会也刚好正在想我?
写入的时候指定字符编码为ISO-8859-1,然后用InputStreamReader读取,读取的时候不指定字符编码(即采用默认字符编码)。那么,假如不能正确还原这句话,就说明默认的字符编码并不是ISO-8859-1。
-
package day170903;
-
-
import java.io.*;
-
-
public class TestDefaultCharEncoding {
-
public static void main(String[] args) {
-
InputStreamReader isr = null;
-
OutputStreamWriter osw = null;
-
try {
-
osw = new OutputStreamWriter(new FileOutputStream("G:/io/ISO-8859-1.txt"),"ISO-8859-1");
-
isr = new InputStreamReader(new FileInputStream("G:/io/ISO-8859-1.txt"));
-
char[] chars = "Est-ce possible que tu sois en train de penser à moi lorsque tu me manques?".toCharArray();
-
osw.write(chars);
-
osw.flush();
-
int len = 0;
-
while((len=isr.read())!=-1) {
-
System.out.print((char)len);
-
}
-
} catch (UnsupportedEncodingException | FileNotFoundException e) {
-
e.printStackTrace();
-
} catch (IOException e) {
-
e.printStackTrace();
-
} finally {
-
try {
-
if(isr!=null) {
-
isr.close();
-
}
-
if(osw!=null) {
-
osw.close();
-
}
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
}
输出结果是:
Est-ce possible que tu sois en train de penser ? moi lorsque tu me manques?
大部分都正确还原了,因为法语中大部分也是英文字母。但是那个法语特有的(相比于英语)à 读出来以后无法识别,变成了问号。
假如默认编码真的是ISO-8859-1,那么读取是完全没有问题的。现在有问题,正好说明默认编码不是ISO-8859-1。
基本上到这儿就完事了,但是还要说一句。虽然我们可以很方便地知道在不指定字符编码的情况下,JVM将会采用什么编码,但是还是建议采用字符类的时候加上字符编码,因为写清楚字符编码可以让别人明白你的原意,而且能避免代码转手后换了一个开发工具后可能出现的编码异常问题。
-----------------------------------------------------------------------------------end
可以因为无能为力而选择视而不见,但是不要因为心知肚明而忘了开口。
FileReader采用的默认编码的更多相关文章
- hibernate自动建表采用UTF-8字符编码
hibernate自动建表采用UTF-8字符编码 hibernate建表默认为UTF-8编码 >>>>>>>>>>>>>& ...
- eclipse 设置 默认编码为 utf-8
学习javaweb时,开发工具都采用utf-8的编码方式,给eclipse设置默认编码为utf-8的编码方法 菜单 Window -> preference -> General -> ...
- MyEclipse中把JSP默认编码改为UTF-8
在MyEclispe中创建Jsp页面,Jsp页面的默认编码是“ISO-8859-1”,如下图所示: 在这种编码下编写中文是没有办法保存Jsp页面的,会出现如下的错误提示: 因此可以设置Jsp默认的编码 ...
- [Linux] 修改系统默认编码
locale 命令 locale 命令用以设置程序运行的语言环境. locale 设置语言环境的命名规则为 Language_area.charset,例如 en_US.utf8 表示语言为英语,地区 ...
- MySql默认编码所造成的乱码麻烦1.222
1.前言 MySQL在安装时,最后的一步,会让你选择MySQL服务器及客户端.数据库.连接接口的默认编码.通常可选择 UTF8和GB2312. 但是,如果你选择了utf8的时候,恰好你要从另一个数据库 ...
- python3.x设置默认编码(sys.stdout.encoding和sys.defaultencoding)
查了一会资料得出的结论是如果你用的是python3.x,那么就最好别去设置sys.defaultencoding或者sys.stdout.encoding记住在需要编码的时候用encode,解码的时候 ...
- myeclipse学习总结一(在MyEclipse中设置生成jsp页面时默认编码为utf-8编码)
1.每次我们在MyEclispe中创建Jsp页面,生成的Jsp页面的默认编码是"ISO-8859-1".在这种情况下,当我们在页面中编写的内容存在中文的时候,就无法进行保存.如下图 ...
- CentOS7下mysql5.6修改默认编码
参考原文教程:Centos7下修改mysql5.6编码方式 解决网站中文显示问号 解决办法: 修改MySQL数据库字符编码为UTF-8,UTF-8包含全世界所有国家需要用到的字符,是国际编码. 具体操 ...
- 转载--改变ubuntu默认编码为GBK
在Ubuntu支持中文后(方法见上篇文章),默认是UTF-8编码,而Windows中文版默认是GBK编码.为了一致性,通常要把Ubuntu的默认编码改为GBK.当然你也可以不改,但这会导致我们在两个系 ...
随机推荐
- 素数表(Eratosthenes)
怎么判断一个数是素数? 常规的方法是枚举从2开始的数,看看是否能被整除. 但是,如果要判断的数很多的时候,那么效率会十分低下.... 一个优化的方法是不用判断比这个数小的所有数(到平方根位置),而是判 ...
- echarts同一页面四个图表切换的js数据交互
需求:点击tab页,切换四个不同的图表,ajax向后台请求数据,展示在四个不同的图表中. 其余的就不多说,直接上js代码了 $(function() { $("#heart").o ...
- 关于Altium Designer的BOM,元件清单
在生成BOM列表的时候,要记得调整BOM的表格的宽度,以免显示不全, 还有就是BOM列表一共有 comment栏 ,description栏,designator栏,footprint栏,libref ...
- UVA 11987 - Almost Union-Find
第一次交TLE,说好的并查集昂. 好吧我改.求和.个数 在各个步骤独立算.. 还是TLE. 看来是方法太慢,就一个数组(fa),移动的话,移动跟结点要遍历一次 T T 嗯,那就多一个数组. 0.189 ...
- 键盘钩子监测按键后,获取键码及按键名称(MFC)
LRESULT CALLBACK LowLevelKeyboardProc(int nCode,WPARAM wParam,LPARAM lParam){ if(nCode ==HC_ACTION & ...
- 【例题3-4 UVA - 340】Master-Mind Hints
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 这里出现了没有在相同位置的只能唯一配对. 就是说 3322 2234 这种情况. 只有3个weak pair. 即key[1]=a[ ...
- LVS负载均衡+动静分离+高可用(nginx+tomcat+keepalived)
文章目录 [隐藏] 一.环境介绍 二.环境安装 1.安装JDK 2.两台服务器安装tomcat 3.nginx安装 4.keepalive安装 三.负载均衡 四.动静分离 五.keepalive高可用 ...
- [NIO]用dawn发送接收HTTP请求
HTTP协议的下层使用的是tcp.所以我们建立一个tcp连接就能发送接收http请求.dawn底层使用了nio.可是经过dawn的封装之后,我们在编写代码的时候,就和使用普通的堵塞式socket一样 ...
- 原生js螺旋运动
window.onload=function(){ var oSpiral=document.getElementById('spiral'); var oUl=oSpiral.getElements ...
- 【solr专题之四】关于VelocityResponseWriter 分类: H4_SOLR/LUCENCE 2014-07-22 12:32 1639人阅读 评论(0) 收藏
一.关于Velocity的基本配置 在Solr中,可以以多种方式返回搜索结果,如单纯的文本回复(XML.JSON.CSV等),也可以返回velocity,js等格式.而VelocityResponse ...