RandomAccessFile:管理文件的内容
1、 创建对象
2、 字节数据读写操作
3、 文件指针操作
扩展:
1、文件是什么:是一个以byte为单位的数据序列。这个数据序列每个数据单元有序号对应,从0~file.length()-1,读写文件需要按照序号位置读写:这个序号位置称为文件指针(File Pointer)。如果文件指针到达了file.length(),到达EOF(End Of File)文件的末尾。
如:mp3文件:是5M个byte
2014*1024*5个byte数据
2、文件是干什么用的?
持久化存储数据的手段,用于软件内部或软件之间交换数据。
持久化是什么呢?内存中的数据断电不能保存,所以需要将执行的程序(只保存对象)保存为一个文件在磁盘上,下次打开这个程序可以继续执行。
3、文件都是以byte为单位读写。
4、文件是一个长长的byte序列。
创建对象:
1、 RandomAccessFile在对文件进行随机访问操作时有两个模式分别是只读模式(只读取文件)和读写模式(对文件数据进行读写)。提供了按byte为单位读写文件。
1、只读模式
在创建RandomAccessFile时,其提供的构造方法要求我们传入访问模式:
1、--RandomAccessFile(File file,Stringmode)
2、--RandomAccessFile(String filename,String mode)
--其中构造方法的第一个参数是需要访问的文件,而第二个参数是访问模式。
--“r”:表示对该文件的访问是只读模式,如果文件不存在,则出错
--“rw”:表示对文件的访问是读写模式,如果文件不存在,则创建文件
2、读写模式:
只需要在第二个参数中传入“rw”。表示对该文件的访问就是又可以读又可以写。
字节数据读写操作:
1、write(int d)方法:
RandomAccessFile提供了一个可以向文件中写出字节的方法:
---void write(int d):将d数据的低八位写出到打开的文进中。
---该方法会根据当前指针(从0开始)所在位置处写入一个字节,是将参数int的“低8位“写出,就是写出一个byte,int的前24位会被丢掉。
public void testWriteFile() throws Exception {
//打开一个文件
//检查文件指针的位置
//写出数据:在当前指针位置写出一个int的低8位
//每次写指针都会向后移动一个位置,方便下次写
//检查文件指针的位置
//写出数据
RandomAccessFile raf=new RandomAccessFile("write.txt","rw");
long index=raf.getFilePointer();//获取指针
System.out.println(index);//0
raf.write(50);//使用十六进制表示就是0011 0010
index=raf.getFilePointer();
System.out.println(index);//1
raf.write(0xca);//1100 1010
raf.write(0x100);//256 (1)0000 0000 低八位都是0
index=raf.getFilePointer();
System.out.println(index);//3
raf.close();
}
2、read()方法:
RandomAccessFile提供了一个可以从文件中读取字节的方法:
---int read()
该方法会从文件当前指针位置读取一个byte(8位)填充到int的低八位,高24位为0,返回值范围整数:0~255,如果返回-1表示读取到了文件末尾!每次读取后自动移动文件指针,准备下次读取。
public void testRead() throws Exception {
//读 取
RandomAccessFile raf=new RandomAccessFile("write.txt","r");
long index=raf.getFilePointer();
System.out.println(index);//0
int b=raf.read();
System.out.println(Integer.toBinaryString(b));//这是上面第一次写入的50的十六进制的二进制表示,11 0010
index=raf.getFilePointer();
System.out.println(index);//1
b=raf.read();
System.out.println(Integer.toBinaryString(b));//这是上面第二次写入的0xca,1100 1010
index=raf.getFilePointer();
System.out.println(index);//2
b=raf.read();//这是上面第三次写入的0x100表示256,输出的只输出低八位,所以应该是0
System.out.println(Integer.toBinaryString(b));//0
index=raf.getFilePointer();
System.out.println(index);//3
b=raf.read();
System.out.println(b);//读到了文件的末尾,现在返回值是-1
System.out.println(Integer.toBinaryString(b));//11111111111111111111111111111111,-1表示为补码
raf.close();
}
public void testRead2() throws Exception {
//循环读取
RandomAccessFile raf=new RandomAccessFile("write.txt","r");
int b;
while((b=raf.read())!=-1) {
System.out.println(Integer.toBinaryString(b));// 110010 11001010 0
}
raf.close();
}
3、write(byte[] d)方法:
RandomAccessFile提供了一个可以向文件中写出一组字节的方法:
---void write(byte[] d)
该方法会根据当前指针所在位置处连续写出给定数组中的所有字节。
重载的方法:
---void write(byte[] d,int offset,int len)
该方法会根据当前指针所在位置处连续写出给定数组中的部分字节,这个部分是从数组的offset处开始,连续len个字节。
public void testWrite1() throws Exception {
RandomAccessFile raf=new RandomAccessFile("write2.txt","rw");
//0xca 0xff大于127,超过了byte的范围,byte的范围是-128~127
//经过强制类型转换之后,变为负数!
byte[] buf= {0x32,(byte)0xca,0x00,(byte)0xff};
// 十进制数据是 50 -54 0 -1
raf.write(buf);
//写完之后文件指针的位置是4
raf.close();
}
4、read(byte[] b)方法:
RandomAccessFile提供了一个可以从文件中批量读取字节的方法:
---int read(byte[] b)
该方法会从文件中尝试最多读取给定数组的总长度的字节量,并从给定的字节数组中第一个位置开始,将读取到的字节顺序存放至数组中,返回值为实际读取到的字节量。
public void testReadByBuffer() throws Exception {
RandomAccessFile raf=new RandomAccessFile("write2.txt","r");
//返回长度不够
byte[] buf=new byte[3];//buf的长度是3,而上面写入了4个字节,所以只返回前3个字节的内容
int n=raf.read(buf);//返回字节量,也就是buf中字节的长度,3
System.out.println(n);
//显示byte数组中的数据
for(byte b:buf) {
//System.out.println(Integer.toHexString(b));
//32 ffffffca 0
//因为0xca超过byte的范围了,所以返回补码
System.out.println(Integer.toHexString(b&0xff));
//32 ca 0
}
//返回长度多余
// byte[] buf1=new byte[5];//超过了文件中写入的个数
// int m=raf.read(buf1);
// System.out.println(m);//4
// for(byte bt:buf1) {
// System.out.println(Integer.toHexString(bt&0xff));//32 ca 0 ff 0
//最后一个0是因为buf1初始化数组中的元素是0
// }
raf.close();
}
public void testReadByBuffer1() throws Exception {
RandomAccessFile raf=new RandomAccessFile("write2.txt","r");
//如果将文件的全部数据一次读取到byte数组中
//提示:文件长度raf.length()
byte[] buf=new byte[(int)raf.length()];//将文件的数据一次读取到内存中
//适合小文件,不能用于大文件
int n=raf.read(buf);
System.out.println(n);//4
//达到文件末尾,再读取返回-1
int m=raf.read(buf);
System.out.println(m);//-1
}
public void testWriteByteArray() throws Exception {
RandomAccessFile raf=new RandomAccessFile("write1.txt","rw");
byte[] buf="helloworld".getBytes();
//将字节数组中所有字节一次性写出
raf.write(buf);
raf.close();
}
public void testReadByteArray() throws Exception {
RandomAccessFile raf=new RandomAccessFile("write1.txt","r");
//创建10个字节的数组,因为上面helloworld有十个字节
byte[] buf=new byte[10];
int len=raf.read(buf);
System.out.println("读取到了"+len+"个元素");//读取到了10个元素
System.out.println(new String(buf));//helloworld
raf.close();
}
5、拆分方法:
raf.writeInt(i):将int拆分成4个byte写出
raf.writeLong(i):将long拆分成8个byte写出
raf.writeDouble(i):将double拆分成8个byte写出
public void testWriteInt() throws Exception {
int row=4;
int col=1;
RandomAccessFile raf=new RandomAccessFile("write3.txt","rw");
raf.writeInt(row);//注意不是write,这个地方增加了4个byte
raf.writeInt(col);
raf.close();
}
6、拼接方法:
int i=raf.readInt():连续读取4个byte拼接为一个int返回
其余的都一个道理
public void testReadInt() throws Exception {
int row,col;
RandomAccessFile raf=new RandomAccessFile("write3.txt","r");
row=raf.readInt();//注意不是read
col=raf.readInt();//注意读取顺序
raf.close();
System.out.println("row="+row+",col="+col);//row=4,col=1
}
7、如何使用RandomAccessFile存储一个电话本
int long Stirng String
序号 创建时间 姓名 电话
1 创建时间 小米 118
2 创建时间 黑米 110
…
文件的设计,每条信息为372个字节
0~3 4个 存储int编号
4~11 8个 存储long创建时间
12+50 62个 存储String姓名,从左边起写,后面的补0
62+30 92个 存储String电话
92~200 个 存储String地址
剩下的备用
如何写出:
第一条:
0 编号
4 时间
12 name
62 mobile
92 address
第二条:
372+0 编号
372+4 时间
372+12 name
372+62 mobile
372+92 address
第n条:
372*(n-1)+0 编号
372*(n-1)+4 时间
372*(n-1)+12 name
372*(n-1)+62 mobile
372*(n-1)+92 address
设计方法:
//将信息写入到raf文件中,n表示第几条
publicvoid writeContect(RandomAccessFile raf,int n,int id,Datetime,
Stringname,String mobile,String address) {}
public void writeContect(RandomAccessFile raf,int n,int id,Date time,
String name,String mobile,String address) throws Exception {
//1、先为文件增加一个定长记录
long start=372*(n-1);
raf.setLength(start+372);//先为每条记录分配一个372的空间
raf.seek(start);//是id的写出位置,然后从头开始分配
raf.writeInt(id);
raf.seek(start+4);//是时间的写出为止
raf.writeLong(time.getTime());
raf.seek(start+12);//是姓名的写出位置
raf.write(name.getBytes());
raf.seek(start+62);//是电话的写出位置
raf.write(mobile.getBytes());
raf.seek(start+92);//是地址的写出位置
raf.write(address.getBytes());
}
//@Test
public void testWriteContect() throws Exception {
//写入文件
RandomAccessFile raf=new RandomAccessFile("contect.dat","rw");
writeContect(raf,1,1,new Date(),"小米","118","北京大钟寺");
writeContect(raf,2,2,new Date(),"黑米","110","北京");
raf.close();
}
读取地址信息:
public Map<String,Object>readContect(RandomAccessFile raf,int n){}
//读取地址信息
public Map<String,Object> readContect(RandomAccessFile raf,int n) throws Exception{
int start=372*(n-1);
raf.seek(start+0);
int id=raf.readInt();//读取编号
raf.seek(start+4);
long time=raf.readLong();//读取时间
raf.seek(start+12);
//读取人名12连续50个byte
byte[] buf=new byte[50];//读取人名
raf.read(buf);
String name=new String(buf).trim();//trim()去掉字符串前后的空白
//读取电话
buf=new byte[30];
raf.seek(start+62);
raf.read(buf);
String mobile=new String(buf).trim();
//读取地址
buf=new byte[200];
raf.seek(start+92);
raf.read(buf);
String address=new String(buf).trim();
Map<String,Object> map=new LinkedHashMap<String,Object>();
map.put("id", id);
map.put("time", new Date(time));
map.put("name", name);
map.put("mobile", mobile);
map.put("address",address);
return map;
}
//@Test
public void testReadContect() throws Exception {
RandomAccessFile raf=new RandomAccessFile("contect.dat","r");
//读取一条联系人信息
Map<String,Object> contact=readContect(raf,2);//读第二条
raf.close();
//输出读取结果
System.out.println(contact);
}
“id”:1
“time”:Date
“name”:“黑米”
“mobile”:110
“address”:“北京”
因为数据类型什么都有,所以使用Object类型。
8、seek方法:
raf.seek(pointer):
移动文件读取指针到指定位置
如果指针位置超出文件长度,文件自动增加
如:在新文件上执行
raf.seek(372):
文件会增长到372,文件的内容默认为0
9、skipBytes方法
RandomAccessFile提供了一个方法可以尝试跳过输入的n个字节以丢弃跳过的字节。
方法:
----int skipByte(int n)
该方法可能跳过一些较少数量的字节,这可能由任意数量的条件引起,在跳过n个字节之前已经达到文件的末尾只是其中的一种可能。此方法不抛出EOFException。返回跳过的实际字节数。如果n是负数,则不跳过任何字节。这个是相对位置,在相对于当前位置跳过n个字节。