文件随机访问流、关心几个特点:
1、他实现的接口不再是InputStream、OutputStream中的任何一个、而是实现的DataInput、DataOutput。这也注定了他可以对java基础类型进行操作、而且是即可读取、也可以写入、关于这部分的方法大多数都是从DataInputStream、DataOutputStream那里荡来的。
2、如其名、随机流、这里的随机并不是不可控制、不可预测的去访问文件、而是可以通过指针的形式定位到具体的位置——“文件指针”、具体使用“文件指针”的方法:调用此流的seek(long n)方法来设置位置、使用此方法要注意的是如果传入的n大于文件长度、此方法不会改变文件长度、只是当写入下一个字节时会改变文件大小、即下一个写入的字节是添加在n后的。如果n小于文件长度、则会从n位置开始覆盖后面的file内容、
3、此流既可以读取文件、也可以写入文件、也可具有只读功能、但是没有只写功能、具体是哪种类型是在构造方法中指定的——String mode;具体的区别上面有说明。。
这里把实例放在了前面、因为源码和方法所占篇幅太长、有兴趣的可继续往下看。
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<cbuf.length; i++){ cbuf[i] = 'a'; rsfWriter.writeChar(cbuf[i]); } printFileLength(rsfWriter); //result: 10023 //再从“文件指针”为1000的地方插入一个长度为100、内容全是a的字节数组 //这里file长依然是10023、因为他是从“文件指针”为5000的地方覆盖后面的200个字节、下标并没有超过文件长度 byte[] bbuf = new byte[100]; for (int i = 0; i < bbuf.length; i++) { bbuf[i] = 1; } rsfWriter.seek(1000); rsfWriter.writeBytes(new String(bbuf)); printFileLength(rsfWriter); } /** * 从文件中读取内容 * 这里我们要清楚现在文件中有什么内容、而且还要清楚这些内容起始字节下标、长度 * * @throws IOException */ public static void testRandomAccessFileRead() throws IOException{ /* * 对文件中内容简单说明: * 1、从0到1000 为空 * 2、从1001到1100是100个1 * 3、从1101到5000是空 * 4、从5001到5200是字符'a' * 5、从5201到10000是空 * 6、从10001到10011是字符串"陈华应" * 7、从10012到10023是"aabcde" */ RandomAccessFile rsfReader = new RandomAccessFile(file, "r"); //可按照自己想读取的东西所在的位置、长度来读取 //读取"陈华应" rsfReader.seek(10000); System.out.println(rsfReader.readUTF()); //读取100个字符'a' rsfReader.seek(5000); byte[] bbuf = new byte[200]; rsfReader.read(bbuf); System.out.println(new String(bbuf)); //读取100个1 byte[] bbuf2 = new byte[100]; rsfReader.seek(1000); rsfReader.read(bbuf2, 0, 100); for(byte b : bbuf2){ System.out.print(b); } //读取字符'aabcde' byte[] bbuf3 = new byte[12]; rsfReader.seek(10012); rsfReader.read(bbuf3); System.out.println(new String(bbuf3)); } /** * 打印文件长度 * @param rsfWriter 指向文件的随机文件流 * @throws IOException */ private static void printFileLength(RandomAccessFile rsfWriter) throws IOException { System.out.println("file length: " + rsfWriter.length() + " file pointer: " + rsfWriter.getFilePointer()); } public static void main(String[] args) throws IOException { testRandomAccessFileWriter(); testRandomAccessFileRead(); } }
RandomAccessFile(File file, String mode) 创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。 RandomAccessFile(String name, String mode) 从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。
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 编码以与机器无关的方式将一个字符串写入该文件。
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 < 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(); } fd = new FileDescriptor(); open(name, imode); } /** * 返回指定文件的“文件描述符” */ public final FileDescriptor getFD() throws IOException { if (fd != null) return fd; throw new IOException(); } /** * 打开唯一与此流有关的FileChannel */ public final FileChannel getChannel() { synchronized (this) { if (channel == null) channel = FileChannelImpl.open(fd, true, rw, this); return channel; } } /** * 以指定模式打开指定文件 */ private native void open(String name, int mode) throws FileNotFoundException; // 'Read' primitives /** * 读取下一个字节并以整数形式返回 */ public native int read() throws IOException; /** * 将一组字节读取到b中 */ private native int readBytes(byte b[], int off, int len) throws IOException; /** * 将一组字节读取到b中、调用上面的方法 */ public int read(byte b[], int off, int len) throws IOException { return readBytes(b, off, len); } /** * 将一组字节读取到b中 */ public int read(byte b[]) throws IOException { return readBytes(b, 0, b.length); } /** * 读取字节放入到b中、直到存满b或者文件结尾、或者异常才结束 * 与read(byte[] b)的区别是返回值。 */ public final void readFully(byte b[]) throws IOException { readFully(b, 0, b.length); } /** * 读取字节放入到下标从off开始的后len个位置的字节数组b中、直到存满b或者文件结尾、或者异常才结束 * 与read(byte[] b, int off, int len)的区别是返回值。 */ public final void readFully(byte b[], int off, int len) throws IOException { int n = 0; do { int count = this.read(b, off + n, len - n); if (count < 0) throw new EOFException(); n += count; } while (n < len); } /** * 跳过n个字节、返回实际跳过的字节、他是通过从定位“文件指针”来实现跨过要跳过的字节 */ public int skipBytes(int n) throws IOException { long pos; long len; long newpos; if (n <= 0) { return 0; } pos = getFilePointer(); len = length(); newpos = pos + n; if (newpos > 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 < 0) throw new EOFException(); return (ch != 0); } /** * 从此文件读取一个有符号的八位值。 */ public final byte readByte() throws IOException { int ch = this.read(); if (ch < 0) throw new EOFException(); return (byte)(ch); } /** * 从此文件读取一个无符号的八位数。 */ public final int readUnsignedByte() throws IOException { int ch = this.read(); if (ch < 0) throw new EOFException(); return ch; } /** * 从此文件读取一个有符号的 16 位数。 */ public final short readShort() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (short)((ch1 << 8) + (ch2 << 0)); } /** * 从此文件读取一个无符号的 16 位数。 */ public final int readUnsignedShort() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (ch1 << 8) + (ch2 << 0); } /** * 从此文件读取一个字符。 */ public final char readChar() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (char)((ch1 << 8) + (ch2 << 0)); } /** * 从此文件读取一个有符号的 32 位整数。 */ public final int readInt() throws IOException { int ch1 = this.read(); int ch2 = this.read(); int ch3 = this.read(); int ch4 = this.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); } /** * 从此文件读取一个有符号的 64 位整数。 */ public final long readLong() throws IOException { return ((long)(readInt()) << 32) + (readInt() & 0xFFFFFFFFL); } /** * 从此文件读取一个 float。 */ public final float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } /** * 从此文件读取一个 double。 */ public final double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); } /** * 从此文件读取文本的下一行。 */ public final String readLine() throws IOException { StringBuffer input = new StringBuffer(); int c = -1; boolean eol = false; while (!eol) { switch (c = read()) { case -1: case '\n': eol = true; break; case '\r': eol = true; long cur = getFilePointer(); if ((read()) != '\n') { seek(cur); } break; default: input.append((char)c); break; } } if ((c == -1) && (input.length() == 0)) { return null; } return input.toString(); } /** * 从此文件读取一个字符串。 */ public final String readUTF() throws IOException { return DataInputStream.readUTF(this); } /** * 将一boolean型数据写入文件中 */ public final void writeBoolean(boolean v) throws IOException { write(v ? 1 : 0); //written++; } /** * 将一个字节写入文件中 */ public final void writeByte(int v) throws IOException { write(v); //written++; } /** * 将一个short写入文件 */ public final void writeShort(int v) throws IOException { write((v >>> 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 < clen; i++) { b[j++] = (byte)(c[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(); } }