Java_io体系之RandomAccessFile简介、走进源码及示例——20

Java_io体系之RandomAccessFile简介、走进源码及示例——20

 

RandomAccessFile

1、       类功能简介:

 

       文件随机访问流、关心几个特点:

 

1、他实现的接口不再是InputStream、OutputStream中的任何一个、而是实现的DataInput、DataOutput。这也注定了他可以对java基础类型进行操作、而且是即可读取、也可以写入、关于这部分的方法大多数都是从DataInputStream、DataOutputStream那里荡来的。

 

2、如其名、随机流、这里的随机并不是不可控制、不可预测的去访问文件、而是可以通过指针的形式定位到具体的位置——“文件指针”、具体使用“文件指针”的方法:调用此流的seek(long n)方法来设置位置、使用此方法要注意的是如果传入的n大于文件长度、此方法不会改变文件长度、只是当写入下一个字节时会改变文件大小、即下一个写入的字节是添加在n后的。如果n小于文件长度、则会从n位置开始覆盖后面的file内容、

 

3、此流既可以读取文件、也可以写入文件、也可具有只读功能、但是没有只写功能、具体是哪种类型是在构造方法中指定的——String mode;具体的区别上面有说明。。

 

                 这里把实例放在了前面、因为源码和方法所占篇幅太长、有兴趣的可继续往下看。

 

2、       实例演示:

 

 

