io流的相关类继承关系:
参考文章:
io流图来源
分类
IO流分类:流向
输入流 读取数据 FileReader Reader
输出流 写出数据 FileWriter Writer
数据类型
字节输入流 InputStream
字节输出流 OutputStream
字符输入流 Reader
字符输出流 Writer
功能来源
功能:
节点流:运用到特定地方读写操作的流,如:文件FileOutputStream,FileInputStream
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。(这个是装饰流)
BufferedImputStrean
BufferedOutputStream
BufferedReader
BufferedWrite
合并拆分文件
其可以使用的方法有很多种。
从流的种类不同,和操作字节的方法不同,也就可以组合成很多种方法;这里都只是举两例;
拆分文件
思路:
思路是:将全部内容读取到内存中,然后再写入文件; 按照底层使用方法不同:方式一:
使用数组的方法Arrays.copyOfRange,api如下:注意点:
左闭右开,左边包括,右边不包括;
还有一点,第一个参数是目标数组,第二个参数是目标数组要复制的起始索引,第二个参数是复制的最后索引;
代码如下:
/*
* 拆分文件
* 把原文件的内容读取到内存中,
* 然后将文件挨个分到子文件中
*/
public static void splitFile(File srcFile,int eachSize){
if(srcFile.length()==0){
throw new RuntimeException("文件为0,不可拆分");
}
byte[] fileContent=new byte[(int) srcFile.length()];
try {
/*将文件读取到内存中*/
FileInputStream fis=new FileInputStream(srcFile);
fis.read(fileContent);
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
// 计算需要划分成多少个子文件
int fileNumber;
// 如果文件的长度能整除
if(fileContent.length % eachSize==0){
fileNumber=fileContent.length/eachSize;
}else {//如果文件的长度不能整除
fileNumber=fileContent.length/eachSize+1;
}
for (int i=0;i<fileNumber;i++) {
String eachFileName=srcFile.getName()+"_"+i;
// 这个方法是根据父类的目录创建指定路径名的文件
File eachFile=new File(srcFile.getParent(),eachFileName);
byte[] eachContent;
/*
* 从源文件的内容,复制部分数据到子文件
* */
// 如果不是最后一个文件
if(i!=fileNumber-1){
eachContent= Arrays.copyOfRange(fileContent,eachSize*i,eachSize*(i+1));
}else{//如果是最后一个
eachContent = Arrays.copyOfRange(fileContent, eachSize * i, fileContent.length);
}
/*
* 写文件
* */
try {
FileOutputStream fos=new FileOutputStream(eachFile);
fos.write(eachContent);
System.out.printf("输出子文件%s,其大小是%d字节%n",eachFile.getAbsoluteFile(),eachFile.length());
} catch (IOException e) {
e.printStackTrace();
}
}
}
方式二:
使用缓存输出流,即BufferedOutputStream的write方法,该方法的底层是用System.arraycopy(src, srcPos, dest, destPos, length),这个底层的方法不看了,看下writer方法,api如下:
注意点:
这个跟上面的不同!!!!!
这个跟上面的不同!!!!!
这个跟上面的不同!!!!!
这两个参数是给定一个起始位置,然后给定写入的长度,和上面那个方法是不一样的
/*
* 拆分文件
* */
public static void splitFile(String fileLoad,int eachSize) throws IOException {
// 创建一个给定文件目录File类型
File f=new File(fileLoad);
// 如果该文件不存在,或者不是文件或者文件大小为0
if(!f.exists() || !f.isFile() || f.length()==0){
throw new RuntimeException("该文件不存在,或者为文件夹,或者长度为0,总之无法拆分");
}
// 文件的长度为
byte[] all = new byte[(int) f.length()];
// 总共有多少份子文件
int fileCount;
// 如果文件长度能整除
if(f.length() % eachSize==0){
fileCount=all.length/eachSize;
}else {//如果文件长度不能整除
fileCount=(all.length/eachSize)+1;
}
// 使用BufferInputStream
BufferedInputStream bis;
/*
* 通过文件的长度除以每一份的大小,确定有多少份之后
* 创建对应的份数,也就是要写入到指定的文件名中
* */
// 定义子文件
File subFile;
// 定义缓存输出流
BufferedOutputStream bos = null;
// 遍历文件数量
for (int i = 0; i < fileCount; i++) {
// 定义子文件名
String eachFileName=f.getName()+"_"+i;
/*
* 需要用到根据父路径创建文件,否则该文件就会默认创建在
* 当前项目地址下
* */
subFile=new File(f.getParent(),eachFileName);
bis=new BufferedInputStream(new FileInputStream(f));
bis.read(all);
bos=new BufferedOutputStream(new FileOutputStream(subFile));
// 第一个参数是写入的起始,第二个是写入的字节数
bos.write(all,eachSize*i,eachSize);
bos.flush();
System.out.printf("输出子文件%s,其大小是%d字节%n",subFile.getAbsoluteFile(),subFile.length());
bos.close();
bis.close();
}
}
合并文件
思路:
思路是:合并文件跟拆分不一样,不是全部读取到内存中,再写入;当然全部读取再写如,也是可行的!这里不讨论;这里是一边读取,一边写入(咋感觉跟多线程一样。。。。);方式一:
private static void murgeFile(String folder, String fileName) {
try {
// 合并的目标文件
File destFile = new File(folder, fileName);
FileOutputStream fos = new FileOutputStream(destFile);
int index = 0;
while (true) {
//子文件
File eachFile = new File(folder, fileName + "-" + index++);
//如果子文件不存在了就结束
if (!eachFile.exists())
break;
//读取子文件的内容
FileInputStream fis = new FileInputStream(eachFile);
byte[] eachContent = new byte[(int) eachFile.length()];
fis.read(eachContent);
fis.close();
//把子文件的内容写出去
fos.write(eachContent);
fos.flush();
System.out.printf("把子文件 %s写出到目标文件中%n",eachFile);
}
fos.close();
System.out.printf("最后目标文件的大小:%,d字节" , destFile.length());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
第二种方式,同样使用缓存输入输出流;
方式二:
/*
* 合并文件
* */
public static void mergeFile(String fileLoad,String fName) throws IOException {
File f=new File(fileLoad);
// 如果该路径不是一个文件夹,不存在,或者没有文件
if(f.isFile() && !f.exists() && f.length()==0){
throw new RuntimeException("该文件夹不存在,或者为文件,或者长度为0,总之无法合并");
}
// 定义写入到的文件
File mergeFile =new File(fileLoad,fName);;
// 定义输出输入流
BufferedInputStream bis=null;
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(mergeFile));
// 获取该文件夹下的文件,不包含子文件
File[] files = f.listFiles();
// 遍历这些文件
for (File file : files) {
if(file.isFile() && file.getName().contains("_")){
bis=new BufferedInputStream(new FileInputStream(file));
byte[] fileContent = new byte[(int) file.length()];
// 先读取
bis.read(fileContent);
bis.close();
// 后写入
bos.write(fileContent);
bos.flush();
}
}
bos.close();
}
注意点
读取文件,写入的话,每一次new 一个新的BufferedOutputStream,要是给定的文件对象参数是一样的,就会重写文件的内容,因而言,要想实现追加写入的效果的话,其写入创建的流,一定要是单例;如下错误示范:
// 定义输出输入流
BufferedInputStream bis=null;
// 获取该文件夹下的文件,不包含子文件
File[] files = f.listFiles();
// 遍历这些文件
for (File file : files) {
if(file.isFile() && file.getName().contains("_")){
bis=new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(mergeFile));
byte[] fileContent = new byte[(int) file.length()];
// 先读取
bis.read(fileContent);
bis.close();
// 后写入
bos.write(fileContent);
bos.flush();
}
}
bos.close();
将这个:
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(mergeFile));
代码块写入循环,就会导致每一次都new一个输出流对象,也就是会将指定的file对象所在内容重新写入!!!
切记切记切记!!!
流关闭的方式
关闭流的话,最好是不要在try中声明流的引用,因为这样假如文件不存在亦或者出现读取和写入的时候出现错误就会导致抛出异常;使用标准的方式,就是try-catch-finally,将流的关闭放到finally中,除此之外还要判断流的引用是否为null
在jdk7.0版本中,其超类OutputStream实现了一个AutoCloseable的接口;实现该接口的类都可以,在try()的括号中实例化流;如前面的合并文件,如下:
/*
* 合并文件
* */
public static void mergeFile(String fileLoad,String fName){
File f=new File(fileLoad);
// 如果该路径不是一个文件夹,不存在,或者没有文件
if(f.isFile() && !f.exists() && f.length()==0){
throw new RuntimeException("该文件夹不存在,或者为文件,或者长度为0,总之无法合并");
}
// 定义写入到的文件
File mergeFile =new File(fileLoad,fName);
// 获取该文件夹下的文件,不包含子文件
File[] files = f.listFiles();
// 遍历这些文件
for (File file : files) {
if(file.isFile() && file.getName().contains("_")){
try(
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(mergeFile))
) {
byte[] fileContent = new byte[(int) file.length()];
// 先读取
bis.read(fileContent);
bos.write(fileContent);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
使用这种方式,让代码看上去更加清爽!!!
未完待续。.。。