@Test
public void testRandomAccessFile() throws IOException {
// 根本没有必要纠结字符集的问题,直接二进制读写就OK
String fileName = "a.txt";
String add = "Random";
byte[] addBytes = add.getBytes();
RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
raf.seek(0);
// 读取 addBytes 长的字节出来
byte[] tempBytes = new byte[addBytes.length];
// read 之后会自动移指针,此时 tempBytes 也可能填不满
int firstLen = raf.read(tempBytes);
// 将需要添加的字符串添加到文件最前面
raf.seek(0);
raf.write(addBytes);
// 好麻烦,直接就用 addBytes 的长度作为每次读写的长度得了
byte[] buffer = new byte[tempBytes.length];
int len;
int tempLen = 0;
// 再读取一个数组
while ((len = raf.read(buffer)) != -1){
// 如果能读到,就回滚指针
raf.seek(raf.getFilePointer() - len);
// 把 tempBytes 里的字节写入
raf.write(tempBytes);
// 把 buffer 中的数据转存到 tempBytes
// 对象复制不能像这样:tempBytes = buffer; 这是把 tempBytes 变量指向了 buffer
// 不是将 buffer 的数据赋值给 tempBytes
tempBytes = buffer.clone();
tempLen = len;
}
System.out.println("len = " + len);
// len == -1 意味着到文件尾了,此时,tempBytes 还有数据没写入
// 如果 tempLen == 0 ,说明在 while 第一次 raf.read(buffer) 的时候就到末尾了
if (tempLen == 0){
// 写入最初的 tempBytes
raf.write(tempBytes,0,firstLen);
}
// 说明是第n次 raf.read(buffer) 才到末尾,此时 tempBytes 可能没有填满
// tempLen 不可能为 -1
if (tempLen != 0){
raf.write(tempBytes,0,tempLen);
}
// 释放资源
raf.close();
}
之前版本的效率太差了,1G的文件要接近20分钟才能完成添加,我不能接受。
改了一版,每次都读取64k,我用的是固态硬盘,1G的文件用时2秒搞定。
这里假设要添加的内容是小于64k,大于64k情况我懒得搞了。
@Test
public void testRandomAccessFile() throws IOException {
long begin = System.currentTimeMillis();
// 根本没有必要纠结字符集的问题,直接二进制读写就OK
String fileName = "E:\\test\\big_file_update.txt";
String add = "RandomAccessFile\n";
byte[] addBytes = add.getBytes();
// 每次读取都是64k
int byteLen = 64 * 1024;
long pointer = 0;
// 读指针、写指针算起来好麻烦,直接记在变量里面
long readPointer = 0;
long writePointer = 0;
RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
/// 获取文件原始长度
long fileLen = raf.length();
raf.seek(0);
// 读取 byteLen 长的字节出来
byte[] tempBytes = new byte[byteLen];
// read 之后会自动移指针,此时 tempBytes 也可能填不满,文件小于要添加的内容
int firstLen = raf.read(tempBytes);
readPointer = raf.getFilePointer();
// 将需要添加的字符串添加到文件最前面
raf.seek(0);
// 写入之后指针在 addBytes.length
raf.write(addBytes);
writePointer = raf.getFilePointer();
// 如果文件长度不大于byteLen,说明第一次读取已经读完了
if (fileLen <= byteLen){
// 写入收工
raf.write(tempBytes,0,firstLen);
raf.close();
return;
}
// 如果文件长度大于byteLen,说明后面还有,且 tempBytes 是读满的
// 读指针和写指针是必然要错开的
// 首先假设要添加的内容是小于byteLen的
byte[] buffer = new byte[byteLen];
int len;
int tempLen = tempBytes.length;
// 循环读取,写入
while (true){
raf.seek(readPointer);
len = raf.read(buffer);
readPointer = raf.getFilePointer();
// 如果readPointer == fileLen,说明文件已读完
if (readPointer == fileLen){
// 转到写指针
raf.seek(writePointer);
// 把 tempBytes 里的字节写入
raf.write(tempBytes,0,tempLen);
raf.write(buffer,0,len);
// 收工
break;
}
// 转到写指针
raf.seek(writePointer);
// 把 tempBytes 里的字节写入
raf.write(tempBytes,0,tempLen);
writePointer = raf.getFilePointer();
if (len == -1){break;}
// 把 buffer 中的数据转存到 tempBytes
tempBytes = buffer.clone();
tempLen = len;
}
// 释放资源
raf.close();
long end = System.currentTimeMillis();
System.out.println(end - begin);
}