广告
返回顶部
首页 > 资讯 > 后端开发 > Python >使用FileReader采用的默认编码
  • 366
分享到

使用FileReader采用的默认编码

2024-04-02 19:04:59 366人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录FileReader采用的默认编码事情的真相是什么呢?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”,不过可以根据需要从下拉框选择。

所以开头那个疑问,完全是因为不知道默认的编码其实是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;  
}  

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: 使用FileReader采用的默认编码

本文链接: https://www.lsjlt.com/news/159554.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • 使用FileReader采用的默认编码
    目录FileReader采用的默认编码事情的真相是什么呢?FileReader的编码问题为什么结果中还是有部分乱码呢?FileReader采用的默认编码 很久以前听教学视频,里面讲到...
    99+
    2022-11-12
  • 如何使用FileReader采用的默认编码
    小编给大家分享一下如何使用FileReader采用的默认编码,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!FileReader采用的默认编码很久以前听教学视频,里面讲到Java采用的默认编码是ISO-8859-1,一直记着...
    99+
    2023-06-22
  • 怎么使用phpmyadmin修改数据库默认编码
    小编给大家分享一下怎么使用phpmyadmin修改数据库默认编码,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!phpmyadmi...
    99+
    2022-10-18
  • 用python查看和更改系统默认编码
    用python查看和更改系统默认编码   python在安装时,默认的编码是ascii,当程序中出现非ascii编码时,python的处理常常会报这样的错UnicodeDecodeError: 'ascii' codec c...
    99+
    2023-01-31
    系统 python
  • phpmyadmin数据库管理工具的默认用户名和密码默认是什么
    小编给大家分享一下phpmyadmin数据库管理工具的默认用户名和密码默认是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!p...
    99+
    2022-10-18
  • python默认参数的使用方法
    本篇内容介绍了“python默认参数的使用方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!说明指定默认值的形式参数必须在所有没有默认值的参...
    99+
    2023-06-20
  • MySQL的默认用户名和密码是什么
    这篇文章主要介绍MySQL的默认用户名和密码是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!MySQL是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据...
    99+
    2022-10-18
  • MySQL默认值约束的使用方法
    MySQL默认值约束的使用方法?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧!常用数据库约束:default 默认约束;no...
    99+
    2022-10-18
  • Java8默认方法会破坏用户的代码吗
    这篇文章主要讲解了“Java8默认方法会破坏用户的代码吗”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java8默认方法会破坏用户的代码吗”吧!起初看来,默认方法给Java虚拟机的指令集带来...
    99+
    2023-06-17
  • Win8采用扁平化的Metro设计风格如何设置默认进入桌面
      Windows 8系统推出的全新的”开始“屏幕界面作为登陆后的默认界面,采用扁平化的Metro设计风格。但这一界面很多人不喜欢,他们更希望登陆后默认进入桌面。这该如何设置呢   ...
    99+
    2022-06-04
    设计风格 如何设置 扁平化
  • Ubuntu 19.10如何将使用GCC 9作为默认编译器
    今天就跟大家聊聊有关Ubuntu 19.10如何将使用GCC 9作为默认编译器,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。作为我们这一周期一直期待的变化,Ubuntu 19.10升...
    99+
    2023-06-05
  • java接口使用默认方法的讲解
    1、接口中默认方法的格式: public default返回值类型方法名(参数列表) { } 2、接口中默认方法的注意事项: 默认的方法不是抽象的方法,所以不重写。但是可以重写...
    99+
    2022-11-12
  • MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决
    目录LambdaQueryWrapper使用 int默认值问题问题现象解决方法问题分析小结一下lambdaquerywrapper in使用样例直接使用列名报错下面这样okLambd...
    99+
    2022-11-12
  • 如何在SASS 中使用变量的默认值
    今天就跟大家聊聊有关如何在SASS 中使用变量的默认值,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。SASS 中定义的变量,后设置的值会覆盖旧的值。$color: red;...
    99+
    2023-06-08
  • java接口使用默认方法的示例分析
    这篇文章给大家分享的是有关java接口使用默认方法的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1、接口中默认方法的格式:public default返回值类型方法名(参数列表) {...
    99+
    2023-06-14
  • springboot2.x默认使用的代理是cglib代理操作
    背景 因为项目优化,打算写个日志的切面类,于是起了个springboot 工程,在这里面测试。结果在springboot 里面测试正常,能正确打印日志,但是把代码复制到实际项目中,在...
    99+
    2022-11-12
  • 如何使用Django默认的Auth权限管理系统
    目录1.Django默认已经提供了认证系统Auth模块。2.Django用户模型3.自定义User对象1.Django默认已经提供了认证系统Auth模块。 认证系统包含: 用户管理权...
    99+
    2023-02-13
    Django Auth权限管理系统 Django 权限管理系统
  • 使用elementuiadmin去掉默认mock权限控制的设置
    目录elementuiadmin去掉默认mock权限控制的设置vue-elementui-admin的动态权限控制总结elementuiadmin去掉默认mock权限控制的设置 一般...
    99+
    2023-05-17
    elementuiadmin elementuiadmin mock 默认mock权限控制设置
  • 记录--使用el-time-picker默认值遇到的问题
    目录使用el-time-picker默认值问题el-time-picker 默认时间展示使用el-time-picker默认值问题 首先文档有指出不同组件的默认值default-va...
    99+
    2022-11-13
  • 深入JS函数中默认参数的使用详解
    目录ES5中的默认参数全新默认参数的诞生默认位置默认参数对函数的length属性的影响重新创建的默认参数默认参数与形参的结合使用默认参数的作用域问题默认参数的隐藏性死区(隐藏的暂时性...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作