文章目录

  • 简介
  • 字符和字节
  • 按字符读取的方式
  • 按字节读取的方式
  • 寻找出错的行数
  • 总结

简介

小师妹最新对java IO中的reader和stream产生了一点点困惑,不知道到底该用哪一个才对,怎么读取文件才是正确的姿势呢?今天F师兄现场为她解答。

更多精彩内容:
区块链从入门到放弃系列教程-涵盖密码学,超级账本,以太坊,Libra,比特币等持续更新
Spring Boot 2.X系列教程:七天从无到有掌握Spring Boot-持续更新
Spring 5.X系列教程:满足你对Spring5的一切想象-持续更新
java程序员从小工到专家成神之路(2020版)-持续更新中,附详细文章教程

字符和字节

小师妹最近很迷糊:F师兄,上次你讲到IO的读取分为两大类,分别是Reader,InputStream,这两大类有什么区别吗?为什么我看到有些类即是Reader又是Stream?比如:InputStreamReader?

小师妹,你知道哲学家的终极三问吗?你是谁?从哪里来?到哪里去?

F师兄,你是不是迷糊了,我在问你java,你扯什么哲学。

小师妹,其实吧,哲学是一切学问的基础,你知道科学原理的英文怎么翻译吗?the philosophy of science,科学的原理就是哲学。

你看计算机中代码的本质是什么?代码的本质就是0和1组成的一串长长的二进制数,这么多二进制数组合起来就成了计算机中的代码,也就是JVM可以识别可以运行的二进制代码。

更多内容请访问www.flydean.com

小师妹一脸崇拜:F师兄说的好像很有道理,但是这和Reader,InputStream有什么关系呢?

别急,冥冥中自有定数,先问你一个问题,java中存储的最小单位是什么?

小师妹:容我想想,java中最小的应该是boolean,true和false正好和二进制1,0对应。

对了一半,虽然boolean也是java中存储的最小单位,但是它需要占用一个字节Byte的空间。java中最小的存储单位其实是字节Byte。不信的话可以用之前我介绍的JOL工具来验证一下:

[main] INFO com.flydean.JolUsage - java.lang.Boolean object internals:OFFSET  SIZE      TYPE DESCRIPTION                               VALUE0    12           (object header)                           N/A12     1   boolean Boolean.value                             N/A13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

上面是装箱过后的Boolean,可以看到虽然Boolean最后占用16bytes,但是里面的boolean只有1byte。

byte翻译成中文就是字节,字节是java中存储的基本单位。

有了字节,我们就可以解释字符了,字符就是由字节组成的,根据编码方式的不同,字符可以有1个,2个或者多个字节组成。我们人类可以肉眼识别的汉字呀,英文什么的都可以看做是字符。

而Reader就是按照一定编码格式读取的字符,而InputStream就是直接读取的更加底层的字节。

小师妹:我懂了,如果是文本文件我们就可以用Reader,非文本文件我们就可以用InputStream。

孺子可教,小师妹进步的很快。

按字符读取的方式

小师妹,接下来F师兄给你讲下按字符读取文件的几种方式,第一种就是使用FileReader来读取File,但是FileReader本身并没有提供任何读取数据的方法,想要真正的读取数据,我们还是要用到BufferedReader来连接FileReader,BufferedReader提供了读取的缓存,可以一次读取一行:

public void withFileReader() throws IOException {File file = new File("src/main/resources/www.flydean.com");try (FileReader fr = new FileReader(file); BufferedReader br = new BufferedReader(fr)) {String line;while ((line = br.readLine()) != null) {if (line.contains("www.flydean.com")) {log.info(line);}}}}

每次读取一行,可以把这些行连起来就组成了stream,通过Files.lines,我们获取到了一个stream,在stream中我们就可以使用lambda表达式来读取文件了,这是谓第二种方式:

public void withStream() throws IOException {Path filePath = Paths.get("src/main/resources", "www.flydean.com");try (Stream<String> lines = Files.lines(filePath)){List<String> filteredLines = lines.filter(s -> s.contains("www.flydean.com")).collect(Collectors.toList());filteredLines.forEach(log::info);}}

第三种其实并不常用,但是师兄也想教给你。这一种方式就是用工具类中的Scanner。通过Scanner可以通过换行符来分割文件,用起来也不错:

public void withScanner() throws FileNotFoundException {FileInputStream fin = new FileInputStream(new File("src/main/resources/www.flydean.com"));Scanner scanner = new Scanner(fin,"UTF-8").useDelimiter("\n");String theString = scanner.hasNext() ? scanner.next() : "";log.info(theString);scanner.close();}

