随机存取文件流:RandomAccessFile类
既可以作为输入流出现也可以作为输出流出现,所以再去输入输出的时候就可以不用两个类了,用这一个类即可,但注意还是要去造两个对象,其中一个表示输入,另外一个表示输出
实现了DataInput和DataOutput接口
它不像其他的流是继承4个现有的基类流的某一个,而是直接继承Object
还有一个特别之处是在写文件的时候如果文件不存在可以去创建文件(这和其他流一样),如果说这个文件存在,里面已经有一些数据,在去写数据的时候,会从头开始把原文件进行覆盖
其他流要么是在文件末尾进行追加,要么是把整个文件进行覆盖
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* RandomAccessFile的使用:既可以作为输入流出现也可以作为输出流出现
* 创建 RandomAccessFile 类实例需要指定一个mode参数,该参数指定RandomAccessFile的访问模式:一般用的是r和rw注意:没有w
* r: 以只读方式打开,比如如果指定的是r,就只能去读入了,只能作为一个输入流出现
* rw:打开以便读取和写入
* rwd:打开以便读取和写入;同步文件内容的更新
* rws:打开以便读取和写入;同步文件内容和元数据的更新
* 每次write数据时,rw模式数据不会立刻写到硬盘中,rwd模式数据会立刻写入硬盘,如果写数据的过程发生异常,rwd中中已被write的数据保存到硬盘,而rw则全部丢失
* 如果模式为只读r。则不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常。
* 如果模式为rw读写。如果文件不存在则会去创建文件,如果存在则不会创建。
*/
public class Test {
public static void main(String[] args) {
RandomAccessFile raf=null;//ctrl+alt+/查看构造器,这里指明读的文件的位置
RandomAccessFile raf1=null;
try {
raf = new RandomAccessFile(new File("fun.jpg"),"r");
//mode(模式)是参数二,给的是String类型
raf1 = new RandomAccessFile(new File("fun3.jpg"),"rw");
byte[] bytes=new byte[1024];
int len;
while((len=raf.read(bytes))!=-1){
raf1.write(bytes,0,len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(raf!=null)
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(raf1!=null)
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//测试:当RandomAccessFile作为输出流出现时。如果说这个文件存在,里面已经有一些数据,在去写数据的时候,会从头开始把原文件进行覆盖
//这里的覆盖不是对文件进行覆盖(否则结果为xyz),也不是追加,而是在开头开始对原有文件内容进行覆盖
//用已经存在的文本文件hello.txt进行测试,//如果hello.txt不存在,会帮我们造一个,里面的内容是xyz
RandomAccessFile raf2=null;
try {
raf2 = new RandomAccessFile("hello.txt","rw");
raf2.write("xyz".getBytes());//因为要给write传进去字节数组
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(raf2!=null)
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
}//假设hello.txt原来的内容为abcdefghijklmn,执行完之后的内容为xyzdefghijklmn
//RandomAccessFile实现数据的插入,使用seek方法
//void seek(long pos):将文件记录指针定位到 pos 位置,默认情况下pos为0,所以是从头开始实现操作的
//如果我们现在不想从头而是从d这个位置(3,序号和数组的那个是一样的)
//比如变成abcxyzdefghijklmn
RandomAccessFile raf3=null;
try {
raf3 = new RandomAccessFile("hello1.txt","rw");
raf3.seek(3);//将指针调到角标为3的位置,其实是第四个字符
raf3.write("xyz".getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(raf3!=null)
raf3.close();
} catch (IOException e) {
e.printStackTrace();
}
}//上面这么干不是插入的效果,而是从第四个字符开始(包括第四个字符)开始进行覆盖
//write这个方法当要写入的文件有数据时,实现的就是覆盖的操作
//怎么追加到文件末尾?调用File的length方法得到长度,就用这个东西放到seek里,就会从最后一个数据的后面开始写
//我们一般习惯进行数据的追加而不是插入,效率太低
//比如变成abcxyzdefghijklmn怎么实现呢?可以在他覆盖之前把数据读出来,把插入位置后面的数据存起来。即把defghijklmn存起来,其实本质还是覆盖
RandomAccessFile raf4=null;
try {
raf4 = new RandomAccessFile("hello.txt","rw");
raf4.seek(3);
byte[] bytes=new byte[5];
int len;//传给StringBuilder需要int类型的,new File("hello.txt").length()为long类型的,所以还要强制类型转换
//保存指针后的所有数据到StringBuilder中
StringBuilder sb=new StringBuilder((int) new File("hello.txt").length());//StringBuilder底层造了一个数组,初始大小为16,但如果放不下文件的数据就要去扩容,我们尽量不要扩容,就指定造一个长度的数组
while((len=raf4.read(bytes))!=-1){//数据读到bytes中,bytes中的数据可以放到字符串中
sb.append(new String(bytes,0,len));
}
//此时读写位置指针在最后了,还要把指针调回来
raf4.seek(3);
raf4.write("xyz".getBytes());//是从3这个位置开始写
//此时指针在z的后面
//将StringBuilder中的数据写入文件中
raf4.write(sb.toString().getBytes());//StringBuilder没有getBytes()方法,所以先要变成字符串
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(raf4!=null)
raf4.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
可以将StringBuilder替换为ByteArrayOutputStream
ByteArrayOutputStream的使用举例:
import org.junit.Test;
public class ByteArrayOutputStreamTest {
@Test
public void test1() throws Exception {
FileInputStream fis = new FileInputStream("abc.txt");
String info = readStringFromInputStream(fis);
System.out.println(info);
}
private String readStringFromInputStream(FileInputStream fis) throws IOException {
// 方式一:可能出现乱码
// String content = "";
// byte[] buffer = new byte[1024];
// int len;
// while((len = fis.read(buffer)) != -1){
// content += new String(buffer);
// }
// return content;
// 方式二:BufferedReader
BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
char[] buf = new char[10];
int len;
String str = "";
while ((len = reader.read(buf)) != -1) {
str += new String(buf, 0, len);
}
return str;
// 方式三:避免出现乱码
// ByteArrayOutputStream baos = new ByteArrayOutputStream();//ByteArrayOutputStream也是输出流,里面也提供了一个数组
// byte[] buffer = new byte[10];
// int len;
// while ((len = fis.read(buffer)) != -1) {
// baos.write(buffer, 0, len);//实际上write到流里面的数组中了
// }
//
// return baos.toString();
}
}