快速读取大文件的几种方式

转一篇:http://blog.csdn.net/fengxingzhe001/article/details/67640083

原来使用一行一行读取文本的方式,速度是慢的的可以,弄了好久还是不行,后来看了下才知道要用字节流传输会快很多

我自己也测了一下80M的文件,发现给读入块的大小会很明显的影响读入的速度。

测试代码如下:

def useBufferIStream(): Util = {
    try {
      val begin = System.currentTimeMillis
      val file = new File(s"E:\\data\\part-m-00000")
      val fis = new FileInputStream(file)
      val bis = new BufferedInputStream(fis)
      val buffer = new Array[Byte](1024*1024*90)
      var content = ""
      var cnt = 0
      cnt = bis.read(buffer)
      while( cnt != -1) {
        content += new String(buffer, 0, cnt)
        cnt=bis.read(buffer)
      }

      bis.close()
      println("=====BufferIStream===== time: " + (System.currentTimeMillis - begin) + "ms")

    } catch {
      case e: Exception =>
        // TODO Auto-generated catch block
        e.printStackTrace()
        println("error")
    }

  }

  代码中绿色部分为读入块的大小,目前设定的是90M大于要读的数据,这时的读入时间只要0.2s

  如果改为10M即(1024*1024*10),读入时间就需要10s左右,速度有很明显的变化。

  这里解释一下一部分代码:

  1、val buffer = new Array[Byte](1024*1024*90)  为每次读入文件的大小;

  2、cnt = bis.read(buffer) 读入数据块大小的标识,如果读入块没用信息则为-1,有信息则为这块信息的大小;

  3、content 为最终读入的文本信息

  4、这里使用的Scala语言,测试中发现

    cnt = bis.read(buffer)
      while( cnt != -1) {
        content += new String(buffer, 0, cnt)
        cnt=bis.read(buffer)
      }

  while的这块语句书写必须用这种形式,不能使用  while((cnt=fis.read(buffer)) != -1)  ,虽然在java上运行是都可以的,但是在Scala中,后者运行会报错,具体原因不明,应该跟Scala的一些机制有关

 

  上面代码能够解决基本的读取数据问题,但是无法保证数据分块读入时每一行数据是完整的,因此在前文基础上作出部分改动 

  代码如下:(实现将一个86G文件分解为90M的若干小文件,并保证每个小文件中每行数据的完整性)

def safeCopy():Unit={
try {
var size = 0
var count = 0
var tmp = -1
var tmp2 = 0
var stmp = new Array[Byte](1024*1024*90)
var content = ""
val fis = new FileInputStream("E:\\data\\part-m-00003")
//val fis = new FileInputStream("E:\\data\\test\\test.txt")
val bis = new BufferedInputStream(fis)
val buffer = new Array[Byte](1024*1024*90)
// val buffer = new Array[Byte](1024)
size = bis.read(buffer)
while (size != -1){
var fos = new FileOutputStream("E:\\data\\part3\\part_"+"%04d".format(count))
// var fos = new FileOutputStream("E:\\data\\test\\test_"+"%04d".format(count))
var bos = new BufferedOutputStream(fos)
tmp = findSize(buffer,size)
if(tmp>0) {
if (count != 0) {
bos.write(stmp, 0, tmp2)
}
tmp2 = size - tmp
Array.copy(buffer, tmp, stmp, 0, tmp2)
bos.write(buffer, 0, tmp-1)
}else{
bos.write(buffer, 0, size)
}
size = bis.read(buffer)
bos.flush()

println(s"finish $count")
count+=1
}
bis.close()
println("success!!")
} catch {
case e: Exception =>
e.printStackTrace()
println ("error!!")
}
}

  def findSize(buffer:Array[Byte],size:Int):Int={

var i=size-1
// println(size)
// println(i)
var j=1
var num = -1
while(i>=0 && j==1){
if(buffer(i)==13 || buffer(i)==10){
num = i
j=0
}
i-=1
}
num+1
}

  其中,findSize函数负责寻找每块文件中完整数据的长度,buffer(i)==10  其中的10,为换行符的Byte值(每一行数据以换行符作为结束)

 

新的方法,发现可以更为容易的解决上述问题

    public static void main(String[] args) throws IOException {
        String inputFile = "E:\\data\\part-m-00000";
        String outputFile = "E:\\data\\test01\\a-0";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(inputFile)));
        BufferedReader in = new BufferedReader(new InputStreamReader(bis,"utf-8"),60*1024*1024);
        FileWriter fw=new FileWriter(outputFile);
        int count = 0;
        int count1 = 0;
        Long start = System.currentTimeMillis();
        while(in.ready()){
            String line = in.readLine();
            count++;
            fw.append(line+"\n");
            if(count == 150000){
                count1++;
                fw.flush();
                fw.close();
                fw = new FileWriter("E:\\data\\test01\\a-"+count1);
                count =0;
                Long end = System.currentTimeMillis();
                System.out.println((end-start));
                start = System.currentTimeMillis();
            }

        }
        in.close();
        fw.flush();
        fw.close();
    }

  速度与前面不相上下,并且可以很好地解决按条读取的需求,大小通过控制条数的多少来实现。

 

posted @ 2017-09-29 14:11  LazyJoJo  阅读(6425)  评论(0编辑  收藏  举报