今天自己总结一下 RandomAccessFile:
出错一些例子就从网上直接拿来了Java的RandomAccessFile提供对文件的读写功能,与普通的输入输出流不一样的是RamdomAccessFile可以任意的访问文件的任何地方。
RandomAccessFile的对象包含一个记录指针,用于标识当前流的读写位置,这个位置可以向前移动,也可以向后移动。RandomAccessFile包含两个方法来操作文件记录指针。
long getFilePoint():记录文件指针的当前位置。
void seek(long pos):将文件记录指针定位到pos位置。
RandomAccessFile包含InputStream的三个read方法,也包含OutputStream的三个write方法。同时RandomAccessFile还包含一系列的readXxx和writeXxx方法完成输入输出。
RandomAccessFile不属于InputStream和OutputStream类系的。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫不相干,甚至不使用InputStream和OutputStream类中已经存在的任何功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。
RandomAccessFile的绝大多数功能,但不是全部,已经被JDK 1.4的nio的"内存映射文件(memory-mapped files)"给取代了,你该考虑一下是不是用"内存映射文件"来代替RandomAccessFile了。
- publicclassTestRandomAccessFile{
- publicstaticvoidmain(String[]args)throwsIOException{
- RandomAccessFilerf=newRandomAccessFile("rtest.dat","rw");
- for(inti=0;i<10;i++){
- //写入基本类型double数据
- rf.writeDouble(i*1.47777);
- }
- rf.close();
- rf=newRandomAccessFile("rtest.dat","rw");
- //直接将文件指针移到第5个double数据后面
- rf.seek(5*8);
- //覆盖第6个double数据
- rf.writeDouble(47.0001);
- rf.close();
- rf=newRandomAccessFile("rtest.dat","r");
- for(inti=0;i<10;i++){
- System.out.println("Value"+i+":"+rf.readDouble());
- }
- rf.close();
- }
- }
内存映射文件
内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件。有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问。这种解决办法能大大简化修改文件的代码。
fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中。注意,你必须指明,它是从文件的哪个位置开始映射的,映射的范围又有多大;也就是说,它还可以映射一个大文件的某个小片断。
MappedByteBuffer是ByteBuffer的子类,因此它具备了ByteBuffer的所有方法,但新添了force()将缓冲区的内容强制刷新到存储设备中去、load()将存储设备中的数据加载到内存中、isLoaded()位置内存中的数据是否与存储设置上同步。这里只简单地演示了一下put()和get()方法,除此之外,你还可以使用asCharBuffer( )之类的方法得到相应基本类型数据的缓冲视图后,可以方便的读写基本类型数据。
- publicclassLargeMappedFiles{
- staticintlength=0x8000000;//128Mb
- publicstaticvoidmain(String[]args)throwsException{
- //为了以可读可写的方式打开文件,这里使用RandomAccessFile来创建文件。
- FileChannelfc=newRandomAccessFile("test.dat","rw").getChannel();
- //注意,文件通道的可读可写要建立在文件流本身可读写的基础之上
- MappedByteBufferout=fc.map(FileChannel.MapMode.READ_WRITE,0,length);
- //写128M的内容
- for(inti=0;i<length;i++){
- out.put((byte)'x');
- }
- System.out.println("Finishedwriting");
- //读取文件中间6个字节内容
- for(inti=length/2;i<length/2+6;i++){
- System.out.print((char)out.get(i));
- }
- fc.close();
- }
- }
尽管映射写似乎要用到FileOutputStream,但是映射文件中的所有输出必须使用RandomAccessFile,但如果只需要读时可以使用FileInputStream,写映射文件时一定要使用随机访问文件,可能写时要读的原因吧。
该程序创建了一个128Mb的文件,如果一次性读到内存可能导致内存溢出,但这里访问好像只是一瞬间的事,这是因为,真正调入内存的只是其中的一小部分,其余部分则被放在交换文件上。这样你就可以很方便地修改超大型的文件了(最大可以到2 GB)。注意,Java是调用操作系统的"文件映射机制"来提升性能的。
- /*
- *程序功能:演示了RandomAccessFile类的操作,同时实现了一个文件复制操作。
- */
- packagecom.lwj.demo;
- importjava.io.*;
- publicclassRandomAccessFileDemo{
- publicstaticvoidmain(String[]args)throwsException{
- RandomAccessFilefile=newRandomAccessFile("file","rw");
- //以下向file文件中写数据
- file.writeInt(20);//占4个字节
- file.writeDouble(8.236598);//占8个字节
- file.writeUTF("这是一个UTF字符串");//这个长度写在当前文件指针的前两个字节处,可用readShort()读取
- file.writeBoolean(true);//占1个字节
- file.writeShort(395);//占2个字节
- file.writeLong(2325451l);//占8个字节
- file.writeUTF("又是一个UTF字符串");
- file.writeFloat(35.5f);//占4个字节
- file.writeChar('a');//占2个字节
- file.seek(0);//把文件指针位置设置到文件起始处
- //以下从file文件中读数据,要注意文件指针的位置
- System.out.println("——————从file文件指定位置读数据——————");
- System.out.println(file.readInt());
- System.out.println(file.readDouble());
- System.out.println(file.readUTF());
- file.skipBytes(3);//将文件指针跳过3个字节,本例中即跳过了一个boolean值和short值。
- System.out.println(file.readLong());
- file.skipBytes(file.readShort());//跳过文件中“又是一个UTF字符串”所占字节,注意readShort()方法会移动文件指针,所以不用加2。
- System.out.println(file.readFloat());
- //以下演示文件复制操作
- System.out.println("——————文件复制(从file到fileCopy)——————");
- file.seek(0);
- RandomAccessFilefileCopy=newRandomAccessFile("fileCopy","rw");
- intlen=(int)file.length();//取得文件长度(字节数)
- byte[]b=newbyte[len];
- file.readFully(b);
- fileCopy.write(b);
- System.out.println("复制完成!");
- }
- }
RandomAccessFile 插入写示例:
- publicstaticvoidinsert(longskip, StringfileName){
- try{
- RandomAccessFileraf=newRandomAccessFile(fileName,"rw");
- if(skip<0||skip>raf.length()){
- System.out.println("跳过字节数无效");
- return;
- }
-
- byte[]b= "插入的内容".getBytes();
- raf.setLength(raf.length()+b.length);
-
- for(longi=raf.length()-1;i>b.length+skip-1;i--){
- raf.seek(i-b.length);
- bytetemp=raf.readByte();
- raf.seek(i);
- raf.writeByte(temp);
- }
-
- raf.seek(skip);
- raf.write(b);
- raf.close();
- }catch(Exceptione){
- e.printStackTrace();
- }
- }
利用RandomAccessFile实现文件的多线程下载,即多线程下载一个文件时,
package com.lilei.duo;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class MulThreadDownload {
public static void main(String[] args) throws Exception {
String path ="http://192.168.0.101:8080/House/java51.rar";
new MulThreadDownload().download(path, 3);
}
private void download(String path , int threadsize) throws Exception{
path ="http://192.168.0.101:8080/House/java51.rar";
URL url = new URL(path);
HttpURLConnection conn =(HttpURLConnection)url.openConnection();
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
if(conn.getResponseCode() == 200){
// 1. 得到 服务器返回的文件长度
long length = conn.getContentLength();
//随机访问文件类 生成1个和 网络文件相同的 文件 不写路径 默认保存在当前程序下
File f = new File("d:\\java11.rar");
// 第2个参数 模式 r只读打开 rw读写打开 rws立刻同步到本地 rwd 只把文件的数据 同步。。不会同步原数据
RandomAccessFile accessFile = new RandomAccessFile(f, "rwd" );
//设置长度 和网络长度相等
accessFile.setLength(length);
//计算负责 下载的数据量
int block = (int)(length%threadsize == 0 ? length/ threadsize : length/ threadsize +1);
for (int i = 0; i < threadsize ; i++) {
int threadid=i;
new DownloadThread( i , block , url , f ).start();
}
}else{
System.out.println("下载失败");
}
}
private class DownloadThread extends Thread{
private int threadid;
private int block;
private URL url;
private File file;
public DownloadThread(int threadid , int block ,URL url ,File file){
this.threadid=threadid;
this.block = block;
this.url=url;
this.file=file;
}
@Override
public void run() {
int start = threadid * block ; // 从多少字节开始下载
int end = (threadid+1) * block -1; // 多少结束
try {
RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");//因为文件存在了 所以只会构造对象
//将 对应的 指针 指向 对应的位置 准备开始读取
accessFile.seek(start);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
// 设置头信息 指定 从网络区域 哪里开始干什么。。。 "Range" 组拼协议
conn.setRequestProperty("Range", "bytes="+ start + "-" + end);
if(conn.getResponseCode() == 206){
InputStream in = conn.getInputStream();
byte[] b = new byte[1000];
int len =0 ;
while( (len = in.read(b))!= -1){
// assess提供了 写方法
accessFile.write( b, 0, len);
}
accessFile.close();
in.close();
}
System.out.println(threadid+"线程 下载完成");
} catch (Exception e) {
e.printStackTrace();
}
}
}