java读取文件卡顿_100G的文件如何读取 - 第306篇

这篇博客介绍了Java中处理大文件的不同方法,包括内存读取、IO逐行读取(FileReader、BufferedReader、Scanner、RandomAccessFile)和NIO读取(FileChannel)。通过实例测试,展示了各种方法的性能和适用场景,强调了在处理大文件时避免一次性加载到内存中的重要性。
摘要由CSDN通过智能技术生成

75cc8dd09352c1bcb5a0b9b682ec3ab4.png

相关历史文章(阅读本文之前,您可能需要先看下之前的系列 )

国内最全的Spring Boot系列之三

精度不够,滑动时间来凑「限流算法第二把法器:滑动时间窗口算法」- 第301篇

没有预热,不叫高并发「限流算法第三把法器:令牌桶算法」- 第302篇

水满自溢「限流算法第四把法器:漏桶算法」- 第303篇

一分钟get:缓存穿透、缓存击穿、缓存雪崩 - 第304篇

布隆过滤器Bloom Filter竟然让我解决了一个大厂的问题 - 第305篇

在前面的文章《布隆过滤器Bloom Filter竟然让我解决了一个大厂的问题》大厂面试题中,还隐含着一个问题,已经被我们的粉丝提前嗅探出来了:“如何用4g内存读取298g的文件(a和b文件)“。

这里我们把它抽象成,Java如何读取大文件?

师傅:我可爱的小徒儿,你知否?

7785fadadf754b45904f56b446428f90.png

悟纤:知否?知否?应是绿肥红瘦。你说我知否?

74b0ca1f9c0935a82c144d48d231e626.gif

师傅:徒儿,你这是什么乱七八糟的,你不说,我怎知你知否?

悟纤:那徒儿,今天来给你展示下我深厚的功力了。

师傅:来,请开始你的表演。

b7fc1652e255f8ac4eb082122f6b703c.png

一、内存读取法:简单明了,不玩阴的

此方法的思路很简单,就是把文件直接读取到内存中,然后进行操作。

1.1 方法一:使用java.nio.file.Files读取文本文件

