我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何使用FileReader采用的默认编码

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何使用FileReader采用的默认编码

小编给大家分享一下如何使用FileReader采用的默认编码,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

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这个类的开头有下面一段文档注释(中文部分为我劣质的翻译):

所以,设计者已经在文档注释中讲明白了这么设计的原因。但是对于我们来说,现在比较重要的是这个所谓的默认的字符编码是什么。

这个时候我们来看一下我们使用的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”,不过可以根据需要从下拉框选择。

这代码看起来很费劲,而且接着又要看其它代码。最终结果是这个所谓的默认字符编码,其实就是JVM启动时候的本地编码。

这个要查看的话,就在对应的项目上点击右键,选择Properties选项,在弹出的属性窗口中,可以看到当前项目在JVM中运行时候的默认字符编码。对于咱们中国人来说,一般都是“GBK”,不过可以根据需要从下拉框选择。

如何使用FileReader采用的默认编码

所以开头那个疑问,完全是因为不知道默认的编码其实是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将会采用什么编码,但是还是建议采用字符类的时候加上字符编码,因为写清楚字符编码可以让别人明白你的原意,而且能避免代码转手后换了一个开发工具后可能出现的编码异常问题。

FileReader的编码问题

有一个UTF-8编码的文本文件,用FileReader读取到一个字符串,然后转换字符集:str=new String(str.getBytes(),"UTF-8");结果大部分中文显示正常,但最后仍有部分汉字显示为问号!

public static List<String> getLines( String fileName )  {      List<String> lines = new ArrayList<String>();      try      {          BufferedReader br = new BufferedReader(new FileReader(fileName));          String line = null;          while( ( line = br.readLine() ) != null )              lines.add(new String(line.getBytes("GBK"), "UTF-8"));          br.close();      }      catch( FileNotFoundException e )      {      }      catch( IOException e )      {      }      return lines;  }

文件读入时是按OS的默认字符集即GBK解码的,我先用默认字符集GBK编码str.getBytes(“GBK”),此时应该还原为文件中的字节序列了,然后再按UTF-8解码,生成的字符串按理说应该就应该是正确的。

为什么结果中还是有部分乱码呢?

问题出在FileReader读取文件的过程中,FileReader继承了InputStreamReader,但并没有实现父类中带字符集参数的构造函数,所以FileReader只能按系统默认的字符集来解码,然后在UTF-8 -> GBK -> UTF-8的过程中编码出现损失,造成结果不能还原最初的字符。

原因明确了,用InputStreamReader代替FileReader,InputStreamReader isr=new InputStreamReader(new FileInputStream(fileName),"UTF-8");这样读取文件就会直接用UTF-8解码,不用再做编码转换。

public static List<String> getLines( String fileName )  {      List<String> lines = new ArrayList<String>();      try      {          BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), "UTF-8"));          String line = null;          while( ( line = br.readLine() ) != null )              lines.add(line);          br.close();      }      catch( FileNotFoundException e )      {      }      catch( IOException e )      {      }      return lines;  }

看完了这篇文章,相信你对“如何使用FileReader采用的默认编码”有了一定的了解,如果想了解更多相关知识,欢迎关注编程网行业资讯频道,感谢各位的阅读!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何使用FileReader采用的默认编码

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何使用FileReader采用的默认编码

小编给大家分享一下如何使用FileReader采用的默认编码,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!FileReader采用的默认编码很久以前听教学视频,里面讲到Java采用的默认编码是ISO-8859-1,一直记着
2023-06-22

Ubuntu 19.10如何将使用GCC 9作为默认编译器

今天就跟大家聊聊有关Ubuntu 19.10如何将使用GCC 9作为默认编译器,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。作为我们这一周期一直期待的变化,Ubuntu 19.10升
2023-06-05

如何在SASS 中使用变量的默认值

今天就跟大家聊聊有关如何在SASS 中使用变量的默认值,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。SASS 中定义的变量,后设置的值会覆盖旧的值。$color: red;$colo
2023-06-08

如何在java中使用sqrt默认方法

如何在java中使用sqrt默认方法?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Java是什么Java是一门面向对象编程语言,可以编写桌面应用程序、Web应用程序、分布式系统
2023-06-07

javascript中如何设置和使用默认值

本篇内容主要讲解“javascript中如何设置和使用默认值”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“javascript中如何设置和使用默认值”吧!设置默认值在过去,为了设置函数参数的默认
2023-07-06

Ubuntu如何配置默认Java使用哪个

这篇文章给大家分享的是有关Ubuntu如何配置默认Java使用哪个的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Ubuntu简介Ubuntu是一个以桌面应用为主的Linux操作系统,其名称来自非洲南部祖鲁语或豪萨
2023-06-27

Win8采用扁平化的Metro设计风格如何设置默认进入桌面

Windows 8系统推出的全新的”开始“屏幕界面作为登陆后的默认界面,采用扁平化的Metro设计风格。但这一界面很多人不喜欢,他们更希望登陆后默认进入桌面。这该如何设置呢?  步骤首先如果是windows8.1则在
2022-06-04

如何设置MySQL数据库默认使用MyISAM?

要设置默认存储引擎,请使用以下语法 -set @@default_storage_engine = ’yourEngineType’;现在将上述语法实现为将默认引擎设置为MyISAM。查询如下 −mysq
2023-10-22

如何使用Django默认的Auth权限管理系统

本文主要介绍了如何使用Django默认的Auth权限管理系统,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-02-13

python如何在函数声明中使用默认值

小编给大家分享一下python如何在函数声明中使用默认值,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!在函数声明中使用默认值在几乎所有的Python项目中,大多数
2023-06-27

如何使用email-ext替换Jenkins的默认邮件通知

今天就跟大家聊聊有关如何使用email-ext替换Jenkins的默认邮件通知,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1 简述众所周知,Jenkins 默认提供了一个邮件通知,
2023-06-04

如何使用 Go 连接到非默认 firestore 数据库?

php小编小新在这篇文章中将向大家介绍如何使用Go语言连接到非默认的Firestore数据库。Firestore是一种灵活的、可扩展的NoSQL文档数据库,但默认情况下它只连接到Google Cloud项目的默认数据库。然而,有时候我们可能
如何使用 Go 连接到非默认 firestore 数据库?
2024-02-08

ThinkPHP中join关联查询如何不使用默认的表前缀

小编给大家分享一下ThinkPHP中join关联查询如何不使用默认的表前缀,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!php有什么特点1、执行速度快。2、具有很
2023-06-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录