Java IO体系之RandomAccessFile

介绍

RandomAccessFile是Java输入/输出流体系中功能最丰富的文件内容访问类,既可以读取文件内容,也可以向文件输出数据。与普通的输入/输出流不同的是,RandomAccessFile支持跳到文件任意位置读写数据,RandomAccessFile对象包含一个记录指针,用以标识当前读写处的位置,当程序创建一个新的RandomAccessFile对象时,该对象的文件记录指针对于文件头(也就是0处),当读写n个字节后,文件记录指针将会向后移动n个字节。除此之外,RandomAccessFile可以自由移动该记录指针

  • RandomAccessFile包含两个方法来操作文件记录指针
long getFilePointer()//返回文件记录指针的当前位置
void seek(long pos)//将文件记录指针定位到pos位置
  • RandomAccessFile类在创建对象时,除了指定文件本身,还需要指定一个mode参数,该参数指定RandomAccessFile的访问模式,该参数有如下四个值:
r:以只读方式打开指定文件。如果试图对该RandomAccessFile指定的文件执行写入方法则会抛出IOException
rw:以读取、写入方式打开指定文件。如果该文件不存在,则尝试创建文件
rws:以读取、写入方式打开指定文件。相对于rw模式,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备,默认情形下(rw模式下),是使用buffer的,只有cache满的或者使用RandomAccessFile.close()关闭流的时候儿才真正的写到文件
rwd:与rws类似,只是仅对文件的内容同步更新到磁盘,而不修改文件的元数据

源码解析

继承关系

public class RandomAccessFile implements DataOutput, DataInput, Closeable

成员变量

private FileDescriptor fd;
private FileChannel channel = null;
private boolean rw;

/**
 * The path of the referenced file
 * (null if the stream is created with a file descriptor)
 */
private final String path;

private Object closeLock = new Object();
private volatile boolean closed = false;

// 以只读方式打开指定文件
private static final int O_RDONLY = 1;
// 以读取、写入方式打开指定文件
private static final int O_RDWR =   2;
// 以读取、写入方式打开指定文件,并对文件的内容或元数据的每个更新都同步写入到底层存储设备
private static final int O_SYNC =   4;
private static final int O_DSYNC =  8;

构造函数

/**
 * Creates a random access file stream to read from, and optionally
 * to write to, a file with the specified name.
 */
public RandomAccessFile(String name, String mode)
    throws FileNotFoundException
{
    this(name != null ? new File(name) : null, mode);
}

/**
 * Creates a random access file stream to read from, and optionally to
 * write to, the file specified by the {@link File} argument.  A new {@link
 * FileDescriptor} object is created to represent this file connection.
 */
public RandomAccessFile(File file, String mode)
    throws FileNotFoundException
{
    String name = (file != null ? file.getPath() : null);
    int imode = -1;
    if (mode.equals("r"))
        imode = O_RDONLY;
    else if (mode.startsWith("rw")) {
        imode = O_RDWR;
        rw = true;
        if (mode.length() > 2) {
            if (mode.equals("rws"))
                imode |= O_SYNC;
            else if (mode.equals("rwd"))
                imode |= O_DSYNC;
            else
                imode = -1;
        }
    }
    if (imode < 0)
        throw new IllegalArgumentException("Illegal mode \"" + mode
                                           + "\" must be one of "
                                           + "\"r\", \"rw\", \"rws\","
                                           + " or \"rwd\"");
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkRead(name);
        if (rw) {
            security.checkWrite(name);
        }
    }
    if (name == null) {
        throw new NullPointerException();
    }
    if (file.isInvalid()) {
        throw new FileNotFoundException("Invalid file path");
    }
    fd = new FileDescriptor();
    fd.attach(this);
    path = name;
    open(name, imode);
}

成员方法

/**
* Returns the current offset in this file.
* 返回文件记录指针的当前位置
*/
public native long getFilePointer() throws IOException;

/**
* Sets the file-pointer offset, measured from the beginning of this
* file, at which the next read or write occurs. 
* 将文件记录指针定位到pos的位置
*/
public void seek(long pos) throws IOException {
   if (pos < 0) {
       throw new IOException("Negative seek offset");
   } else {
       seek0(pos);
   }
}

/**
* Reads a byte of data from this file. The byte is returned as an
* integer in the range 0 to 255 ({@code 0x00-0x0ff}). This
* method blocks if no input is yet available.
*/
public int read() throws IOException {
   return read0();
}

private native int read0() throws IOException;

/**
 * Writes the specified byte to this file. The write starts at
 * the current file pointer.
 *
 * @param      b   the {@code byte} to be written.
 * @exception  IOException  if an I/O error occurs.
 */
public void write(int b) throws IOException {
    write0(b);
}

private native void write0(int b) throws IOException;


/**
 * Writes a string to the file using
 * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
 * encoding in a machine-independent manner.
 */
public final void writeUTF(String str) throws IOException {
    DataOutputStream.writeUTF(str, this);
}

测试DEMO

源码

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;

/*
 * 参考文档:
 * https://www.cnblogs.com/zuochengsi-9/p/6485737.html
*/
	
public class RandomAccessFileTest {