按字节读取的方式

小师妹听得很满足,连忙催促我:F师兄,字符读取方式我都懂了,快将字节读取吧。

我点了点头,小师妹,哲学的本质还记得吗?字节就是java存储的本质。掌握到本质才能勘破一切虚伪。

还记得之前讲过的Files工具类吗?这个工具类提供了很多文件操作相关的方法,其中就有读取所有bytes的方法,小师妹要注意了,这里是一次性读取所有的字节!一定要慎用,只可用于文件较少的场景,切记切记。

public void readBytes() throws IOException {Path path = Paths.get("src/main/resources/www.flydean.com");byte[] data = Files.readAllBytes(path);log.info("{}",data);}

如果是比较大的文件,那么可以使用FileInputStream来一次读取一定数量的bytes:

public void readWithStream() throws IOException {File file = new File("src/main/resources/www.flydean.com");byte[] bFile = new byte[(int) file.length()];try(FileInputStream fileInputStream  = new FileInputStream(file)){fileInputStream.read(bFile);for (int i = 0; i < bFile.length; i++) {log.info("{}",bFile[i]);}}}

Stream读取都是一个字节一个字节来读的,这样做会比较慢,我们使用NIO中的FileChannel和ByteBuffer来加快一些读取速度:

public void readWithBlock() throws IOException {try (RandomAccessFile aFile = new RandomAccessFile("src/main/resources/www.flydean.com", "r");FileChannel inChannel = aFile.getChannel();) {ByteBuffer buffer = ByteBuffer.allocate(1024);while (inChannel.read(buffer) > 0) {buffer.flip();for (int i = 0; i < buffer.limit(); i++) {log.info("{}", buffer.get());}buffer.clear();}}}

小师妹:如果是非常非常大的文件的读取,有没有更快的方法呢?

当然有,记得上次我们讲过的虚拟地址空间的映射吧:

我们可以直接将用户的地址空间和系统的地址空间同时map到同一个虚拟地址内存中,这样就免除了拷贝带来的性能开销:

public void copyWithMap() throws IOException{try (RandomAccessFile aFile = new RandomAccessFile("src/main/resources/www.flydean.com", "r");FileChannel inChannel = aFile.getChannel()) {MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());buffer.load();for (int i = 0; i < buffer.limit(); i++){log.info("{}", buffer.get());}buffer.clear();}}

寻找出错的行数

小师妹:好赞!F师兄你讲得真好,小师妹我还有一个问题:最近在做文件解析,有些文件格式不规范,解析到一半就解析失败了,但是也没有个错误提示到底错在哪一行,很难定位问题呀,有没有什么好的解决办法?

看看天色已经不早了,师兄就再教你一个方法,java中有一个类叫做LineNumberReader,使用它来读取文件可以打印出行号,是不是就满足了你的需求:

