在之前文章中介绍了怎样在java中实现对txt文档的读取和写入的操作,并且可以通过保存为json格式方便数据的使用,但是如果需要对txt中的数据修改或删除操作,通常的做法是通过读取操作,将所有的数据读取出来放在一个临时的变量中,例如String中,但是如果数据量比较少则这样操作没有问题,但是一旦数据量比较大,例如需要对5G的数据进行删除操作,则会存在内存不足的情况。
因此在对于数据的删除和修改操作时,建议使用RandomAccessFile来实现,由于其特有的处理方式,在读取和写入操作时,使用该类操作仍然可以提高操作效率。
RandomAccessFile类继承自Object,实现了Closeable、DataInput和DataOutput,没有继承字节流或字符流中的任何一个类,通过DataInput和DataOutput可以方便读写操作,且效率较高。
1.写入
public boolean addFile(String filePath,String data) {
RandomAccessFile rFile = null;
try {
File file = new File(filePath);
rFile = new RandomAccessFile(file, "rw");//读取文件
long point = rFile.length();
rFile.seek(point);// 到达文件尾
rFile.writeBytes(data + "\r\n");
/*rFile.writeUTF(data + "\r\n");
rFile.writeInt(2);*/
rFile.close();
} catch (Exception e) {
return false;
}
return true;
}
在第5行中创建RandomAccessFile对象时,需要传入指定的modle,该modle包含四种方式,如下所示:
- "r":只读方式打开,如果调用任何方式的write时,会抛出IOException;
- "rw":读写方式,如果该文件不存在,则尝试创建该文件;
- "rws":读写方式,相比较"rw",该方式要求对文件的内容或元数据的每个更新都同步写入到底层存储设备;
- "rwd":读写方式,相比较"rw",该方式要求对文件内容的每个更新都同步至底层存储设备。
在第8行中写入的操作是通过writeBytes方式实现,其实RandomAccessFile的写入操作不仅包含对于字节的写入操作,还包括例如9行writeUTF对于字符串的写入,该方式的写入默认采用"UTF-8"的方式,因此不必担心中文乱码的问题,同时还支持对8中基本数据类型的操作。
以上操作是将数据直接写入至文件的末尾位置,实现数据的追加添加,仔细观察上述的代码的第6和7行,在第6行中通过length()方法获取当前文件所有的字节数,通过seek(int)方法跳转至文件末尾的位置,该操作有点类似C语言中的指针概念,即通改变指针指向的位置,修改存储的数据内容,默认新创建时位于0的位置。
因此,我们可以借助该方法修改指定位置的数据,前提是我们知道欲修改数据的位置,如下示例代码:
rFile.seek(point-NUM); //如果已知欲插入的位置,可以直接跳转至插入点
byte[] ch = new byte[LINE_NUM]; //定义欲插入数据的字节数
String str = new String(ch); //初始化 使用空行占位
rFile.seek(point); //重新回到之前文件的末尾位置
rFile.writeBytes(data); //写入需要插入的数据
2.读取
读取操作与本文提到的之前bufferedReader.readLine()方式,基本相同,但我们通过设置RandomAccessFile为"r"方式,可以减少内存和cpu的消耗,且读取速度较快。
同样在读取操作时,我们不仅可以指定读取方式为逐行读取,也可以使用readUTF()方式避免中文乱码;也支持对8种基本数据类型的读取。
public String readLine(String filePath) {
StringBuilder stContent = null;
RandomAccessFile rFile = null;
if (new File(filePath).exists()) {
try {
stContent = new StringBuilder();
rFile = new RandomAccessFile(filePath, "r");
String line = null;
//rFile.readUTF();
//rFile.readInt();
while (null != (line = rFile.readLine())) {//循环遍历
stContent.append(line);
}
rFile.close();
} catch (Exception e) {
//异常处理
LogUtils.i("e "+e.toString());
}
}
return stContent.toString();
}
3. 增加
在添加操作中已经提到可以通过修改point的方式,在指定位置增加数据,但是该操作会影响到增加数据后面的数据,例如之前的数据为"abcdefg",在"c"后增加"123",则修改后的数据则为"abc123g",因此如果不影响之前的数据,需要通过以下操作:
- 通过文件流,先将之前的数据缓存起来;
- 在指定位置增加新的数据;
- 将之前文件流缓存的数据,重写添加到文件中。
public static void insert(long pos, String content) {
File tempFile = null;
try {
tempFile = File.createTempFile("temp", null);
//在虚拟机终止时,请求删除此抽象路径名表示的文件或目录
tempFile.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempFile);
RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
raf.seek(pos);
byte[] buffer = new byte[4];
int num = 0;
while (-1 != (num = raf.read(buffer))) {
fos.write(buffer, 0, num);
}
raf.seek(pos);
raf.writeBytes(content);
FileInputStream fis = new FileInputStream(tempFile);
while (-1 != (num = fis.read(buffer))) {
raf.write(buffer, 0, num);
}
} catch (IOException e) {
e.printStackTrace();
}
}
4.修改
对于文件中已有的内容需要进行修改操作,可以通过修改RandomAccessFile位置的方式实现,但是该方式操作起来比较复杂,且需要详细计算位置参数,本文中建议使用String的split方法,将包含需要替换的数据分隔后,替换操作,流程如下:
- 逐行读取文件,判断是否包含需要替换的内容;
- 查找到需要替换的内容后,使用split方法,将该行分隔;
- 移动RandomAccessFile的位置至行首,先写入分隔前部分的内容,再写入替换的内容,最后写入分隔后部分的内容。
public static void change(String oldStr,String newStr){
try {
RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
String line;
while (null!=(line=raf.readLine())) {
if(line.contains(oldStr)){
String[] split = line.split(oldStr);
raf.seek(split[0].length());
raf.writeBytes(newStr);
raf.writeBytes(split[1]);
}
}
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
5.删除
删除的操作与修改类似,只是将修改的内容至为空,即可。
6.小结
本文首先简介了RandomAccessFile类,重点介绍了其“指针”的概念和读取操作的模式等。通过实际样例代码和流程分析,详细讲解了通过RandomAccessFile类实现了增删改查的操作。
欢迎关注晓涵说(CSDN)、xukang868(github)账号信息,查看更多文章。
网友评论