JavaSE核心API--随机读写文件流
java.io.RandomAccessFile
1)理论讲解:文件复制
代码演示:
RandomAccessFile src = new RandomAccessFile("bee0.png", "r");// 创建一个RAF用于读取原文件
RandomAccessFile desc = new RandomAccessFile("bee0_cp.png", "rw");// 再创建另一个RAF用于向复制文件中写入
int d = -1;// 用于记录每次读取到的字节
while ((d = src.read()) != -1) {
desc.write(d);
}
System.out.println("复制完毕!");
src.close();
desc.close();
2)理论讲解:随机读写
传统的机械硬盘是由于其物理特性决定这单字节读写效率差 但是块读写效率是可以保证的
所以我们通过提高每次读取的数据量,减少实际读写的次数,可以提高读写的效率
随机读写:通常是单字节读写模式 块读写:一次读写一组字节的模式
代码演示:
long start = System.currentTimeMillis();
try (RandomAccessFile src = new RandomAccessFile("bee0.png", "r"))// 创建一个RAF用于读取原文件
{
RandomAccessFile desc = new RandomAccessFile("bee1_cp.png", "rw");// 再创建另一个RAF用于向复制文件中写入
byte[] data = new byte[1024 * 10];// 每次实际读取到的字节量(一次读取10kb)
int len = -1;
while ((len = src.read(data)) != -1) {// 循环读取,直到文件末尾结束
desc.write(data, 0, len);
}
desc.close();
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!耗时:" + (end - start) + "ms");
3)小练习:简易记事本工具
程序启动后,要求用户输入一个文件名,然后开始对文件进行写操作 之后用户输入的每行字符串都写入到该文件中,当用户输入量单词"exit"时,程序退出。
代码演示:
Scanner scan = new Scanner(System.in);
System.out.println("请输入文件名(以.txt结尾):");
String fileName = scan.nextLine();// 1.输入文件名
while (true) {
if (fileName.endsWith(".txt")) {// 2.判断文件名是否符合格式
break;
}
System.out.println("输入格式错误,请重新输入!");
fileName = scan.nextLine();
}
File file = new File(fileName);
if (!file.exists()) {// 判断文件是否存在
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("文件创建成功!");
} else {
System.out.println("该文件已存在!");
scan.close();
return;
}
try (RandomAccessFile raf = new RandomAccessFile(fileName, "rw")) {// 3.使用RAF对该文件进行写操作
/** 4.将用户输入的每行字符串写入到文件中 */
byte[] data = {};
String line = "";
String lineFeed = "\n";
String resultLine = "";
System.out.println("开始写吧!");
while (true) {
line = scan.nextLine();
if (line.equals("exit")) {
break;
}
resultLine = line + lineFeed;// 用户输入一行字符串之后自动换行
data = resultLine.getBytes("utf-8");
raf.write(data);
}
System.out.println("写入完毕!");
} catch (Exception e) {
e.printStackTrace();
}
scan.close();
4)理论讲解:RandomAccessFile类
java.io.RandomAccessFile
用来读写文件数据的类,其基于指针对文件数据进行读写操作。
代码演示:
/*
* RandomAccessFile创建有两种模式:
* r:只读模式,只读取文件数据,并不会写入内容
* rw:读写模式,对文件既可以读也可以写。
*
* 常见构造方法:
* RandomAccessFile(String path,String mode)
* RandomAccessFile(File file,String mode)
* mode:创建的模式(r,rw)
*/
/*
* 对当前目录下的raf.dat文件进行读写操作
* 对于"rw"模式创建时,若指定的文件不存在时会自动创建出来
* 若为"r"只读模式时,若指定的文件不存在则会直接抛出异常:FileNotFoundException
*/
RandomAccessFile raf=new RandomAccessFile("raf.dat","rw");
/*
* void write(int d)
* 向文件中写入1个字节,写入的是给定的int值对应的
* 2进制的“低八位”
* vvvvvvvv
* 00000000 00000000 00000000 00000001
*
*/
raf.write(97);//0~255之间的数(1个byte字节)
raf.write(100);
System.out.println("写入完毕!");
raf.close();
5)理论讲解:读写基本类型数据,以及RAF如何基于指针进行读写操作
代码演示:
RandomAccessFile raf = new RandomAccessFile("raf.dat", "rw");
/*
* long getFilePointer()
* 获取指针位置
*/
long pos = raf.getFilePointer();
int max = Integer.MAX_VALUE;// 向文件中写入一个int最大值
/*
*
* 01111111 11111111 11111111 11111111
* max>>>24
* 00000000 00000000 00000000 01111111
*
*/
// raf.write(max>>>24);
// raf.write(max>>>16);
// raf.write(max>>>8);
// raf.write(max);
/*
* void writeInt(int d)
* 一次性写入4字节,将给定的int值写出
*/
raf.writeInt(max);
System.out.println("pos:" + raf.getFilePointer());
/*
* 对应的基本类型数据都提供了写操作
*/
raf.writeDouble(123.123);
System.out.println("pos:" + raf.getFilePointer());
raf.writeLong(123L);
System.out.println("写出完毕!");
/*
* void seek(long pos)
* 将指针移动到指定位置
*/
raf.seek(0);
System.out.println("pos:" + raf.getFilePointer());
/*
* RAF提供了一组读取基本类型的方法
* int readInt()
* 连续读取4个字节,并还原为int值返回
* 若在读取的过程中发现读取到了文件末尾,则直接抛出文件末尾异常:EOFException
*/
int d = raf.readInt();
System.out.println(d);
/*
* 读取double值 1.先将指针移动到double第一个字节的位置
*/
raf.seek(8);
double dou = raf.readDouble();
System.out.println(dou);
System.out.println("pos:" + raf.getFilePointer());
// long lon = raf.readLong();
// System.out.println(lon);
// System.out.println("pos:"+raf.getFilePointer());
// d = raf.read();
// System.out.println(d);
// 将double值的内容覆盖为567.567
// 1.先将指针移动到double的第一个字节位置
raf.seek(8);
// 2.重新写入8个字节的新double值
raf.writeDouble(567.567);
System.out.println("double修改完毕!");
System.out.println("pos:" + raf.getFilePointer());
raf.seek(8);
dou = raf.readDouble();
System.out.println(dou);
raf.close();
6)理论讲解:读取文本数据(字符串)
代码演示:
/*
* 字符串支持构造方法,将字节转换成字符串
* String(byte[] data)
* 按照系统默认字符集转换为字符串(不推荐)
* String(byte[] data,String csn)
* 按照指定的字符集将字节转换为对应字符串
*/
try (RandomAccessFile raf = new RandomAccessFile("raf.txt", "r")) {// 打开只读模式
byte[] data = new byte[(int) raf.length()];// 根据文件大小创建字节数组
raf.read(data);// 一次性读取文件中的所有字节
String str = new String(data, "UTF-8");// 将字节按照字符集还原成字符串
System.out.println(str);
} catch (Exception e) {
e.printStackTrace();
}
7)小练习:完成用户注册功能
要求用户输入注册信息包括:用户名,密码,昵称,年龄
其中前三个为字符串,年龄为int值
每个用户信息都写入到user.dat文件中保存
设计: 每条记录占用固定的100字节 其中:用户名,密码,昵称各占32字节,年龄占4字节
字符串故意留白有利于后期修改数据,并且格式固定,长度固定
读取时效率高,但是会有部分空间上的浪费
代码演示:
Scanner scan = new Scanner(System.in);
System.out.println("欢迎注册!");
System.out.println("请输入用户名:");
String username = scan.nextLine();
System.out.println("请输入密码:");
String password = scan.nextLine();
System.out.println("请输入昵称:");
String nickname = scan.nextLine();
System.out.println("请输入年龄:");
int age = Integer.parseInt(scan.nextLine());
/** 开启随机读写模式并往注册表文件上按顺序写入用户信息 */
try (RandomAccessFile raf = new RandomAccessFile("user.dat", "rw")) {
raf.seek(raf.length());// 先将指针移动到文件末尾处
byte[] data = {};// 创建一个字节数组
/** 写用户名 */
data = username.getBytes("utf-8");// 1.先将用户名转换为一组字节
data = Arrays.copyOf(data, 32);// 2.将该字节数组扩容至32字节
raf.write(data);// 3.将32字节写入文件
/** 写密码 */
data = password.getBytes("utf-8");// 1.先将密码转换为一组字节
data = Arrays.copyOf(data, 32);// 2.将该字节数组扩容至32字节
raf.write(data);// 3.将32字节写入文件
/** 写昵称 */
data = nickname.getBytes("utf-8");// 1.先将昵称转换为一组字节
data = Arrays.copyOf(data, 32);// 2.将该字节数组扩容至32字节
raf.write(data);// 3.将32字节写入文件
/** 写年龄 */
raf.writeInt(age);// 将8字节的int型数据写入文件
System.out.println("pos:" + raf.getFilePointer());
System.out.println("注册完毕!");
} catch (Exception e) {
e.printStackTrace();
}
scan.close();
8)小练习:将user.dat文件中的每个用户信息读取出来并输出到控制台
代码演示:
try (RandomAccessFile raf = new RandomAccessFile("user.dat", "r");) {
for (int i = 0; i < raf.length() / 100; i++) {
byte[] data = new byte[32];
/** 读取用户名 */
raf.read(data);
String username = new String(data, "UTF-8").trim();
/** 读取密码 */
raf.read(data);
String password = new String(data, "UTF-8").trim();
/** 读取昵称 */
raf.read(data);
String nickname = new String(data, "UTF-8").trim();
/** 读取年龄 */
int age = raf.readInt();
System.out.println("pos:" + raf.getFilePointer());
System.out.println(username + "," + password + "," + nickname + "," + age);
}
} catch (Exception e) {
e.printStackTrace();
}
9)小练习:完成修改昵称功能
程序启动后,要求用户输入用户名及新的昵称,然后对user.dat文件中该用户的昵称进行修改,若文件中没有此用户则提示:没有此用户!
实现思路:
循环读取user.dat文件中的每条记录中的用户名,然后与用户输入的用户名进行比对
若不是则进行下一次循环,若是该用户,则将指针移动到该条记录的昵称位置然后重写---将新的昵称以32字节形式写入以覆盖原昵称完成修改操作
若循环完毕后仍然没有匹配到用户,则最终提示:没有此用户
代码演示:
Scanner scan = new Scanner(System.in);
System.out.println("请输入用户名:");
String usernameInput = scan.nextLine();
try (RandomAccessFile raf = new RandomAccessFile("user.dat", "rw");// 打开文件读写模式
) {
/** 开始遍历注册表上的用户名进行查询 */
for (int i = 0; i < raf.length() / 100; i++) {
/*
* 假设注册表上有3条记录 raf.length()=300字节/100=3条记录
*
* i=0 开始读第1条记录 用户名的起始位置pos:0=i*100 i=1 开始读第2条记录 用户名的起始位置pos:100=i*100 i=2
* 开始读第3条记录 用户名的起始位置pos:200=i*100
*/
raf.seek(i * 100);// 每一次都先将指针放在用户名的起始位置开始读
/** 读取用户名 */
byte[] data = new byte[32];// 1.一次读取32个字节
raf.read(data);// 2.块读文件
String username = new String(data, "UTF-8").trim();// 3.将其还原为字符串(trim是为了去除字符串后面的留白部分)
if (username.equals(usernameInput)) {// 如果读取注册表上的用户名和用户输入的用户名相匹配
/** 修改昵称 */
raf.seek(i * 100 + 64);// 将指针移动到该条记录的昵称位置
System.out.println("请输入新的昵称:");
String nickname = scan.nextLine();// 新的昵称由用户重新输入
/** 重新写入昵称 */
data = nickname.getBytes("utf-8");// 1.先将昵称转换为一组字节
data = Arrays.copyOf(data, 32);// 2.将该组字节扩容至32字节
raf.write(data);// 3.将32字节写入文件
/** 修改密码 */
raf.seek(i * 100 + 32);// 将指针移动到该条记录的密码位置
System.out.println("请输入新的密码:");
String password = scan.nextLine();// 新的密码由用户重新输入
/** 重新写入密码 */
data = password.getBytes("utf-8");// 1.先将密码转换为一组字节
data = Arrays.copyOf(data, 32);// 2.将该组字节扩容至32字节
raf.write(data);// 3.将32字节写入文件
System.out.println("修改完毕!");
scan.close();
return;
}
}
System.out.println("没有此用户!");
scan.close();
} catch (Exception e) {
e.printStackTrace();
}
10)理论讲解:写入字符串
代码演示:
/*
* String提供了将字符串转换为字节的方法:
* byte[] getBytes()
* 按照系统默认字符集将当前字符串转换为对应的字节,不推荐这种方法,依赖系统默认字符集不利于跨平台
*
* byte[] getBytes(String csn)
* 按照指定的字符集csn(charset name)转换为一组字节
* 常见字符集:
* GBK:国标编码,其中英文1字节,中文2字节
* UTF-8:unicode的字符集编码,其中英文1字节,中文3字节。utf-8支持世界流行的所有文字
* 所以也成为万国码,互联网最常用字符集
* ISO8859-1:一种欧洲的编码集,不支持中文
*/
RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");//打开读写模式
byte[] data = new byte[1024*10];//块读写模式
String str = "好嗨呦~";//要写入的字符串
data = str.getBytes("utf-8");//把该字符串按照字符集转换成字节数组
raf.write(data);//将该数组写入文件
str = "感觉人生已经到达了高潮~";
data = str.getBytes("utf-8");
raf.write(data);
System.out.println("写入完毕!");
raf.close();