按行读取数据量很大的文件-BufferedReader

900 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

前言

明天就放假了兄弟们,大家都有什么计划呀?放松当然也是要放松的,但是希望我可以在空闲时间把最近做的这些需求整理好记录一下吧!简要概述需求就是:按行读取文件内容,对每一行的数据进行处理!上一篇文章记录了如何获取到每一个文件的绝对路径;今天记录一下如何按行读取数据量很大的文件,总共学习了两种方法,今天先记录一下其中的一种 :BufferedReader;

代码

这一段代码,在我测试的时候处理有70万行的数据的文件时运行良好,大家可以按需修改;领导说用list接收行内容不合适,这个我现在也还有疑问image.png

 // 按行读取文件,并截取数据存入list
public static List<String> readThread(String filePath){

    long start = System.currentTimeMillis();
    LOG.info("按行读取文件,并截取数据开始>>>>>>>>>>>>>>" +  LocalDateTime.now().toString() );
     List<String> list  = new ArrayList<>();
     try {
         BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
         BufferedReader in = new BufferedReader(new InputStreamReader(bis, "utf-8"), 10 * 1024 * 1024);//10M缓存
         String tempString;
         while ((tempString = in.readLine()) != null) {
             if(tempString.indexOf(",") != -1 && tempString.indexOf(",") > 14){
                list.add(tempString.substring(tempString.indexOf(",")-14));
             }else if(tempString.indexOf(",") == 14){
                list.add(tempString);
             }
//            在这里处理行数据        String line = in.readLine();

        }
        in.close();
        long end = System.currentTimeMillis();
        LOG.info("按行读取文件,并截取数据完成,耗时:" + (end - start) + " ms");
    } catch (IOException e) {
        e.printStackTrace();
    }
        return list;
    }

详解

概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成。

分类 按照流向分为:输入流和输出流;按照传输单位分为:字符流和字节流;

FileInputStream

它被称为文件字节输入流,意思指对文件数据以字节的形式进行读取操作;查看对应的源码如下:

public FileInputStream(String name) throws FileNotFoundException {
    this(name != null ? new File(name) : null);
}

BufferedInputStream

BufferedInputStream是一个带有缓冲区的输入流,通常使用它可以提高我们的读取效率。

原理:

BufferedInputStream内部有一个缓冲区,每次调用read方法的时候,它首先尝试从缓冲区里读取数据,若读取失败(缓冲区无可读数据),则选择从物理数据源(譬如文件)读取新数据(这里会尝试尽可能读取多的字节)放入到缓冲区中,最后再将缓冲区中的内容部分或全部返回给用户.由于从缓冲区里读取数据远比直接从物理数据源(譬如文件)读取速度快,所以BufferedInputStream的效率很高!

InputStreamReader

类 InputStreamReader是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。 为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:  

BufferedReader in= new BufferedReader(new InputStreamReader(bis));

readLine()

Java中BufferedReader类的readLine()方法用于一次读取一行文本。行尾应以'\ n'或'\ r'或EOF来理解。

参数: 此方法不接受任何参数。

返回值: 此方法返回此方法读取的String,并排除任何可用的终止符号。如果缓冲的流已经结束并且没有要读取的行,则此方法返回NULL。

异常: 如果发生I /O错误,则此方法将引发IOException。

综述

整体意思就是用InputStreamReader这个中介把FileInputStream这个字节流转换成字符流BufferedReader,再调用BufferedReader 对象的readLine()方法读取文件的每一行;

总结

这个按行读取大文件的方法,领导评审的时候说BufferedReader在读几次之后效率就会变得慢,性能不友好;可能是我测试的数据量比较小吧,没有明显的体会;领导说读出来的每行数据用list接收不合适,提了一嘴list的最大存储量是65535,但是我测试的时候70万条数据也没有报错呀?所以还是很疑惑的。

image.png