	/**
	 * 读的方法
	 * @param path
	 *            文件路径
	 * @param pointer
	 *            指针位置
	 **/
	public static void randomRed(String path, int pointer) {
		try {
			/** 以只读的方式建立一个RandomAccessFile对象 **/
			RandomAccessFile raf = new RandomAccessFile(path, "r");
			// 获取RandomAccessFile对象文件指针的位置,初始位置是0
			raf.seek(pointer);// 移动文件指针位置
			byte[] buff = new byte[1024];
			// 用于保存实际读取的字节数
			int hasRead = 0;
			// 循环读取
			while ((hasRead = raf.read(buff)) > 0) {
				// 打印读取的内容,并将字节转为字符串输入
				System.out.println(new String(buff, 0, hasRead));

			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * 追加方式 写的方法
	 * @param path
	 *            文件路径
	 ***/
	public static void randomWrite(String path,String content) {
		try {
			/** 以读写的方式建立一个RandomAccessFile对象 **/
			RandomAccessFile raf = new RandomAccessFile(path, "rw");

			// 将记录指针移动到文件最后
			raf.seek(raf.length());
			raf.write(content.getBytes());

		} catch (Exception e) {
			e.printStackTrace();
		}

	}
	
	/** 
     * 实现向指定位置插入数据 
     * @param fileName 文件名 
     * @param points 指针位置 
     * @param insertContent 插入内容 
     * **/  
    public static void insert(String fileName,long points,String insertContent){  
        try{  
        File tmp=File.createTempFile("tmp", null);  
        tmp.deleteOnExit();//在JVM退出时删除  
          
        RandomAccessFile raf=new RandomAccessFile(fileName, "rw");  
        //创建一个临时文件夹来保存插入点后的数据  
        FileOutputStream tmpOut=new FileOutputStream(tmp);  
        FileInputStream tmpIn=new FileInputStream(tmp);  
        raf.seek(points);  
        /**将插入点后的内容读入临时文件夹**/  
          
        byte [] buff=new byte[1024];  
        //用于保存临时读取的字节数  
        int hasRead=0;  
        //循环读取插入点后的内容  
        while((hasRead=raf.read(buff))>0){  
            // 将读取的数据写入临时文件中  
            tmpOut.write(buff, 0, hasRead);  
        }  
          
        //插入需要指定添加的数据  
        raf.seek(points);//返回原来的插入处  
        //追加需要追加的内容  
        raf.write(insertContent.getBytes());  
        //最后追加临时文件中的内容  
        while((hasRead=tmpIn.read(buff))>0){  
            raf.write(buff,0,hasRead);  
        }  
        }catch(Exception e){  
            e.printStackTrace();  
        }  
    }  

	public static void main(String[] args) {
		StringBuffer buf = new StringBuffer();
		buf.append("// ========================================测试只读方法start========================");
		System.out.println(buf.toString());
		
		// ========================================测试只读方法start========================
		String path = "D:\\happy-test.txt";
		// the offset position, measured in bytes from the beginning of the file, at which to set the file pointer.
		int seekpointer = 0; // 可以是指定的开始位置,例如80
		randomRed(path, seekpointer); 
		// ========================================测试只读finish========================
		
		buf.setLength(0);
		buf.append("// ========================================测试只读finish========================");
		System.out.println(buf.toString());
		
		buf.setLength(0);
		buf.append("// ========================================测试追加方法start========================");
		System.out.println(buf.toString());
		
		//\r\n换行
		// ========================================测试追加方法start========================
		randomWrite(path,"\r\nLife is half spent before we know what it is."); 
		
		seekpointer = 0;  
		randomRed(path, seekpointer); 
		// ========================================测试追加方法finish========================
		
		buf.setLength(0);
		buf.append("// ========================================测试追加方法finish========================");
		System.out.println(buf.toString());
		
		buf.setLength(0);
		buf.append("// ========================================测试在任意位置追加方法start========================");
		System.out.println(buf.toString());
		
		// ========================================测试在任意位置追加方法start========================
		insert(path, 66, "\r\nApril Showers Bring May Flowers.\r\n"); 
		seekpointer = 0;  
		randomRed(path, seekpointer); 
		// ========================================测试在任意位置追加方法finish========================
		
		buf.setLength(0);
		buf.append("// ========================================测试在任意位置追加方法finish========================");
		System.out.println(buf.toString());
	
	}

}

运行结果

// ========================================测试只读方法start========================
个性模块
Welcome to My GitHub
点击GitHub
Well-known saying
Life itself is the most wonderful fairy tale.
生活本身就是最美妙的童话故事。
FIFA World Cup
Life is full of ups and downs.
人生真是充满跌宕起伏。
// ========================================测试只读finish========================
// ========================================测试追加方法start========================
个性模块
Welcome to My GitHub
点击GitHub
Well-known saying
Life itself is the most wonderful fairy tale.
生活本身就是最美妙的童话故事。
FIFA World Cup
Life is full of ups and downs.
人生真是充满跌宕起伏。
Life is half spent before we know what it is.
// ========================================测试追加方法finish========================
// ========================================测试在任意位置追加方法start========================
个性模块
Welcome to My GitHub
点击GitHub
Well-known saying
Lif
April Showers Bring May Flowers.
e itself is the most wonderful fairy tale.
生活本身就是最美妙的童话故事。
FIFA World Cup
Life is full of ups and downs.
人生真是充满跌宕起伏。
Life is half spent before we know what it is.
// ========================================测试在任意位置追加方法finish========================
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值