java.io.RandomAccessFile
RAF是专门用来读写文件数据的API,其基于指针对文件任意位置进行读写
重点是最后面的字符串的写入与读出
一、对文件操作需要创建对象
RandomAccessFile raf = new RandomAccessFile("./raf.dat",“rw”);
- 创建实例对象时,需要:
导包:import java.io.RandomAccessFile;
抛出异常: throws FileNotFoundException(抛出的异常也会导包 import java.io.FileNotFoundException;)
- 传入参数为:
路径:可以是字符串也可以是File对象
操作名(字符串类型):r(只读,访问文件需存在),rw(读写,访问文件不存在会自动创建)
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
public class RafDemo1 {
public static void main(String[] args) throws FileNotFoundException {
/*
* 对当前目录下的raf.dat文件读写数据
*/
//创建实例对象(需要导包和抛出异常)
//传入参数为 路径(可以是字符串也可以是File对象)
RandomAccessFile raf = new RandomAccessFile("./raf.dat","rw");
}
}
二、对文件的写入操作
write()
需要抛出异常,需要传入写入的数据,写入的结果是该数据对应的二进制值
注意:写入的值是给的值的二进制的最低的那八位二进制
import java.io.IOException;
import java.io.RandomAccessFile;
public class RafDemo1 {
public static void main(String[] args) throws IOException{
RandomAccessFile raf = new RandomAccessFile("./raf.dat","rw");
//对文件的写入操作
//需要抛出异常,需要传入写入的数据,写入的结果是该数据对应的二进制值
raf.write(97);
System.out.println("写入完毕!");
raf.close(); //用完了要关闭
}
}
三、对文件的读出操作
int read()
从文件中读取1个字节,并以int形式返回,若返回值为-1,则表示文件末尾
调用一次读入一个字节,再次调用会读入下一个字节
import java.io.IOException;
import java.io.RandomAccessFile;
public class RafDemo2 {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("./raf.dat","r");
int d = raf.read();
System.out.println(d);
}
}
四、对文件进行复制
上述方法的综合应用
通过对文件的读和写来达到文件的复制
该方法不建议,因为被硬盘读取的短板限制了复制的效率
import java.io.IOException;
import java.io.RandomAccessFile;
public class CopyDemo {
public static void main(String[] args) throws IOException, InterruptedException {
RandomAccessFile src = new RandomAccessFile("./image.jpg","r");
RandomAccessFile desc = new RandomAccessFile("./image_cp.jpg","rw");
int d = -1;
//记录时间,查看效率
//开始时间
long start = System.currentTimeMillis();
//循环条件中读取源文件的每个字节,当读取的值为-1时,读取完毕
//循环体重将读取的每个字节写入到复制文件中
while((d=src.read()) != -1){
desc.write(d);
}
//结束时间
long end = System.currentTimeMillis();
System.out.println("复制完毕,耗时:"+(end-start)/1000+"秒");
//注意:一定不要忘了关闭文件
src.close();
desc.close();
}
}
五、高效文件复制
单字节读写:是随机读写,效率差
块读写:是一组一组字节的读写,效率高
块读写通过提高每次读写的数据量,减少读写次数,可以提高效率
read和write方法的重载方法:
- int read(byte[] data)
一次性从文件中读取给定数组总长度的字节量,并将读取到的数据存入该数组中
返回值为实际读取到的字节量
若返回值为-1,则表示读取的为文件末尾(本次没有读取到任何数据) - void write(byte[] data)
一次性将给定字节数组中所有的数据写入文件
但是要注意,这样的话最后一次若没有读满,将会多写一段倒数第二段的数据
我们要读多少,写多少,针对这种情况
write还有个重载的方法 - void write(byte[] data,int start,int len)
将给定字节数组从下标start处开始连续读写len(读取数据的长度)个字节,再一次性写入文件
import java.io.IOException;
import java.io.RandomAccessFile;
public class CopyDemo2 {
public static void main(String[] args) throws IOException {
RandomAccessFile src = new RandomAccessFile("./image.jpg","r");
RandomAccessFile desc = new RandomAccessFile("./image_copy.jpg","rw");
long start = System.currentTimeMillis(); //开始复制时间
//每次读取10k数据,10k左右是效果最好的读取效率
byte[] data = new byte[1024*10]; //定义每次读多少字节的数组
int len = -1; //记录每次实际读取的字节数
while((len = src.read(data)) != -1){
desc.write(data, 0, len);
}
long end = System.currentTimeMillis(); //结束复制时间
System.out.println("复制完毕,耗时:"+(end-start)/1000+"秒");
//注意:一定不要忘了关闭文件
src.close();
desc.close();
}
}
六、基本类型与字符串的读写操作
字符串部分
使用RAF向文件中写入字符串
String 提供了转换为字节的方法
byte[] getBytes(“字符集格式”);
将当前字符串按照给定字符集转换为一组字节
传入的参数为字符集名称,不区分大小写,如果不写,按照系统默认的字符集操作
推荐用传参的,不要按照系统默认的,因为系统不同将会不能运行
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Scanner;
public class WriteStringDemo {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("./raf.txt", "rw");
Scanner input = new Scanner(System.in);
System.out.println("请输入要写入文件的字符串:");
String line = input.nextLine();
byte[] data = line.getBytes("utf-8");//将字符串转换为字节数组
raf.write(data);
//追加一句
System.out.println("请再输入后面的话:");
raf.write(input.nextLine().getBytes("utf-8"));
System.out.println("写入完毕!");
raf.close();
input.close();
}
}
使用RAF读取文本数据,将文本数据转换为字符串输出
import java.io.IOException;
import java.io.RandomAccessFile;
public class ReadStringDemo {
public static void main(String[] args) throws IOException {
RandomAccessFile src = new RandomAccessFile("./raf.txt", "r");
//创建一个接收读取数据的字节数组
//该数组长度是文件长度,返回值是long需要强转为int
byte[] data = new byte[(int)src.length()];
src.read(data);
//字符串类有一个有参构造方法,传入字节数组,并定义编码格式
String str = new String(data,"utf-8");
System.out.println(str);
src.close();
}
}
基本数据类型
读写基本类型数据以及RAF基于指针的操作
写入操作:
操作原理
一个int型为4字节
int的最大值在二进制中表示为
01111111 11111111 11111111 11111111
write写入每次只能写入低八位,我们可以通过逻辑右移位>>>来获取每个八位的值
针对这种情况,java根据该原理,提供了方法
writeInt(int i); 将给定int值对应的4个字节一次性写入
其他基本类型相同
指针操作:
获取指针位置 getFilePointer();
我们写入操作结束后,指针指向了文件末尾,继续读取会为-1
所以在进行读取操作前,我们需要修改指针位置
我们可以用 seek() 方法将指针拨回0位置
seek方法: void seek(long pos)
移动指针到指定位置
读出操作:
操作原理与写入相同,读出操作也有对应的一次性读出的方法
例如:int型用readInt()
import java.io.IOException;
import java.io.RandomAccessFile;
public class RafDemo3 {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("./raf.dat", "rw");
//向文件写入一个int最大值(4字节)
//01111111 11111111 11111111 11111111
int max = Integer.MAX_VALUE;
//由于read只能读取低八位的值
//通过逻辑右移位>>>,来获取每个八位的值
// for(int i = 24;i>=0;i-=8){
// raf.write(max>>>i);
// }
//java提供了一次写入的方法,该方法原理就是上述方法
//写入其他基本类型的方法大致相同
raf.writeInt(max); //写入int最大值
raf.writeInt(128); //写入int值128
raf.writeLong(1); //写入long类型
System.out.println("写入完毕");
//获取指针位置getFilePointer()
System.out.println("此时指针位置:"+raf.getFilePointer());
//进行读取操作前,我们需要修改指针位置
//注意这里文件指针已经指向了末尾,继续读取会为-1
//将抛出异常EOFException,EOF(end of file)文件末尾
raf.seek(0); //指针指向第一个数字的起始位置
int d = raf.readInt();
System.out.println("读出写入的值1:"+d);
raf.seek(4); //指针指向第二个数字的起始位置
System.out.println("读出写入的值2:"+raf.readInt());
raf.seek(8); //指针指向第三个数字写入的位置
System.out.println("读出写入的值3:"+raf.readLong());
//通过指针对文件修改
//将第二个int值改为int最小值
raf.seek(4);
raf.writeInt(Integer.MIN_VALUE);
raf.seek(4);
System.out.println("输出修改后的值"+raf.readInt());
raf.close();
}
}
练习:用户信息的注册、显示与修改
用户注册,信息写入
package raf;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Scanner;
/**
* 用户注册
* 程序启动后,要求用户输入注册信息(用户名、密码、昵称、年龄)
* 其中除了年龄是int,其余三个皆为String
* 然后将该信息写入user.dat文件中保存
*
* 我们对其做限制
* 每条用户信息占100字节
* 用户名、密码、昵称各占32字节,年龄占4字节
* @author Tian
*
*/
public class RegDemo {
public static void main(String[] args) throws IOException {
System.out.println("欢迎注册");
RandomAccessFile raf = new RandomAccessFile("./user.dat", "rw");
//每次运行代码时,将指针移动到文件末尾,以便再次追加数据
raf.seek(raf.length());
String name;
String pwd;
String nick;
int age;
Scanner input = new Scanner(System.in);
System.out.print("请输入用户名:");
name = input.nextLine();
System.out.print("请输入密码:");
pwd = input.nextLine();
System.out.print("请输入昵称:");
nick = input.nextLine();
System.out.print("请输入年龄:");
age = Integer.parseInt(input.nextLine());
System.out.println("该用户信息为:用户名("+name+"),密码("+pwd+"),昵称("+nick+"),年龄("+age+")");
//写入用户名
byte[] data = name.getBytes("utf-8");
//数组扩容为32字节
data = Arrays.copyOf(data, 32);
raf.write(data);
//写入密码
raf.write(Arrays.copyOf(pwd.getBytes("utf-8"), 32));
//写入昵称
raf.write(Arrays.copyOf(nick.getBytes("utf-8"), 32));
//写入年龄
raf.writeInt(age);
System.out.println("写入完毕!");
}
}
用户信息显示
package raf;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* 将user.dat文件中的用户信息显示到控制台
* @author Tian
*
*/
public class ShowAllUserDemo {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("./user.dat","r");
//定义读取字符串的字节数组
byte[] data = new byte[32];
for(int i=0;i<raf.length()/100;i++){
//读取用户名
raf.read(data);
String name = new String(data,"utf-8").trim(); //注意要加trim去除两端空格
//读取密码
raf.read(data);
String pwd = new String(data,"utf-8").trim(); //注意要加trim去除两端空格
//读取昵称
raf.read(data);
String nick = new String(data,"utf-8").trim(); //注意要加trim去除两端空格
//读取年龄
int age = raf.readInt();
//输出读取信息
System.out.println("用户"+(i+1)+"信息为:用户名("+name+"),密码("+pwd+"),昵称("+nick+"),年龄("+age+")");
}
raf.close();
}
}
用户信息修改
package raf;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Scanner;
/**
* 完成修改昵称的操作
* 程序启动后,要求输入用户名和新的昵称,
* 然后修改user.dat文件中该用户的昵称
* 若输入的用户名在user.dat文件中不存在
* 提示信息:该用户不存在!
* @author Tian
*
*/
public class UpdataDemo {
public static void main(String[] args) throws IOException {
Scanner input = new Scanner(System.in);
System.out.print("请输入要修改用户的用户名:");
String name = input.nextLine();
System.out.print("请输入要修改的昵称:");
String nick = input.nextLine();
//用户输入结束后再打开文件
RandomAccessFile raf = new RandomAccessFile("./user.dat", "rw");
for(int i=0;i<raf.length();i+=100){
//遍历获取用户名
//指针拨到用户名所在指针
raf.seek(i);
//定义读取文件信息的字节数组
byte[] data = new byte[32];
//获取文件中的用户名,注意去除空格
raf.read(data);
String uname = new String(data,"utf-8").trim();
//查找有没有用户名与输入的用户名相同
if(name.equals(uname)){
//如果有相同的用户名,提示用户输入修改的昵称
//但是由于文件这时已经打开了,在做服务端的时候不要这样做
//等待用户输入,会一直占用文件,在服务端,其他人就不能访问这个文件了
//所以我们先把数据都让用户输入,再打开文件运行
//System.out.print("请输入要修改的昵称:");
//String nick = input.nextLine();
//将该昵称写入到当前用户的昵称中,覆盖以前的昵称
//指针拨到i+64的位置
raf.seek(i+64);
//进行写入操作
raf.write(Arrays.copyOf(nick.getBytes("utf-8"), 32));
System.out.println("修改成功!");
raf.close();
input.close();
return;
}
}
System.out.println("查无此人!");
raf.close();
input.close();
}
}