一、RandomAccessFile简介
RandomAccessFile 既可以读取文件内容,也可以向文件输出数据。同时,RandomAccessFile 支持“随机访问”的方式,程序快可以直接跳转到文件的任意地方来读写数据。
由于RandomAccessFile可以自由访问文件的任意位置,
所以如果需要访问文件的部分内容,而不是把文件从头读到尾,使用 RandomAccessFile 将是更好的选择。
与OutputStream、Writer等输出流不同的是,RandomAccessFile允许自由定义文件记录指针,RandomAccessFile可以不从开始的地方开始输出,因此RandomAccessFile可以向已存在的文件后追加内容。
如果程序需要向已存在的文件后追加内容,则应该使用RandomAccessFile。
RandomAccessFile的方法虽然多,但它有一个最大的局限,就是只能读写文件,不能读写其他IO节点。
RandomAccessFile的一个重要使用场景就是网络请求中的 多线程下载 及 断点续传。
2.RandomAccessFile的重要方法
RandomAccessFile 既可以读文件,也可以写文件,所以类似于InputStream的read()方法,以及类似于OutputStream的write()方法,RandomAccessFile都具备。除此之外,RandomAccessFile具备两个特有的方法,来支持其随机访问的特性。
RandomAccessFile对象包含了一个记录指针,用以标识当前读写处的位置,当程序新创建一个RandomAccessFile对象时,该对象的文件指针记录位于文件头(也就是0处),当读/写了n个字节后,文件记录指针将会后移n个字节。除此之外,RandomAccessFile还可以自由移动该记录指针。下面就是RandomAccessFile具有的两个特殊方法,来操作记录指针,实现随机访问:
long getFilePointer( ); // 返回文件记录指针的当前位置
void seek(long pos ); // 将文件指针定位到pos位置
其他方法:
// 指定文件的光标位置,通俗点说就是指定你的光标位置,然后下次读文件数据的时候从该位置读取。
1、seek()
// 我们注意到这是一个long类型的返回值,字面意思就是返回当前的文件光标位置。这样方便我们后面读取插入。
2、getFilePointer()
// 毫无疑问的方法,文件的长度,返回long类型。注意它并不会受光标的影响。只会反应客观的文本长度。
3、length()
// 这些方法跟readstream中的方法一样,例如最后一个:定义缓冲数组,从数组的off偏移量位置开始写,读取转换为数组数据达到len个字节。总之这是一个读文件内容的标准操作api。
4、read()、read(byte[] b)、read(byte[] b,int off,int len)
// 这些方法都是去read | write 每一个字符,个人感觉就是返回他们的ASCII码
当然如果专家们有异议可以指出,我测试的时候至少是这么感觉得。
大家也可以自己试一下。比如readLong就是要求你的文本内容必须有八个字符,不然会报错。
5、readDouble()、 readFloat()、readBoolean()、 readInt()
readLong()、 readShort()、 readByte()、 readChar()
writeDouble()、 writeFloat()、 writeBoolean()、 writeInt()
writeLong()、 writeShort() 、writeByte()、 writeChar()
// 这个方法的作用就是将文本中的内容填满这个缓冲区b。如果缓冲b不能被填满,那么读取流的过程将被阻塞,如果发现是流的结尾,那么会抛出异常。这个过程就比较像“凑齐一车人在发车,
不然不走”。
6、readFully(byte[] b):
// 它返回的就是nio通信中的file的唯一channel, FileChannel
7、getChannel()
// 跳过n字节的位置,相对于当前的point。
8、skipBytes(int n);
三、RandomAccessFile的使用
1:多线程写文件
利用 RandomAccessFile 实现文件的多线程下载,即多线程下载一个文件时,将文件分成几块,每块用不同的线程进行下载。下面是一个利用多线程在写文件时的例子,其中预先分配文件所需要的空间,然后在所分配的空间中进行分块,然后写入:
* 测试利用多线程进行文件的写操作
public
class
Test {
public
static
void
main(String[] args)
throws
Exception {
//
预分配文件所占的磁盘空间,磁盘中会创建一个指定大小的文件
RandomAccessFile raf =
new
RandomAccessFile("D://abc.txt", "rw"
);
raf.setLength(
1024*1024);
//
预分配 1M 的文件空间
raf.close();
//
所要写入的文件内容
String s1 = "第一个字符串"
;
String s2
= "第二个字符串"
;
String s3
= "第三个字符串"
;
String s4
= "第四个字符串"
;
String s5
= "第五个字符串"
;
//
利用多线程同时写入一个文件
new
FileWriteThread(1024*1,s1.getBytes()).start();
//
从文件的1024字节之后开始写入数据
new
FileWriteThread(1024*2,s2.getBytes()).start();
//
从文件的2048字节之后开始写入数据
new
FileWriteThread(1024*3,s3.getBytes()).start();
//
从文件的3072字节之后开始写入数据
new
FileWriteThread(1024*4,s4.getBytes()).start();
//
从文件的4096字节之后开始写入数据
new
FileWriteThread(1024*5,s5.getBytes()).start();
//
从文件的5120字节之后开始写入数据
//
利用线程在文件的指定位置写入指定数据
static
class
FileWriteThread
extends
Thread{
private
int
skip;
private
byte
[] content;
public
FileWriteThread(
int
skip,
byte
[] content){
this
.skip =
skip;
this
.content =
content;
public
void
run(){
RandomAccessFile raf
=
null
;
try
{
raf
=
new
RandomAccessFile("D://abc.txt", "rw"
);
raf.seek(skip);
raf.write(content);
}
catch
(FileNotFoundException e) {
e.printStackTrace();
}
catch
(IOException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
try
{
raf.close();
}
catch
(Exception e) {
2、使用RandomAccessFile实现从指定位置读取文件的功能
public static void main(String[] args)throws IOException {
String filePath="E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt";
RandomAccessFile raf=null;
File file=null;
try {
file=new File(filePath);
raf=new RandomAccessFile(file,"r");
// 获取 RandomAccessFile对象文件指针的位置,初始位置为0
System.out.print("输入内容:"+raf.getFilePointer());
//移动文件记录指针的位置
raf.seek(1000);
byte[] b=new byte[1024];
int hasRead=0;
//循环读取文件
while((hasRead=raf.read(b))>0){
//输出文件读取的内容
System.out.print(new String(b,0,hasRead));
}catch (IOException e){
e.printStackTrace();
}finally {
raf.close();
3、使用RandomAccessFile实现向文件中追加内容的功能
public class RandomAccessFileTest2 {
public static void main(String[] args)throws IOException {
String filePath="E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt";
RandomAccessFile raf=null;
File file=null;
try {
file=new File(filePath);
// 以读写的方式打开一个RandomAccessFile对象
raf=new RandomAccessFile(file,"rw");
//将记录指针移动到该文件的最后
raf.seek(raf.length());
//向文件末尾追加内容
raf.writeChars("这是追加内容。。");
}catch (IOException e){
e.printStackTrace();
}finally {
raf.close();
4、使用RandomAccessFile实现向文件指定位置插入内容的功能
注:RandomAccessFile 不能向文件的指定位置插入内容,如果直接将文件记录指针移动到中间某位置后开始输出,则新输出的内容会覆盖文件原有的内容,如果需要向指定位置插入内容,程序需要先把插入点后面的内容写入缓存区,等把需要插入的数据写入到文件后,再将缓存区的内容追加到文件后面。
* 插入文件指定位置的指定内容
* @param filePath 文件路径
* @param pos 插入文件的指定位置
* @param insertContent 插入文件中的内容
* @throws IOException
public static void insert(String filePath,long pos,String insertContent)throws IOException{
RandomAccessFile raf=null;
File tmp=File.createTempFile("tmp",null);
tmp.deleteOnExit();
try {
// 以读写的方式打开一个RandomAccessFile对象
raf = new RandomAccessFile(new File(filePath), "rw");
//创建一个临时文件来保存插入点后的数据
FileOutputStream fileOutputStream = new FileOutputStream(tmp);
FileInputStream fileInputStream = new FileInputStream(tmp);
//把文件记录指针定位到pos位置
raf.seek(pos);
raf.seek(pos);
//------下面代码将插入点后的内容读入临时文件中保存-----
byte[] bbuf = new byte[64];
//用于保存实际读取的字节数据
int hasRead = 0;
//使用循环读取插入点后的数据
while ((hasRead = raf.read(bbuf)) != -1) {
//将读取的内容写入临时文件
fileOutputStream.write(bbuf, 0, hasRead);
//-----下面代码用于插入内容 -----
//把文件记录指针重新定位到pos位置
raf.seek(pos);
//追加需要插入的内容
raf.write(insertContent.getBytes());
//追加临时文件中的内容
while ((hasRead = fileInputStream.read(bbuf)) != -1) {
//将读取的内容写入临时文件
raf.write(bbuf, 0, hasRead);
}catch (Exception e){
throw e;
public static void main(String[] args)throws IOException {
String filePath="E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt";
insert(filePath,1000,"插入指定位置指定内容");