小师妹学JavaIO之:文件读取那些事
简介
小师妹最新对java IO中的reader和stream产生了一点点困惑,不知道到底该用哪一个才对,怎么读取文件才是正确的姿势呢?今天F师兄现场为她解答。
字符和字节
小师妹最近很迷糊: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 VALUE
0 12 (object header) N/A
12 1 boolean Boolean.value N/A
13 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之:文件读取那些事的更多相关文章
- 小师妹学JavaIO之:文件系统和WatchService
目录 简介 监控的痛点 WatchService和文件系统 WatchSerice的使用和实现本质 总结 简介 小师妹这次遇到了监控文件变化的问题,F师兄给小师妹介绍了JDK7 nio中引入的Watc ...
- 小师妹学JavaIO之:文件File和路径Path
简介 文件和路径有什么关系?文件和路径又隐藏了什么秘密?在文件系统的管理下,创建路径的方式又有哪些?今天F师兄带小师妹再给大家来一场精彩的表演. 文件和路径 小师妹:F师兄我有一个问题,java中的文 ...
- 小师妹学JavaIO之:目录还是文件
目录 简介 linux中的文件和目录 目录的基本操作 目录的进阶操作 目录的腰疼操作 总结 简介 目录和文件傻傻分不清楚,目录和文件的本质到底是什么?在java中怎么操纵目录,怎么遍历目录.本文F师兄 ...
- 小师妹学JavaIO之:MappedByteBuffer多大的文件我都装得下
目录 简介 虚拟地址空间 详解MappedByteBuffer MapMode MappedByteBuffer的最大值 MappedByteBuffer的使用 MappedByteBuffer要注意 ...
- 小师妹学JavaIO之:用Selector来发好人卡
目录 简介 Selector介绍 创建Selector 注册Selector到Channel中 SelectionKey selector 和 SelectionKey 总的例子 总结 简介 NIO有 ...
- 小师妹学JavaIO之:NIO中Channel的妙用
目录 简介 Channel的分类 FileChannel Selector和Channel DatagramChannel SocketChannel ServerSocketChannel Asyn ...
- 小师妹学JavaIO之:Buffer和Buff
目录 简介 Buffer是什么 Buffer进阶 创建Buffer Direct VS non-Direct Buffer的日常操作 向Buffer写数据 从Buffer读数据 rewind Buff ...
- 小师妹学JavaIO之:NIO中那些奇怪的Buffer
目录 简介 Buffer的分类 Big Endian 和 Little Endian aligned内存对齐 总结 简介 妖魔鬼怪快快显形,今天F师兄帮助小师妹来斩妖除魔啦,什么BufferB,Buf ...
- 小师妹学IO系列文章集合-附PDF下载
目录 第一章 IO的本质 IO的本质 DMA和虚拟地址空间 IO的分类 IO和NIO的区别 总结 第二章 try with和它的底层原理 简介 IO关闭的问题 使用try with resource ...
- 小师妹学JVM之:JVM的架构和执行过程
目录 简介 JVM是一种标准 java程序的执行顺序 JVM的架构 类加载系统 运行时数据区域 执行引擎 总结 简介 JVM也叫Java Virtual Machine,它是java程序运行的基础,负 ...
随机推荐
- [C++逆向] 7 变量在内存中的位置和访问方式
目录 全局变量和局部变量的区别 局部静态变量 有意思的 堆变量 变量类型 作用域 可访问 全局变量 进程作用域 整个进程可访问 静态变量 文件作用域 当前代码文件可访问 局部变量 函数作用域 函数内可 ...
- C++ //常用算法 adjacent_find //查找相邻的重复元素
1 //常用算法 adjacent_find 2 //查找相邻的重复元素 3 #include<iostream> 4 #include<string> 5 #include& ...
- RPA能否创造新业态?如何优化组织结构?如何助力疫情中的企业?
RPA能否创造新业态?如何优化组织结构?如何助力疫情中的企业? 从<爱,死亡和机器人>探讨强人工智能时代的RPA发展 文/王吉伟 本周四,王吉伟频道参加了私域流量社群的一个直播活动. 活动 ...
- golang开发_goroutine在项目中的使用姿势
很多初级的Gopher在学习了goroutine之后,在项目中其实使用率不高,尤其一些跨语言过来的人,对并发编程理解不深入,可能很多人只知道go func(),或者掌控不够,谨慎一些,尽量少使用或者不 ...
- Zabbix与乐维监控对比分析(五)——可视化篇
前面我们详细介绍了Zabbix与乐维监控的架构与性能.Agent管理.自动发现.权限管理.对象管理.告警管理方面的对比分析,相信大家对二者的对比分析有了相对深入的了解,接下来我们将对二者的可视化功能进 ...
- reciterdoc 资料库 支持中文搜索了。 vuepress-plugin-fulltext-search(用一半)
http://pengchenggang.gitee.io/reciterdoc/ 支持中文搜索了,可是不容易了 通过插件 vuepress-plugin-fulltext-search 实现全文搜索 ...
- Springboot K8s Job 一次性任务 如何禁用端口监听
问题:SpringBoot一次性任务执行时,也会默认监听服务端口,当使用k8s job运行时,可能多个pod执行存在端口冲突 解决办法:命令行禁用SpringBoot一次性任务启动时端口占用 java ...
- 基于BES2500芯片的方案开发总结和反思
前记 虽然蓝牙芯片现在非常多,可是一旦想做一些差异化的产品时候,能用的非常少.在和客户的反复斟酌中,最终选择了BES2500来开发这款新的产品.这里面是基于自身的实力,产品定位和芯片诉求多重考量的 ...
- 海量数据去重的Hash与BloomFilter
今天我们谈论一下散列表,我之前的两个博文写的都是关于平衡二叉树的 平衡二叉树 增删改查时间复杂度为log2n 平衡的目的是增删改以后,保证下次搜索能稳定排除一半的数据: 总结:通过比较保证有序,通过每 ...
- 修改easyui日期控件只显示年月,并且只能选择年月
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...