package com.chy.io.original.test;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileTests {
	
	private static final File file = new File("D:\\rsf.txt");
	
	/**
	 * 向文件中写入内容
	 */
	public static void testRandomAccessFileWriter() throws IOException{
		//要先将已有文件删除、避免干扰。
		if(file.exists()){
			file.delete();
		}
		
		RandomAccessFile rsfWriter = new RandomAccessFile(file, "rw");
		
		//不会改变文件大小、但是他会将下一个字符的写入位置标识为10000、也就是说此后只要写入内容、就是从10001开始存、
		rsfWriter.seek(10000);
		printFileLength(rsfWriter);		//result: 0
		
		//会改变文件大小、只是把文件的size改变、并没有改变下一个要写入的内容的位置、这里注释掉是为了验证上面的seek方法的说明内容
		//rsfWriter.setLength(10000);
		printFileLength(rsfWriter);		//result: 0
		
		//每个汉子占3个字节、写入字符串的时候会有一个记录写入字符串长度的两个字节
		rsfWriter.writeUTF("陈华应");	
		printFileLength(rsfWriter);		//result: 10011
		
		//每个字符占两个字节
		rsfWriter.writeChar(‘a‘);
		rsfWriter.writeChars("abcde");
		printFileLength(rsfWriter);		//result: 10023
		
		//再从“文件指针”为5000的地方插一个长度为100、内容全是‘a‘的字符数组
		//这里file长依然是10023、因为他是从“文件指针”为5000的地方覆盖后面的200个字节、下标并没有超过文件长度
		rsfWriter.seek(5000);
		char[] cbuf = new char[100];
		for(int i=0; i

 

3、 RandomAccessFile API简介:

A:构造方法

	RandomAccessFile(File file, String mode)     创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。 
	
	RandomAccessFile(String name, String mode)  	从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。 
补充一:mode的取值有下面四种情况
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw" 打开以便读取和写入。
"rws" 打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。
"rwd" 打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。
补充二:关于文件的“元数据”
The definition of metadata is "data about other data." With a file system, the data is contained in its files and directories, and the metadata tracks information about each of these objects: Is it a regular file, a directory, or a link? What is its size, creation date, last modified date, file owner, group owner, and access permissions?
补充三:"rw" "rws" "rwd"之间的区别
当操作的文件是存储在本地的基础存储设备上时(如硬盘, NandFlash等),"rws" 或 "rwd", "rw" 才有区别。
当模式是 "rws" 并且 操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]” 或 “修改文件元数据(如文件的mtime)”时,都会将这些改变同步到基础存储设备上。
当模式是 "rwd" 并且 操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]”时,都会将这些改变同步到基础存储设备上。
当模式是 "rw" 并且 操作的是基础存储设备上的文件;那么,关闭文件时,会将“文件内容的修改”同步到基础存储设备上。至于,“更改文件内容”时,是否会立即同步,取决于系统底层实现。
一般使用前两个就ok。

B:一般方法

	 void close()    关闭此随机访问文件流并释放与该流关联的所有系统资源。 
	 
	FileChannel getChannel()    返回与此文件关联的唯一 FileChannel 对象。 
	
	FileDescriptor getFD()    返回与此流关联的不透明文件描述符对象。 
	
	long getFilePointer()      返回此文件中的当前偏移量。 
	
	long length()     返回此文件的长度。 
	
	int read()     从此文件中读取一个数据字节。 
	
	int read(byte[] b)       将最多 b.length 个数据字节从此文件读入 byte 数组。 
	
	int read(byte[] b, int off, int len)      将最多 len 个数据字节从此文件读入 byte 数组。 
	
	boolean readBoolean()      从此文件读取一个 boolean。 
	
	byte readByte()     从此文件读取一个有符号的八位值。 
	
	char readChar()     从此文件读取一个字符。 
	
	double readDouble()     从此文件读取一个 double。 
	
	float readFloat()     从此文件读取一个 float。 
	
	void readFully(byte[] b)      将 b.length 个字节从此文件读入 byte 数组,并从当前文件指针开始。 
	
	void readFully(byte[] b, int off, int len)    将正好 len 个字节从此文件读入 byte 数组,并从当前文件指针开始。 
	
	int readInt()     从此文件读取一个有符号的 32 位整数。 
	
	String readLine()    从此文件读取文本的下一行。 
	
	long readLong()      从此文件读取一个有符号的 64 位整数。 
	
	short readShort()     从此文件读取一个有符号的 16 位数。 
	
	int readUnsignedByte()     从此文件读取一个无符号的八位数。 
	
	int readUnsignedShort()      从此文件读取一个无符号的 16 位数。 
	
	String readUTF()       从此文件读取一个字符串。 
	
	void seek(long pos)     设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。 
	
	void setLength(long newLength)     设置此文件的长度。 
	
	int skipBytes(int n)     尝试跳过输入的 n 个字节以丢弃跳过的字节。 
	
	void write(byte[] b)     将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。 
	
	void write(byte[] b, int off, int len)     将 len 个字节从指定 byte 数组写入到此文件,并从偏移量 off 处开始。 
	
	void write(int b)     向此文件写入指定的字节。 
	
	void writeBoolean(boolean v)     按单字节值将 boolean 写入该文件。 
	
	void writeByte(int v)     按单字节值将 byte 写入该文件。 
	
	void writeBytes(String s)     按字节序列将该字符串写入该文件。 
	
	void writeChar(int v)     按双字节值将 char 写入该文件,先写高字节。 
	
	void writeChars(String s)     按字符序列将一个字符串写入该文件。 
	
	void writeDouble(double v)     使用 Double 类中的 doubleToLongBits 方法将双精度参数转换为一个 long,然后按八字节数量将该 long 值写入该文件,先定高字节。 
	
	void writeFloat(float v)     使用 Float 类中的 floatToIntBits 方法将浮点参数转换为一个 int,然后按四字节数量将该 int 值写入该文件,先写高字节。 
	
	void writeInt(int v)      按四个字节将 int 写入该文件,先写高字节。 
	
	void writeLong(long v)     按八个字节将 long 写入该文件,先写高字节。 
	
	void writeShort(int v)      按两个字节将 short 写入该文件,先写高字节。 
	
	void writeUTF(String str)     使用 modified UTF-8 编码以与机器无关的方式将一个字符串写入该文件。 

4、 源码分析

 

package com.chy.io.original.code;

import java.io.Closeable;
import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UTFDataFormatException;
import java.nio.channels.FileChannel;

import sun.nio.ch.FileChannelImpl;


/**
 * 随机访问文件流、既可以对文件进行读取、也可以对文件进行写入、还可以写入java基础类型、不再是InputStream或者OutputStream的子类、
 * 而是实现了DataOutput、DataInput、通过这两个接口也可以看出、此类具有将java基础类型写入文件或者读取到程序中的功能、关于写入/读取文件基础类型
 * 的操作大多数都是剽窃DataInputStream/DataOutputStream的。
 */

public class RandomAccessFile implements DataOutput, DataInput, Closeable {
	java.io.RandomAccessFile
    private FileDescriptor fd;
    private FileChannel channel = null;
    private boolean rw;//标识此文件是否既可以读又可以写

    //标识不同mode对应的值
    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;

    /**
     * 使用指定的文件、模式构造RandomAccessFile、初始化参数、打开到文件的连接
     */
    public RandomAccessFile(String name, String mode)
	throws FileNotFoundException
    {
        this(name != null ? new File(name) : null, mode);
    }

    /**
     * 使用指定的文件、模式构造RandomAccessFile、初始化参数、打开到文件的连接
     */
    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  len) {
		    newpos = len;
		}
		seek(newpos);
	
		/* return the actual number of bytes skipped */
		return (int) (newpos - pos);
    }

    // ‘Write‘ primitives

    /**
     * 在当前“文件指针”标示的地方写入一个字节
     */
    public native void write(int b) throws IOException;

    /**
     * 将b的一部分写入到文件中
     */
    private native void writeBytes(byte b[], int off, int len) throws IOException;

    /**
     * 将b写入到文件中
     */
    public void write(byte b[]) throws IOException {
    	writeBytes(b, 0, b.length); 
    }

    /**
     * 将b的一部分写入到文件中
     */
    public void write(byte b[], int off, int len) throws IOException {
    	writeBytes(b, off, len);
    }

    // ‘Random access‘ stuff

    /**
     * 返回当前文件的偏移量、即“文件描述符”的位置
     */
    public native long getFilePointer() throws IOException;

    /**
     * 设置“文件指针”的偏移量、从文件的开头开始计数、如果pos大于文件的长度、也不会改变文件的大小、
     * 文件的大小只有在当“文件指针”指向文件最后的时候、再向文件中写入字节才会扩展。
     */
    public native void seek(long pos) throws IOException;

    /**
     * 返回文件的字节数
     */
    public native long length() throws IOException;

    /**
     * 设置文件长度:
     * 				if(newLength > originalLength)
     * 					扩展文件长度、新增加的长度使用默认值填充;
     * 				else
     * 					截取源文件的前originalLength字节、如果源文件的偏移量大于newLength、则将源文件的偏移量设为newLength;
     */
    public native void setLength(long newLength) throws IOException;

    /**
     * 关闭此流、释放与此流有关的所有资源
     */
    public void close() throws IOException {
        if (channel != null)
            channel.close();

        close0();
    }
    
    //  一些方法是从DataInputStream/DataOutputStream中剽窃来的。。。

    /**
     * 读取一个boolean型数据
     */
    public final boolean readBoolean() throws IOException {
		int ch = this.read();
		if (ch >> 8) & 0xFF);
	write((v >>> 0) & 0xFF);
	//written += 2;
    }

    /**
     * 将一个char写入文件
     */
    public final void writeChar(int v) throws IOException {
	write((v >>> 8) & 0xFF);
	write((v >>> 0) & 0xFF);
	//written += 2;
    }

    /**
     * 将一个int写入文件
     */
    public final void writeInt(int v) throws IOException {
	write((v >>> 24) & 0xFF);
	write((v >>> 16) & 0xFF);
	write((v >>>  8) & 0xFF);
	write((v >>>  0) & 0xFF);
	//written += 4;
    }

    /**
     * 将一个long写入文件
     */
    public final void writeLong(long v) throws IOException {
	write((int)(v >>> 56) & 0xFF);
	write((int)(v >>> 48) & 0xFF);
	write((int)(v >>> 40) & 0xFF);
	write((int)(v >>> 32) & 0xFF);
	write((int)(v >>> 24) & 0xFF);
	write((int)(v >>> 16) & 0xFF);
	write((int)(v >>>  8) & 0xFF);
	write((int)(v >>>  0) & 0xFF);
	//written += 8;
    }

    /**
     * 将一个long写入文件
     */
    public final void writeFloat(float v) throws IOException {
	writeInt(Float.floatToIntBits(v));
    }

    /**
     * 将一个double写入文件。
     */
    public final void writeDouble(double v) throws IOException {
	writeLong(Double.doubleToLongBits(v));
    }

    /**
     * 将一个字符串转换成一串有序字节写入
     */
    public final void writeBytes(String s) throws IOException {
	int len = s.length();
	byte[] b = new byte[len];
	s.getBytes(0, len, b, 0);
	writeBytes(b, 0, len);
    }

    /**
     * 将一个字符串以一串有序字符写入文件中
     */
    public final void writeChars(String s) throws IOException {
	int clen = s.length();
	int blen = 2*clen;
	byte[] b = new byte[blen];
	char[] c = new char[clen];
	s.getChars(0, clen, c, 0);
	for (int i = 0, j = 0; i >> 8);
	    b[j++] = (byte)(c[i] >>> 0);
	}
	writeBytes(b, 0, blen);
    }

    /**
     * 调用DataOutputStream的 writeUTF(String str, DataOutput out) 将一个字符串写入文件。
     */
    public final void writeUTF(String str) throws IOException {
        DataOutputStream.writeUTF(str, this);
    }

    private static native void initIDs();

    private native void close0() throws IOException;

    static {
    	initIDs();
    }

}

 

 

更多IO内容:java_io 体系之目录

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。