随机读取写入类
RandomAccessFile支持使用字节流从随机指定的位置开始读取指定长度的数据。由此可以衍生出很多功能,比如文件分块等。
看随机读取写入类RandomAccessFile的构造方法:
发现该构造方法在只能传入文件对象或者文件路径,不能传入一线流或者缓冲流,并且构造方法必须指定读取模式,由四种模式,分别是:“r”,“rw”,“rws”,rwd"。常用的是前两种,只读,同时读写。
下面是使用随机读取写入类RandomAccessFile的程序实例
package com.company;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* @author 发达的范
* @version 1.0
* @date 2021/04/09 21:02
*/
public class RandomTest {
public static void main(String[] args) throws IOException {
// RandomRead1();
// RandomRead2();
RandomRead3();
}
//从指定位置开始,一直到文件末尾,读取文件
public static void RandomRead1() throws IOException {
//随机读取流的构造器只要文件路径或文件对象,不能套其他节点流或者缓冲流
RandomAccessFile raf = new RandomAccessFile(new File("src/com/company/PrintTest.java"), "r");
raf.seek(7);//从第七个字节开始读取,一直到文件末尾
int len = -1;
byte[] flush = new byte[1024];
while ((len = raf.read(flush)) != -1) {
System.out.println(new String(flush, 0, len));
}
raf.close();
}
//指定起始位置开始读取指定长度的一段内容
public static void RandomRead2() {
int beginPos = 10;//开始位置
int actualSize = 1026;//这个actualSize是我想实际读取的数据大小
try {
RandomAccessFile raf = new RandomAccessFile(new File("src/com/company/Decorate02.java"), "r");
raf.seek(beginPos);//定位到初始位置
int len = -1;
byte[] flush = new byte[1024];//每次读取的长度
while ((len = raf.read(flush)) != -1) {
if (actualSize > len) {//如果实际长度大于缓冲数组的长度说明一次没有读完
System.out.println(new String(flush, 0, len));
actualSize -= len;//减去此次读取的长度是为了标记最后一次读取到的有效数据
} else {
System.out.println(new String(flush, 0, actualSize));
break;
}
}
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//分块,分多少块
public static void RandomRead3() {
File src = new File("src/com/company/Decorate02.java");
long len = src.length();//文件总长度
int blockSize = 256;//每块大小
int size = (int) Math.ceil(len * 1.0 / blockSize);//计算分多少块,ceil向上取整
System.out.println(size);
int beginPos = 0;//起始位置
//这样做的目的是什么?actualSize的意思和RandomRead2()方法的意思相同,都是指实际想要读多少个字节的数据
//做判断是因为我实际想读的数据可能比文件总字节数还要多
//目的是找每块的起始位置和每块的实际大小
int actualSize = (int) (blockSize > len ? len : blockSize);
for (int i = 0; i < size; i++) {
beginPos = i * blockSize;
if (i == size - 1) {//最后一块
actualSize = (int) len;
} else {
actualSize = blockSize;
len -= actualSize;//剩余量
}
System.out.println(i + "--" + beginPos + "--" + actualSize);
split(beginPos, actualSize);
}
}
//分割方法
public static void split(int beginPos, int actualSize) {
try {
RandomAccessFile raf = new RandomAccessFile(new File("src/com/company/Decorate02.java"), "r");
raf.seek(beginPos);
int len = -1;
byte[] flush = new byte[256];
while ((len = raf.read(flush)) != -1) {
if (actualSize > len) {
System.out.println(new String(flush, 0, len));
actualSize -= len;
} else {
System.out.println(new String(flush, 0, actualSize));
break;
}
}
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
三个方法均能正常运行,由于运行结果是控制台输出的字符,太多,不方便截图。
使用随机读取写入类RandomAccessFile分割文本文件并写出的程序实例:
package com.company;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* @author 发达的范
* @version 1.0
* @date 2021/04/09 21:02
*/
public class RandomTest02 {
public static void main(String[] args) throws IOException {
RandomRead3();
}
//分块,分多少块,每块的起始位置
public static void RandomRead3() {
String srcPath = "src/com/company/Decorate02.java";
File src = new File(srcPath);
long len = src.length();//文件总长度
int blockSize = 256;//每块大小
int size = (int) Math.ceil(len * 1.0 / blockSize);//计算分多少块
System.out.println(size);
int beginPos = 0;//起始位置
//这样做的目的是什么?actualSize的意思和RandomRead2()方法的意思相同,都是指实际想要读多少个字节的数据
//做判断是因为我实际想读的数据可能比文件总字节数还要多
//目的是找每块的起始位置和每块的实际大小
int actualSize = (int) (blockSize > len ? len : blockSize);
for (int i = 0; i < size; i++) {
beginPos = i * blockSize;
if (i == size - 1) {//最后一块
actualSize = (int) len;
} else {
actualSize = blockSize;
len -= actualSize;//剩余量
}
System.out.println(i + "--" + beginPos + "--" + actualSize);
split(i, beginPos, actualSize, srcPath);
}
}
public static void split(int i, int beginPos, int actualSize, String srcPath) {
try {
RandomAccessFile raf = new RandomAccessFile(new File(srcPath), "r");
//注意此处输出文件夹使用的是相对路径,程序识别为时候是在工程文件夹里面找,所以要先在工程文件夹里面先建立一个
//对应的文件夹
RandomAccessFile raf2 = new RandomAccessFile(new File("Random/"+i+"Decorate02.java"), "rw");
raf.seek(beginPos);
int len = -1;
byte[] flush = new byte[256];
while ((len = raf.read(flush)) != -1) {
if (actualSize > len) {
raf2.write(flush, 0, len);
actualSize -= len;
} else {
raf2.write(flush, 0, actualSize);
break;
}
}
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
下面是使用面向对象的思想写分割文件(图片)的程序:
package com.company;
import java.io.IOException;
/**
* @author 发达的范
* @version 1.0
* @date 2021/04/09 21:02
*/
public class RandomTest02 {
public static void main(String[] args) throws IOException {
SplitFile splitFile = new SplitFile("E:/(A)PostgraduateFlies/JavaLearning/javaEE.jpg",
"E:/(A)PostgraduateFlies/JavaLearning/JavaProjects/TestingProject02/Random" ,4096);
splitFile.split();
}
}
package com.company;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* 封装分割文件的程序
*
* @author 发达的范
* @version 1.0
* @date 2021/04/12 15:10
*/
public class SplitFile {
private String srcPath;//源文件路径
private String destDir;//目标文件路径
private List<String> destPaths;//存放分割后文件的名称的List容器
private int size;//分成多少块
private int blockSize;//每块大小
private long len;//源文件的总大小
public SplitFile() {
}
public SplitFile(String srcPath, String destDir, int blockSize) {
this.srcPath = srcPath;
this.destDir = destDir;
this.blockSize = blockSize;
this.destPaths = new ArrayList<>();//容器实例化
init();
}
private void init() {
File src = new File(srcPath);
this.len = src.length();//文件总长度
this.size = (int) Math.ceil(len * 1.0 / blockSize);//计算分多少块
System.out.println(src.getName()+"被分成了"+size+"块");
for (int i = 0; i < size; i++) {
this.destPaths.add(this.destDir +File.separator+ i + "-" + src.getName());//向分割后的目标文件夹添加对应的路径
}
}
public void setSrcPath(String srcPath) {
this.srcPath = srcPath;
}
public void setBlockSize(int blockSize) {
this.blockSize = blockSize;
}
public void split() {
int actualSize = 0;
for (int i = 0; i < size; i++) {
int beginPos = i * blockSize;
if (i == size - 1) {//最后一块
actualSize = (int) len;
} else {
actualSize = blockSize;
len -= actualSize;//剩余量
}
try {
RandomAccessFile raf = new RandomAccessFile(this.srcPath, "r");
raf.seek(beginPos);
RandomAccessFile raf2 = new RandomAccessFile(this.destPaths.get(i), "rw");
int len = -1;
byte[] flush = new byte[256];
while ((len = raf.read(flush)) != -1) {
if (actualSize > len) {
raf2.write(flush, 0, len);
actualSize -= len;
} else {
raf2.write(flush, 0, actualSize);
break;
}
}
raf.close();
raf2.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果:
下面是把上面分割之后的文件再合并起来的程序:
package com.company;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* 把分割之后的文件正确合并到一个文件
*
* @author 发达的范
* @version 1.0
* @date 2021/04/12 16:42
*/
public class combine {
public static void main(String[] args) {
String srcPath = "E:/(A)PostgraduateFlies/JavaLearning/JavaProjects/TestingProject02/Random";
String destPath="E:/(A)PostgraduateFlies/JavaLearning/JavaProjects/TestingProject02/Random/java.jpg";
File src = new File(srcPath);
// List<String> srcPaths = new ArrayList<>();//源文件夹们
File[] srcFiles = src.listFiles();
// for (int i = 0; i < srcFiles.length; i++) {
// srcPaths.add(srcFiles[i].getAbsolutePath());
// }
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destPath,true));
for (int i = 0; i < srcFiles.length; i++) {
// InputStream bis = new BufferedInputStream(new FileInputStream(srcPaths.get(i)));
InputStream bis = new BufferedInputStream(new FileInputStream(srcFiles[i]));
int len=-1;
byte[] flush = new byte[1024];
while ((len = bis.read(flush)) != -1) {
bos.write(flush, 0, len);
}
bis.close();
}
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
程序正常运行,并且合成的文件大小格式也正确,但是这个文件不能打开,暂时不清楚是什么原因。
合并流SequenceInputStream
把多个InputStream合并成一个序列输入流,方便操作。
下面看序列流SequenceInputStream的具体程序实例:
package com.company;
import java.io.*;
import java.util.Vector;
/**
* 使用序列流SequenceInputStream把分割之后的文件正确合并到一个文件
*
* @author 发达的范
* @version 1.0
* @date 2021/04/12 16:42
*/
public class combine_Vector {
public static void main(String[] args) {
String srcPath = "E:/(A)PostgraduateFlies/JavaLearning/JavaProjects/TestingProject02/Random";
String destPath = "E:/(A)PostgraduateFlies/JavaLearning/JavaProjects/TestingProject02/Random/java.jpg";
File src = new File(srcPath);
// List<String> srcPaths = new ArrayList<>();//源文件夹们
File[] srcFiles = src.listFiles();
// for (int i = 0; i < srcFiles.length; i++) {
// srcPaths.add(srcFiles[i].getAbsolutePath());
// }
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destPath, true));
Vector<InputStream> vi = new Vector<>();//Vector是一个容器,泛型为输入流
SequenceInputStream sis = null;//序列流,把所有的流集中处理
for (int i = 0; i < srcFiles.length; i++) {
vi.add(new BufferedInputStream(new FileInputStream(srcFiles[i])));
}
sis = new SequenceInputStream(vi.elements());//把Vector容器中的所有输入流实例化成
//一个SequenceInputStream的对象
int len = -1;
byte[] flush = new byte[1024];
while ((len = sis.read(flush)) != -1) {
bos.write(flush, 0, len);
}
bos.flush();//关闭流之前先flush一下,放防止内存中滞留数据
sis.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这个程序也能正常运行,并且合成的文件大小格式也正确,但是这个文件也不能打开。
具体分析序列流的用法就是:
- 使用一个Vector容器添加泛型为,专门存放InputStream流;
- 实例化一个序列流SequenceInputStream对象,然后把Vector容器中所有的InputStream添加(add)到序列流对象中;
- 直接使用过序列流对象读取所有的输入流的数据。