快速读取大文件的几种方式
转一篇: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(); }
速度与前面不相上下,并且可以很好地解决按条读取的需求,大小通过控制条数的多少来实现。