public void useLineNumberReader() throws IOException {try(LineNumberReader lineNumberReader = new LineNumberReader(new FileReader("src/main/resources/www.flydean.com"))){//输出初始行数log.info("Line {}" , lineNumberReader.getLineNumber());//重置行数lineNumberReader.setLineNumber(2);//获取现有行数log.info("Line {} ", lineNumberReader.getLineNumber());//读取所有文件内容String line = null;while ((line = lineNumberReader.readLine()) != null){log.info("Line {} is : {}" , lineNumberReader.getLineNumber() , line);}}}

总结

今天给小师妹讲解了字符流和字节流,还讲解了文件读取的基本方法,不虚此行。

本文的例子https://github.com/ddean2009/learn-java-io-nio

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/io-file-reader/

本文来源:flydean的博客

欢迎关注我的公众号:程序那些事,更多精彩等着您!

小师妹学JavaIO之:文件读取那些事相关推荐

  1. 小师妹学JavaIO之:文件写入那些事

    文章目录 简介 字符输出和字节输出 格式化输出 输出其他对象 在特定的位置写入 给文件加锁 总结 简介 小师妹又对F师兄提了一大堆奇奇怪怪的需求,要格式化输出,要特定的编码输出,要自己定位输出,什么? ...

  2. .dat文件写入byte类型数组_小师妹学JavaIO之:文件写入那些事

    简介 小师妹又对F师兄提了一大堆奇奇怪怪的需求,要格式化输出,要特定的编码输出,要自己定位输出,什么?还要阅后即焚?大家看F师兄怎么一一接招吧. 字符输出和字节输出 小师妹:F师兄,上次你的IO讲到了 ...

  3. 小师妹学JavaIO之:文件编码和字符集Unicode

    文章目录 简介 使用Properties读取文件 乱码初现 字符集和文件编码 解决Properties中的乱码 真.终极解决办法 总结 简介 小师妹一时兴起,使用了一项从来都没用过的新技能,没想却出现 ...

  4. 小师妹学JavaIO之:文件File和路径Path

    文章目录 简介 文件和路径 文件中的不同路径 构建不同的Path 总结 简介 文件和路径有什么关系?文件和路径又隐藏了什么秘密?在文件系统的管理下,创建路径的方式又有哪些?今天F师兄带小师妹再给大家来 ...

  5. 小师妹学JavaIO之:文件系统和WatchService

    文章目录 简介 监控的痛点 WatchService和文件系统 WatchSerice的使用和实现本质 总结 简介 小师妹这次遇到了监控文件变化的问题,F师兄给小师妹介绍了JDK7 nio中引入的Wa ...

  6. java文件夹路径的表达_小师妹学JavaIO之:文件File和路径Path

    简介 文件和路径有什么关系?文件和路径又隐藏了什么秘密?在文件系统的管理下,创建路径的方式又有哪些?今天F师兄带小师妹再给大家来一场精彩的表演. 文件和路径 小师妹:F师兄我有一个问题,java中的文 ...

  7. 小师妹学JavaIO之:MappedByteBuffer多大的文件我都装得下

    文章目录 简介 虚拟地址空间 详解MappedByteBuffer MapMode MappedByteBuffer的最大值 MappedByteBuffer的使用 MappedByteBuffer要 ...

  8. 小师妹学JavaIO之:目录还是文件

    文章目录 简介 linux中的文件和目录 目录的基本操作 目录的进阶操作 目录的腰疼操作 总结 简介 目录和文件傻傻分不清楚,目录和文件的本质到底是什么?在java中怎么操纵目录,怎么遍历目录.本文F ...

  9. mappedbytebuffer_小师妹学JavaIO之:MappedByteBuffer多大的文件我都装得下

    简介 大大大,我要大!小师妹要读取的文件越来越大,该怎么帮帮她,让程序在性能和速度上面得到平衡呢?快来跟F师兄一起看看吧. 虚拟地址空间 小师妹:F师兄,你有没有发现,最近硬盘的价格真的是好便宜好便宜 ...

最新文章

  1. R聚类(整群)抽样(Cluster Sampling)
  2. Jsp实现在线影院售票系统
  3. 计算机二级msoffice操作题如何评分,2017年计算机二级MSOffice操作题及答案解析
  4. 数据库和MySQL相关面试题目
  5. 怎么用贝塞尔工具画圆_Win10恶意软件删除工具怎么用?这个方法都舍不得分享...
  6. web前端开发之div+css教程精华收集二
  7. 【编译制导指令】#pragma pack - 字节数基准对齐
  8. kafka自带的zk启动_KafKa 启动
  9. mysql 表单记录主键重新从1开始排序
  10. Android 获取当前地理位置信息
  11. HDU 4609 3-idiots (思维+FFT卷积)
  12. python-linux-获取多台远端机器指定文件并下载至本地(s**y-日志审计)
  13. 微信公众号运营都有哪些实用小技巧,你学会了吗
  14. 安全测试工具BurpSuite
  15. 会通转债,大元转债上市价格预测
  16. Linux逻辑卷、物理卷、卷组常用命令
  17. DS18B20 (by 51单片机)
  18. 交换机直连电脑配置telnet
  19. 创新!全球第二大出版商推iPad电子书
  20. uni-app - 搜索历史记录功能(纯前端)

热门文章

  1. 越来越注重手机拍照的时代,OPPO R11凭借哪些因素占领了市场?
  2. 游戏常用算法:冒泡排序
  3. 固定电话号码正则表达式(支持手机号码,3-4位区号,7-8位直播号码,1-4位分机号)
  4. 选择IT我不曾后悔?希望高人指点迷津
  5. 《解忧杂货店》—— 读后总结
  6. FreeSWITCH开启录音功能
  7. Elasticsearch——分页查询FromSize VS scroll
  8. Elasticsearch7.5.0安全(xpack)之身份认证
  9. 【读书笔记】《怪诞行为学》丹·艾瑞里
  10. 如何修改管理员的名字