使用Files类将文件的所有内容读入字节数组。Files类还有一个方法可以读取所有行到字符串列表。Files类是在Java 7中引入的,如果想加载所有文件内容,使用这个类是比较适合的。只有在处理小文件并且需要加载所有文件内容到内存中时才应使用此方法。

	public static void readFileByFiles(String pathname) {
		Path path = Paths.get(pathname);
		try {
			/*
			 * 使用readAllLines的时候,小文件可以很快读取.
			 * 那么更大的文件,读取的肯定会爆了。
			 */
			//List<String> lines = Files.readAllLines(path);
			
			byte[] bytes = Files.readAllBytes(path);
			String str = new String(bytes);
			System.out.println(str);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

对于小文件,轻松就可以读取进来;

对于大文件就会抛出如下异常:

22c7f5dea3d5516cfc86c971f2bf63c8.png

或者是:

01cd53f0c30da389fbf21a1f044b79d1.png

结论:小文件可以使用这种方式;读取大文件,不能使用。

二、IO逐行读取法:循序渐进,好舒服

2.1 方法二:使用java.io.FileReader类

可以使用FileReader获取BufferedReader,然后逐行读取文件,FileRead也有读取char的方法,当然这样的读取方式效率很低很低了。

	public static void readFileByFileReader(String pathname) {
		File file = new File(pathname);
		FileReader fileReader;
		BufferedReader bufferedReader;
		try {
			fileReader = new FileReader(file);
			
			bufferedReader = new BufferedReader(fileReader);
			String line;
			StringBuffer buffer = new StringBuffer();
			while((line = bufferedReader.readLine()) != null){
			    // 一行一行地处理...
			    //System.out.println(line);
				//处理字符串,并不会将字符串保存真正保存到内存中
				 // 这里简单模拟下处理操作.
				buffer.append(line.substring(0,1));
			}
			 System.out.println("buffer.length:"+buffer.length());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//TODO close处理.
		}
		
	}

对于大文件可以逐行读取,没啥问题,测试了下:3.46G 耗时:11秒

注意:这里本质是使用了BufferedReader的缓冲,如果是使用的fileReader读取char的方式,那么时间会更久。

2.2方法三:使用java.io.BufferedReader

如果想逐行读取文件并对它们进行处理,那么BufferedReader是非常合适的。它适用于处理大文件,也支持编码。BufferedReader是同步的,因此可以安全地从多个线程完成对BufferedReader的读取操作。BufferedReader的默认缓冲区大小为:8KB。

	public static void readFileByBufferedReader(String pathname) {
		
		File file = new File(pathname);
		BufferedReader reader = null;
		FileInputStream fileInputStream = null;
		InputStreamReader inputStreamReader = null;
		try {
			//使用BufferedReader,每次读入1M数据.减少IO.如:
			fileInputStream = new FileInputStream(file);
			inputStreamReader = new InputStreamReader(fileInputStream, Charset.defaultCharset());
			reader = new BufferedReader(inputStreamReader,1*1024*1024);
			 String tempString = null;
			 StringBuffer buffer = new StringBuffer();
			 while( (tempString = reader.readLine()) != null) {
	              //System.out.println(tempString);
				 //处理字符串,并不会将字符串保存真正保存到内存中
				 // 这里简单模拟下处理操作.
				 buffer.append(tempString.substring(0,1));
	         }
			 System.out.println("buffer.length:"+buffer.length());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//TODO close处理
		}
		
	}

逐行读取,可以处理大文件,测试3.46G 耗时:10秒

2.3方法四:使用Scanner读取文本文件

如果要逐行读取文件或基于某些java正则表达式读取文件,则可使用Scanner类。

Scanner类使用分隔符模式将其输入分解为标记,分隔符模式默认匹配空格。然后可以使用各种下一种方法将得到的标记转换成不同类型的值。Scanner类不同步,因此不是线程安全的。

public static void readFileByScanner(String filePath) {
        FileInputStream inputStream = null;
        Scanner sc = null;
        try {
            inputStream = new FileInputStream(filePath);
            sc = new Scanner(inputStream, "UTF-8");
            StringBuffer buffer = new StringBuffer();
            while (sc.hasNextLine()) {
                String line = sc.nextLine();
                //System.out.println(line);
                //处理字符串,并不会将字符串保存真正保存到内存中
                // 这里简单模拟下处理操作.
				buffer.append(line.substring(0,1));
            }
            System.out.println("buffer.length:"+buffer.length());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //TODO close处理
        }
    }

逐行处理,可以处理大文件,测试3.46G 耗时:57秒

2.4 方法五:使用RandomAccessFile读取文本文件

Java中的RandomAccessFile提供了对文件的读写功能。RandomAccessFile 虽然属于http://java.io下的类,但它不是InputStream或者OutputStream的子类;它也不同于FileInputStream和FileOutputStream。 FileInputStream 只能对文件进行读操作,而FileOutputStream 只能对文件进行写操作;但是RandomAccessFile 与输入流和输出流不同之处就是RandomAccessFile可以访问文件的任意地方同时支持文件的读和写,并且它支持随机访问。RandomAccessFile包含InputStream的三个read方法,也包含OutputStream的三个write方法。同时RandomAccessFile还包含一系列的readXxx和writeXxx方法完成输入输出。

	public static void readFileByRandomAccessFile(String pathname) {
		RandomAccessFile randomAccessFile = null;
		String str;
		try {
			randomAccessFile = new RandomAccessFile(pathname, "r");
			StringBuffer buffer = new StringBuffer();
			while ((str = randomAccessFile.readLine()) != null) {
			    //System.out.println(str);
				//处理字符串,并不会将字符串保存真正保存到内存中
				 // 这里简单模拟下处理操作.
				 buffer.append(str.substring(0,1));
}
			System.out.println("buffer.length:"+buffer.length());
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//TODO close处理
		}
	}

逐行读取,可以读取大文件,测试3.46G 耗时:很长...

三、NIO逐行读取法:新的姿势,新的体验

3.1 方法五:使用FileChannel读取文本

顾名思义,FileChannel就是连接到文件的Channel。使用FileChannel,你可以读取文件数据,以及往文件里面写入数据。Java NIO的FileChannel是使用标准Java IO读取文件的一种替代方案。

public static void readFileFileChannel(String pathname) {
		File file = new File(pathname);
		FileInputStream fileInputStream = null;
		
		try {
			fileInputStream = new FileInputStream(file);
			FileChannel fileChannel = fileInputStream.getChannel();
			
			 int capacity = 1*1024*1024;//1M
			 ByteBuffer byteBuffer = ByteBuffer.allocate(capacity);
			 StringBuffer buffer = new StringBuffer();
			 while( fileChannel.read(byteBuffer) != -1) {
				 //读取后,将位置置为0,将limit置为容量, 以备下次读入到字节缓冲中,从0开始存储
	             byteBuffer.clear();
				 byte[] bytes = byteBuffer.array();  
				 String str = new String(bytes);
				 //System.out.println(str);
				//处理字符串,并不会将字符串保存真正保存到内存中
				 // 这里简单模拟下处理操作.
				 buffer.append(str.substring(0,1));
			 }
			 System.out.println("buffer.length:"+buffer.length());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//TODO close处理.
		}
		
	}

逐行读取,可以读取大文件,测试3.46G 耗时:3秒,测试6.5G 耗时:6秒

四、悟纤小结

小结下:

(1)java底层天然支持大文件的读取。
(2)常用的方式就是BufferedReader、FileChannel,FileChannel已经是很快了,测试3.46G 耗时:3秒,测试6.5G 耗时:6秒,测试13G 耗时15秒,那么130G,那么也就150秒左右了。
(3)NIO在大文件上的操作很占优势。(NIO为什么会这么快呢?)

师傅:不错、不错,总结的很赞了。

e6a35348922f2f40fad64c66510d4dbb.gif

徒儿:都是师傅教学有方。

师傅:还是徒儿自己好学、喜欢研究的结果。现在是实现了大文件的读取,那么有没有更快的方式呐?。

徒儿:那是,不知道师傅有何高招呐?

师傅:必修得有高招,不然怎么做你师傅呐,预知详情下回分解。

4410b5419c697d6bbf8d8ca3bb76bd48.png
我就是我,是颜色不一样的烟火。
我就是我,是与众不同的小苹果。
学院中有 Spring Boot相关的课程:
à悟空学院: https:// t.cn/Rg3fKJD
SpringBoot视频: http:// t.cn/A6ZagYTi
Spring Cloud视频: http:// t.cn/A6ZagxSR
SpringBoot Shiro视频: http:// t.cn/A6Zag7IV
SpringBoot交流平台: https:// t.cn/R3QDhU0
SpringData和JPA视频: http:// t.cn/A6Zad1OH
SpringSecurity5.0视频: http:// t.cn/A6ZadMBe
Sharding-JDBC分库分表实战: http:// t.cn/A6ZarrqS
分布式事务解决方案「手写代码」: http:// t.cn/A6ZaBnIr
JVM内存模型和性能调优: http:// t.cn/A6wWMVqG
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值