All Implemented Interfaces:
DataInput, DataOutput
A random access file behaves like a large array of bytes stored in the file system
read bytes starting at the file pointer and advance the file pointer past the bytes read.
write bytes starting at the file pointer and advance the file pointer past the bytes written
通过api的介绍可知,read或者write操作,完成后指针都会跳到下一个字节位置。
It is generally true of all the reading routines in this class that if end-of-file is reached before the desired number of bytes has been read, an EOFException
(which is a kind of IOException
) is thrown. If any byte cannot be read for any reason other than end-of-file, an IOException
other than EOFException
is thrown. In particular, an IOException
may be thrown if the stream has been closed.
EOFException/IOException. 前者是到达了文件末尾了还读则会抛出,后者是读的过程中出现了其他的问题抛出。
public class TestRandomAccessFile {
public static void main(String[] args) throws IOException {
RandomAccessFile rf = new RandomAccessFile("rtest.dat", "rw");
for (int i = 0; i < 10; i++) {
// 写入基本类型double数据
rf.writeDouble(i * 1.414);
}
rf.close();
rf = new RandomAccessFile("rtest.dat", "rw");
// 直接将文件指针移到第5个double数据后面
rf.seek(5 * 8);
// 覆盖第6个double数据
rf.writeDouble(47.0001);
rf.close();
rf = new RandomAccessFile("rtest.dat", "r");
for (int i = 0; i < 10; i++) {
System.out.println("Value " + i + ": " + rf.readDouble());
}
rf.close();
}
}
----------------------
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileTest {
public static void main(String[] args) {
System.out.println(System.getProperty("user.dir"));//test
try {
//RandomAccessFile raf = new RandomAccessFile("raf2/raf.log","rw");//cannot make dir.
RandomAccessFile raf = new RandomAccessFile("raf.txt","rw");// can make file.
for (int i = 0; i < 10; i++) {
raf.writeDouble((i+1)*1.414);
}
raf.close();
// //test
// raf = new RandomAccessFile("raf.txt","rw");
// for (int i = 0; i < raf.length() ; i++) {
// System.out.println(raf.readDouble());
// }
// raf.close();
raf = new RandomAccessFile("raf.txt","rw");
System.out.println("FilePointer location: "+raf.getFilePointer());
raf.seek(5*8);
System.out.println(raf.readDouble());
raf.writeDouble(3.1415926);
raf.close();
raf = new RandomAccessFile("raf.txt","r");
for (int i = 0; i < raf.length(); i++) {
System.out.println("Value "+i+" : "+raf.readDouble());
}
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Student {
private String name;
private int age;
public Student() {
super();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
public class UseIO {
public static void main(String[] args) {
RandomAccessFile randomAccessFile = null;
try {
// 创建一个随机访问文件对象,并设置为可读写模式
randomAccessFile = new RandomAccessFile("newFile.txt",
"rw");
System.out.println("文件指针当前位置:" + randomAccessFile.getFilePointer());
// 添加内容到文件中去
// 使用writeChars方法把一串字符写到文件中
// randomAccessFile.writeChars("I am here!If you love me,please give the kiss to me!\nThank you for your love!");
// 使用writeBytes方法把一串字符写到文件中,使用该方法将会被舍弃字符中的高8位,所以实际上写入的是字符中的低8位.
randomAccessFile
.writeBytes("I am here!If you love me,please give the kiss to me!\nThank you for your love!");
System.out.println("使用readLine方法读取数据:");
System.out.println("此时文件指针当前位置:"
+ randomAccessFile.getFilePointer());
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
// 使用readLine读取文件内容,每个字节的值被转换为字符的低8位,而字符的高8位被赋予0.因此这个方法不支持unicode字符集.
String content = randomAccessFile.readLine();
while (content != null) {
System.out.println(content);
content = randomAccessFile.readLine();
}
// 使用read方法读取指定长度的字节
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
byte[] b = new byte[10];
int length = randomAccessFile.read(b);
System.out.println("真正读取的字节数:" + length);
// 使用当前平台当前的默认字符集把字节数组转换为字符
String convertStr = new String(b);
System.out.println("转换后的内容为:" + convertStr);
// 使用skipBytes跳过若干个字节后,读取后面的字节
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
length = randomAccessFile.skipBytes(10);// 参数可以为负数,当为负数时,则该方法不起作用
System.out.println("实际跳过的字节数:" + length);
content = randomAccessFile.readLine();
while (content != null) {
System.out.println(content);
content = randomAccessFile.readLine();
}
// 之前使用writeBytes写入内容,所以如果我们使用readChar读取内容中的一个字符时,可能将出错
// 出现乱码的原因在于,使用该方法将从文件中读取两个字节做为一个字符高8位和低8位,作为一个unicode字符
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
char c = randomAccessFile.readChar();
System.out.println("读取一个字符:" + c);
System.out.println("读取的这个字符的值为:" + (int) c);
// 设置文件的内容为0字节
randomAccessFile.setLength(0);
// 注意使用UTF格式写入字符串时,对于英文字符,则占一个字节,中文字符,占三个字节,
// 而且当使用writeUTF时,在文件的开始处将写入整个字节的长度(注意不是字符串长度),占两个字节
randomAccessFile.writeUTF("我爱你!i love you!");
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
System.out.println(randomAccessFile.readUTF());
System.out.println("使用writeUTF方法写入字符串时,文件字节长度为:"
+ randomAccessFile.length());
// 设置文件的内容为0字节
randomAccessFile.setLength(0);
// 创建两个学生记录,并写入文件中
Student[] studs = new Student[] { new Student("andy", 23),
new Student("lili", 22) };
for (Student stud : studs) {
randomAccessFile.writeUTF(stud.getName());
randomAccessFile.writeInt(stud.getAge());
}
// 读取刚才写入的内容
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
// 创建学生记录:
Student[] studCreated = new Student[2];
for (int i = 0; i < studCreated.length; i++) {
studCreated[i] = new Student();
studCreated[i].setName(randomAccessFile.readUTF());
studCreated[i].setAge(randomAccessFile.readInt());
System.out.println("第" + i + "位学生的记录:");
System.out.println("name:" + studCreated[i].getName());
System.out.println("age:" + studCreated[i].getAge());
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
randomAccessFile.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
-------------outputs------------------
F:\icss\2013312\eclipse\workspace\Temp
FilePointer location: 0
8.484
Value 0 : 1.414
Value 1 : 2.828
Value 2 : 4.242
Value 3 : 5.656
Value 4 : 7.069999999999999
Value 5 : 8.484
Value 6 : 3.1415926
Value 7 : 11.312
Value 8 : 12.725999999999999
Value 9 : 14.139999999999999
java.io.EOFException
at java.io.RandomAccessFile.readInt(RandomAccessFile.java:776)
at java.io.RandomAccessFile.readLong(RandomAccessFile.java:809)
at java.io.RandomAccessFile.readDouble(RandomAccessFile.java:855)
at RandomAccessFileTest.main(RandomAccessFileTest.java:32)
你会发现,一个double占8个字节,RandomAccessFile有定位功能,而且,读完第六个数后,自己指向第七个数,所以,在这个基础上,你再执行后面的rf.writeDouble(47.ooo1),它就是覆盖第七个数了。
RandomAccessFile不属于InputStream和OutputStream类系的。
实际上,除了实现DataInput和 DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫不相干,甚至不使用InputStream和OutputStream类中已经存在的任何功能;
它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。
基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream结合起来,
再加上它自己的一些方法,比如
定位用的getFilePointer( ),
在文件里移动用的seek( ),
以及判断文件大小的length( )、
skipBytes()跳过多少字节数。
此外,它的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数 (和C的fopen( )一模一样)。它不支持只写文件。
只有RandomAccessFile才有seek搜寻方法,而这个方法也只适用于文件。
使用该版本的开发人员需文件随机存取,就得使用RandomAccessFile类。其I/O性能较之其它常用开发语言的同类性能差距甚远,严重影响程序的运行效率。
开发人员迫切需要提高效率,下面分析RandomAccessFile等文件类的源代码,找出其中的症结所在,并加以改进优化,创建一个"性/价比"俱佳的随机文件访问类BufferedRandomAccessFile。
如果要在文件的结束点追加内容,那就用第一个构造方法,调用RandomAccessFile的seek(long lg)方法,
该方法传递进去一个long型的数据,我们正好可以把file.length()传递进去,这样就按照要求在文件末尾追加内容。
但这里可能会遇到了乱码问题,如果用的RandomAccessFile的 writeBytes(String s)方法写入内容,就会出现乱码,所以最好是用write(byte[] b)方法,
例如:
String s = "中文";
raf.write(s.getBytes());
这样就很好的解决了乱码问题。关于这两种方法,许多人认为好像差不多,其实是完全相反的方式,
writeBytes(String s)方法将中文拆成两个字节写进文本,所以会造成乱码,
write(byte[] b)方法是将每两个字节合成一个中文并写时文本,所以不会出现乱码。
我们还可以调用writeUTF(String str)方法避免乱码。
结下来还有一个解决换行的问题,写了“/n”不管用啊。实际上要用“/r/n”才行。
“/r”即回车,回到一行的开头。“/n”即换行,另起一行。这大家都是知道的。
我们在程序里写文件的时候所谓的“回车符”确切的说应该是“回车换行符”。
在windows系统下,回车换行符号是“/r/n”,但是在Linux等系统下是没有“/r”符号的,我们一般都是在window下,所以说在用程序写文件是应该是“/r/n”才对。
补充:为了适应跨平台需求,建议回车换行用System.getProperty("line.separator")。
RandomAccessFile是一个非常强大的类,当你需要非常快的修改文件的某些字节的时候,用它绝对能省下99%的时间,因为他不像其他流需要重新写一个临时文件。他是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了。这些记录的大小不必相同;但是其大小和位置必须是可知的。但是该类仅限于操作文件。
RandomAccessFile的绝大多数功能,但不是全部,已经被JDK 1.4的nio的"内存映射文件(memory-mapped files)"给取代了,你该考虑一下是不是用"内存映射文件"来代替RandomAccessFile了。
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestRandomAccessFile {
public static void main(String[] args) throws IOException {
RandomAccessFile rf = new RandomAccessFile("rtest.dat", "rw");
for (int i = 0; i < 10; i++) {
// 写入基本类型double数据
rf.writeDouble(i * 1.414);
}
rf.close();
rf = new RandomAccessFile("rtest.dat", "rw");
// 直接将文件指针移到第5个double数据后面
rf.seek(5 * 8);
// 覆盖第6个double数据
rf.writeDouble(47.0001);
rf.close();
rf = new RandomAccessFile("rtest.dat", "r");
for (int i = 0; i < 10; i++) {
System.out.println("Value " + i + ": " + rf.readDouble());
}
rf.close();
}
}
Value 0: 0.0
Value 1: 1.414
Value 2: 2.828
Value 3: 4.242
Value 4: 5.656
Value 5: 47.0001
Value 6: 8.484
Value 7: 9.898
Value 8: 11.312
Value 9: 12.725999999999999
-------------------
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
class Student {
private String name;
private int age;
public Student() {
super();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
public class UseIO {
public static void main(String[] args) {
RandomAccessFile randomAccessFile = null;
try {
// 创建一个随机访问文件对象,并设置为可读写模式
randomAccessFile = new RandomAccessFile("newFile.txt",
"rw");
System.out.println("文件指针当前位置:" + randomAccessFile.getFilePointer());
// 添加内容到文件中去
// 使用writeChars方法把一串字符写到文件中
// randomAccessFile.writeChars("I am here!If you love me,please give the kiss to me!\nThank you for your love!");
// 使用writeBytes方法把一串字符写到文件中,使用该方法将会被舍弃字符中的高8位,所以实际上写入的是字符中的低8位.
randomAccessFile
.writeBytes("I am here!If you love me,please give the kiss to me!\nThank you for your love!");
System.out.println("使用readLine方法读取数据:");
System.out.println("此时文件指针当前位置:"
+ randomAccessFile.getFilePointer());
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
// 使用readLine读取文件内容,每个字节的值被转换为字符的低8位,而字符的高8位被赋予0.因此这个方法不支持unicode字符集.
String content = randomAccessFile.readLine();
while (content != null) {
System.out.println(content);
content = randomAccessFile.readLine();
}
// 使用read方法读取指定长度的字节
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
byte[] b = new byte[10];
int length = randomAccessFile.read(b);
System.out.println("真正读取的字节数:" + length);
// 使用当前平台当前的默认字符集把字节数组转换为字符
String convertStr = new String(b);
System.out.println("转换后的内容为:" + convertStr);
// 使用skipBytes跳过若干个字节后,读取后面的字节
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
length = randomAccessFile.skipBytes(10);// 参数可以为负数,当为负数时,则该方法不起作用
System.out.println("实际跳过的字节数:" + length);
content = randomAccessFile.readLine();
while (content != null) {
System.out.println(content);
content = randomAccessFile.readLine();
}
// 之前使用writeBytes写入内容,所以如果我们使用readChar读取内容中的一个字符时,可能将出错
// 出现乱码的原因在于,使用该方法将从文件中读取两个字节做为一个字符高8位和低8位,作为一个unicode字符
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
char c = randomAccessFile.readChar();
System.out.println("读取一个字符:" + c);
System.out.println("读取的这个字符的值为:" + (int) c);
// 设置文件的内容为0字节
randomAccessFile.setLength(0);
// 注意使用UTF格式写入字符串时,对于英文字符,则占一个字节,中文字符,占三个字节,
// 而且当使用writeUTF时,在文件的开始处将写入整个字节的长度(注意不是字符串长度),占两个字节
randomAccessFile.writeUTF("我爱你!i love you!");
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
System.out.println(randomAccessFile.readUTF());
System.out.println("使用writeUTF方法写入字符串时,文件字节长度为:"
+ randomAccessFile.length());
// 设置文件的内容为0字节
randomAccessFile.setLength(0);
// 创建两个学生记录,并写入文件中
Student[] studs = new Student[] { new Student("andy", 23),
new Student("lili", 22) };
for (Student stud : studs) {
randomAccessFile.writeUTF(stud.getName());
randomAccessFile.writeInt(stud.getAge());
}
// 读取刚才写入的内容
// 重新把文件指针定位到开始处
randomAccessFile.seek(0);
// 创建学生记录:
Student[] studCreated = new Student[2];
for (int i = 0; i < studCreated.length; i++) {
studCreated[i] = new Student();
studCreated[i].setName(randomAccessFile.readUTF());
studCreated[i].setAge(randomAccessFile.readInt());
System.out.println("第" + i + "位学生的记录:");
System.out.println("name:" + studCreated[i].getName());
System.out.println("age:" + studCreated[i].getAge());
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
randomAccessFile.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
----------------------
文件指针当前位置:0
使用readLine方法读取数据:
此时文件指针当前位置:77
I am here!If you love me,please give the kiss to me!
Thank you for your love!
真正读取的字节数:10
转换后的内容为:I am here!
实际跳过的字节数:10
If you love me,please give the kiss to me!
Thank you for your love!
读取一个字符:䤠
读取的这个字符的值为:18720
我爱你!i love you!
使用writeUTF方法写入字符串时,文件字节长度为:23
第0位学生的记录:
name:andy
age:23
第1位学生的记录:
name:lili
age:22
RandomAccessFile类的应用:
/**
*
* @param skip 跳过多少过字节进行插入数据
* @param str 要插入的字符串
* @param fileName 文件路径
*/
public static void beiju(long skip, String str, String fileName){
try {
RandomAccessFile raf = new RandomAccessFile(fileName,"rw");
if(skip < 0 || skip > raf.length()){
System.out.println("跳过字节数无效");
return;
}
byte[] b = str.getBytes();
raf.setLength(raf.length() + b.length);
for(long i = raf.length() - 1; i > b.length + skip - 1; i--){
raf.seek(i - b.length);
byte temp = raf.readByte();
raf.seek(i);
raf.writeByte(temp);
}
raf.seek(skip);
raf.write(b);
raf.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 程序功能:演示了RandomAccessFile类的操作,同时实现了一个文件复制操作。
*/
package com.lwj.demo;
import java.io.*;
public class RandomAccessFileDemo {
public static void main(String[] args) throws Exception {
RandomAccessFile file = new RandomAccessFile("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);
RandomAccessFile fileCopy=new RandomAccessFile("fileCopy","rw");
int len=(int)file.length();//取得文件长度(字节数)
byte[] b=new byte[len];
file.readFully(b);
fileCopy.write(b);
System.out.println("复制完成!");
}
}
利用RandomAccessFile实现文件的多线程下载,即多线程下载一个文件时,将文件分成几块,每块用不同的线程进行下载。下面是一个利用多线程在写文件时的例子,其中预先分配文件所需要的空间,然后在所分配的空间中进行分块,然后写入:
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.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) {
}
}
}
}
}