day20(下)_IO流5(递归穷举与删除,Properties,PrintStream,PrintWriter,SequenceInputStream)

 

1.对目录和文件递归穷举

/*
列出指定目录下的文件或者文件夹,包含子目录中的内容
也就是列出目录下所有的内容
例如:new File(d:\\);
 在d盘下还有一个abc目录而abc下还有文件和目录,而用list()方法只能获取到
 d盘下的文件和目录
*/
package filedemo;
import java.util.Arrays;
import java.io.File;

class FileFilterDemo2{
    /*
     采用了递归(重复调用使用自身功能),即首先遍历该目录下所有文件/目录
     一旦遍历到目录,在调用自身进行遍历.
   */
     public static String level(int n){
      StringBuilder sb=new StringBuilder();
      sb.append("|-");
      while((n--)!=0)
        sb.insert(0," ");//不断的向"|-"前插入空格
      return sb.toString();
     }
     public static void getFiles(File f,int level){
    
      System.out.println(level(level)+f);
      level++;//递归调用为下一级目录
      File[] dirFiles=f.listFiles();//获取到该目录下所有的文件和目录,其中File对象都采用File(File parent,String child)构造
      if(dirFiles==null||dirFiles.length==0)//说明传入的pathName不表示目录或目录为空,那么不需要遍历
          return;
      else
         for(File file : dirFiles){
             if(file.isDirectory())//当file中的对象的child依然是目录接着遍历
                getFiles(file,level);
             else
                System.out.println(level(level)+file);
            }
       
      
}
     
     public static void main(String[] args){
     
        getFiles(new File("f:\\a"),0);
     }
}

_thumb1

关于递归注意事项:

/*
递归注意:
  一定要有出口(也就是限定条件),不然相当于死循环
  递归次数过多,会导致OutOfMemoryError(内存溢出)
*/
class RecursionTest{
  /*十进制->二进制*/
  public static void toBin(int num){
 
   if(num>0)
   {
      toBin(num/2);
      System.out.print(num%2);
   }
  }
  
  /*求前n项和*/
  public static int getSum(int n){
    if(n==1)
     return 1;
    else
      return n+getSum(n-1);
 
  }
  public static void main(String[] args){
     toBin(10);
     System.out.println("\n"+getSum(80000));//不断在内存中开辟空间->OutOfMemoryError(内存溢出) 
  }
}

2.递归删除指定目录:

/*
 删除一个带内容的目录
 删除原理:
    在windows中,删除目录从目录里面往外删除
 从里往外删->递归
*/
package filedemo;
import java.io.File;
class RemoveDir{
    public static void removeDir(File f){
     File[] allFile=f.listFiles();
     if(allFile==null||allFile.length==0)//空文件夹/文件 直接删除
       System.out.println(f.delete());
     
      else{//遍历到最内层->删除,删除完内层->逐步往外删
        for(File files : allFile){
           if(files.isFile())
              System.out.println(files.delete());
           else
              removeDir(files);
        }
         System.out.println(f.delete());//当for执行完,说明该目录下文件已经全部删除,需要删除该目录 
      }
    }
    public static void main(String[] args){
       removeDir(new File("h:\\a"));
    }
}

对于运行结果,(依然是上面遍历的测试目录)会删除9次,会有9个boolean值并且必须都为true,否则可能为同一个文件/目录多次删除

_thumb

3.File练习:

/*
 获取到指定目录中所有java文件的路径,并输出到一个文件中.
思想:
①对目录进行递归搜索
②对于文件只要以.java结尾文件
③把.java文件路径存入ArrayList集合中.

*/
package filedemo;
import java.io.File;
import java.io.IOException;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
class JavaFilesList{
  
  public static void getJavaFiles(File dir,List<File> list){
      File[] files=dir.listFiles();
      
      if(files!=null && files.length!=0)
         for(File f : files)
            if(f.isDirectory())
                getJavaFiles(f,list);
            else
              if(f.getName().endsWith(".java"))
                 list.add(f);
                
  }
  
  public static void writeFile(List<File> list,String javaListFile){
    BufferedWriter bfw=null;
    File[] javaFiles=list.toArray(new File[list.size()]);//集合转数组
    try{
    bfw=new BufferedWriter(new FileWriter(javaListFile));
    for(File f : javaFiles){
      bfw.write(f.getAbsolutePath());//获取到绝对路径写入,list存入File对象不一定封装的是绝对路径,可能是相对路径
      bfw.newLine();
      bfw.flush();
    }
    bfw.write("一共"+list.size()+"个java文件");
   }
   catch(IOException e){
     throw new RuntimeException("写文件异常");
   }
   finally{
    try{
     if(bfw!=null)
         bfw.close();
    }
    catch(IOException e){
       throw new RuntimeException("关闭流异常");
    }
    try{
    Runtime.getRuntime().exec("notepad.exe "+javaListFile);//写完后,自动用记事本打开查看内容
    }
    catch(IOException e){
      e.printStackTrace();
    }
   }
  }
  public static void main(String[] args){    
    
    List<File> list=new ArrayList<File>();
    getJavaFiles(new File("g:\\JavaCode"),list);
    System.out.println(list.size());
    
    writeFile(list,"f:\\JavaFiles.txt");//写入文件
    }
}

将制定文件夹从源路径复制到目的路径下:

/*
采用递归方法遍历文件夹中的内容
例如:C:\s\abc->d:\ 将abc文件夹及文件夹的所有文件/文件夹拷贝到d盘下
              AbsolutePath
abc         
   1.txt      ->d:\abc
   def        ->d:\abc\ def
     2.txt    ->d:\abc\def\ 2.txt
     ghk      ->d:\abc\def\ ghk
       3.txt  ->d:\abc\def\ghk\ 3.txt
   mnl        ->d:\abc\mnl
*/
/*
递归过程:
 1.                      
  src: c:\s\abc 
  dest: d:\
  
  dest<-d:\abc 创建abc
  
  copyFile(c:\s\abc\1.txt,d:\abc\1.txt)
  copyDir(c:\s\abc\def,d:\abc)
2.
  src:c:\s\abc\def
  dest:d:\abc
  
  dest<-d:\abc\def 创建def

  copyFile(c:\s\abc\def\2.txt,d:\abc\def\2.txt)
  copyDir(c:\s\abc\def\ghk,d:\abc\def)
3....
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
class CopyDirectory{
 
 public static void copyFile(File src,File dest)throws IOException{
  FileInputStream fis=new FileInputStream(src);
  FileOutputStream fos=new FileOutputStream(dest);
  byte[] byteArr=new byte[1024*1024];
  int bytes=0;
  while((bytes=fis.read(byteArr))!=-1)
      fos.write(byteArr,0,bytes);
  fis.close();
  fos.close();
 }
 public static void copyDir(File src,File dest)throws IOException{
  File[] files=src.listFiles();
  dest=new File(dest.getPath()+"\\"+src.getName());//例如:第一次 创建 d:+\+abc
  dest.mkdir();                               //     第二次 创建 d:\abc +\+ def
  
  for(File f : files)
    if(f.isFile())
      copyFile(new File(f.getAbsolutePath()),new File(dest.getPath()+"\\"+f.getName()));//例如:当遍历到1.txt时 
                                                                                      //C:\s\abc\1.txt,d:\abc\1.txt
    else//对于文件夹操作
      copyDir(new File(f.getAbsolutePath()),dest);//例如:当遍历到def时
                                                 //C:\s\abc\def,d:\abc
    
 } 
 public static void main(String[] args)throws IOException{
  
  copyDir(new File("C:\\s\\abc"),new File("d:\\"));//abc文件夹以及文件夹中的内容拷贝到D盘下
                                             
  
  System.out.println(new File("d:\\def\\"+"\\"+"\\"+"abc").getPath());
  System.out.println(new File("d:\\def\\"+"abc"));
/*
以上两者结果相同(d:\def\abc),也就是说文件系统中会去除多余的\,但是d:\\与d:结果不同,一个为d:\\,一个为d:
*/
 }

}

4.Properties类方法:

/*
在System类中的获取系统属性用到Properties

Properties 类表示了一个持久的属性集。
Properties 可保存在流中或从流中加载。
属性列表中每个键及其对应值都是一个字符串。 

该集合是和IO技术相结合的集合容器
该对象特点:可以用于键值对形式的配置文件(.ini)
在加载数据时,需要数据有固定格式:Key=Value
*/
package propertydemo;
import java.util.Properties;
import java.util.Set;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.IOException;
class PropertiesDemo{
    
    /*
    自定义方法将文件中的数据存储到Properties集合中
    该文件中的数据是以键值对形式存在
    例如:info.txt: 
    zhangsan=18
    lisi=25
    ...
    */
    public static void storeInProperties()throws IOException{
     BufferedReader bfr=new BufferedReader(new FileReader("info.txt"));
     Properties pro=new Properties();
     String line=null;
     while((line=bfr.readLine())!=null)
      
         if(!line.startsWith("#")){//注释信息不再加入集合
         String[] keyValue=line.split("=");//以"="分割
         pro.setProperty(keyValue[0],keyValue[1]); 
      }
     
     System.out.println(pro);
     bfr.close();
    }
    
    
    /*
    利用Properties中的load方法完成
     void load(InputStream inStream) 
          从输入流中读取属性列表(键和元素对)。 
     void load(Reader reader) 
          按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
          
    */
    public static void storeInProperties_2()throws IOException{
      FileReader fr=new FileReader("info.txt");
      Properties pro=new Properties();
      pro.load(fr);
      System.out.println(pro);
      pro.list(System.out);// void list(PrintStream out)
                            //将属性列表输出到指定的输出流。
     
     /*
     此时如果改变了键值对信息,但是改变后的信息
     依然在内存中,如果想把改变反映到文件中,需要用到
     store方法
     */
     pro.setProperty("zhangsan","30");
     System.out.println(pro);
     FileWriter fw=new FileWriter("info.txt");
     pro.store(fw,"heihei");//heihei为注释信息
                      //在文件中为#heihei
     fr.close();
     fw.close();
    }
    
    
    
    /*Properties存储和获取方法*/
    public static void propertiesMethodDemo(){
        Properties prop=new Properties();
        prop.setProperty("zhang","15");//底层在使用HashTable<Object,Object>的put方法
        prop.put("li","30");
        System.out.println(prop);
        
        Set<String> names=prop.stringPropertyNames();//返回Key的set集合
        for(String n : names)
          System.out.println(n+"="+prop.getProperty(n));
    
    }
    
    public static void main(String[] args)throws IOException{
      propertiesMethodDemo();
      System.out.println();
      
      storeInProperties();
      System.out.println();
      storeInProperties_2();

    } 
}

Properties_thumb

5.Properties的load与store练习:

/*
需求:用于记录应用程序运行次数.如果使用次数已到,那么
     给出注册提示.
分析:
  很容易想到的是:计数器
  可是该计数器
用程序的退出,该计数器也在内存中消失了
下一次在启动该程序,又重新开始从0计数.
这样不是所需要的

程序即使结束,该计数器的值也存在
下次程序启动会先加载该计数器的值
并+1后重新存储起来

所以需要建立一个配置文件.用于记录软件使用次数

该配置文件使用键值对的形式
这样便于阅读数据,并操作数据

键值对数据是map集合.
数据是以文件形式存储,使用io技术
那么map+io-->properties

配置文件可以实现应用程序数据共享
*/
package propertiestest;
import java.util.Properties;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.File;
class PropertiesTest{
 /*每次程序运行获取使用次数(counts),并在获取完之后,++counts存入到配置文件中*/
 public static void checkCounts()throws IOException{ 
  File f=new File("set.ini");
  Properties pro=new Properties();
 if(!f.exists())//文件第一次不存在需要创建
   f.createNewFile();
 FileReader fr=new FileReader(f);
 pro.load(fr);
 
 int count=0;
String value=pro.getProperty("counts");
if(value!=null)//新建文件中没有counts,此时不再获取,只写
    count=Integer.parseInt(pro.getProperty("counts"));

if(count>=3){//该软件只能够使用三次
        System.out.println("免费次数已用完");
        return;//>=3不再写入直接返回
 }

FileWriter fw=new FileWriter(f);
pro.setProperty("counts",(++count)+"");
pro.store(fw,"number of use"); 
fr.close();
fw.close();
/*
 关于pro.store():
    所有写入各个项后,刷新输出流(内部在把属性集写入输出流后调用了flush)
    此方法返回后,输出流仍保持打开状态。 
    但没有关闭写入流
*/

}

public static void main(String[] args)throws IOException{
   checkCounts();
 }
}
/*
注意:①配置文件不存在,需要创建
     ②需要写入键值对
     
*/

6.PrintStream与PrintWriter

/*
打印流:
 该流提供了打印方法,可以将各种数据类型的数据都原样打印
字节打印流:PrintStream
 PrintStream(File file) 
  file-
    同下
 PrintStream(String fileName) 
 fileName -
 要用作此打印流目标的文件名称。
 如果存在该文件,则将其大小截取为零;
 否则,创建一个新文件。将输出写入文件中,并对其进行缓冲处理。 

 PrintStream(OutputStream out) 

字符打印流:PrintWriter
PrintWriter(File file) 
PrintWriter(OutputStream out)
根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。此便捷构造方法创建必要的中间 OutputStreamWriter,后者使用默认字符编码将字符转换为字节。
  
PrintWriter(String fileName) 
PrintWriter(Writer out)//比PrintStream多了一个可以接收字符输出流的构造函数
*/
package printdemo;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.FileWriter;
class PrintStreamDemo{
    public static void main(String[] args)throws IOException{
     BufferedReader bufr =new BufferedReader(new InputStreamReader(System.in));
     //PrintWriter out=new PrintWriter(System.out);//PrintWriter通用性比较强,在写入流后,需要刷新
     /*
          System.out在内部会被封装为new BufferedWriter(new OutputStreamWriter(System.out))
     */
     //PrintWriter out=new PrintWriter(System.out,true);//autoFlush-boolean 变量;如果为 true,则 println、printf 或 format 方法将刷新输出缓冲区,下面由于用到println所以可以不用out.flush
       PrintWriter out=new PrintWriter(new FileWriter("PrintWriter.txt"),true);//true含义同上,这样写可以把new FileWriter中的数据刷新到文本中,而不用在out.flush  
                       
     String line=null;
     while((line=bufr.readLine())!=null){
         out.println(line);//将值打印初始化的流对象中
         //out.flush();//刷新的是初始化时的流对象
     }
     bufr.close();
     out.close();
    }
}

7.SequenceInputStream:

/*
SequenceInputStream(顺序输入流):
    表示其他输入流的逻辑串联。
    它从输入流的有序集合开始,
    并从第一个输入流开始读取,
    直到到达文件末尾,
    接着从第二个输入流读取,
    依次类推,直到到达包含的最后一个输入流的文件末尾为止。 
该类的由来:
  例如:
  已知:四个文件:src1.txt,src2.txt,src3.txt,dest.txt;
  需求:把前三个文件中数据写入到dest.txt中
  分析:
   src1.txt--->FileReader("src.txt")--->FileWriter("dest.txt",true)-->dest.txt
   src2.txt,src3.txt同理    
   每个源和目的需要单独的IO流对象操作,非常麻烦
 SequenceInputStream作用:对多个源依次操作,更加方便.


*/
package sequenceinputstream;
import java.io.*;
import java.util.*;
class SequenceDemo{
    public static void main(String[] args)throws IOException{
    /*
    SequenceInputStream(Enumeration<? extends InputStream> e) 
    该构造函数形参为枚举->集合Vector<E>的elements返回Enumeration<E>
    */    
     Vector<FileInputStream> vector=new Vector<FileInputStream>();
     vector.add(new FileInputStream("f:\\src1.txt"));
     vector.add(new FileInputStream("f:\\src2.txt"));
     vector.add(new FileInputStream("f:\\src3.txt"));
     Enumeration<FileInputStream> en=vector.elements();
     SequenceInputStream sis=new SequenceInputStream(en);
     BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("f:\\dest.txt"));
     //FileOutputStream fis=new FileOutputStream("f:\\dest.txt");
     int aByte=0;
     //byte[] b=new byte[1024];
    
     while((aByte=sis.read())!=-1){
        bufos.write(aByte);
        bufos.flush();
     }
     /*
     int bytes;
     while((bytes=sis.read(b))!=-1)
        fis.write(b,0,bytes); 
      }
      */
      bufos.close();
      sis.close();

    
   }
}

 

文件切割和合并综合练习:

/*
切割文件:其实就是一个源(该文件)对应多个目的(多个碎片文件)
切割
    ①需要一个输入流对象关联该文件
    ②为了控制每次读取大小,自定义一个缓冲(数组)
    ③循环读取,每次读取将读取的数据,通过输出流写入一个文件中,直至源文件末尾(-1)
合并
    多个源对应一个目的,这里可以采用SequenceInputStream
*/

import java.io.FileInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Iterator;
class SplitFileTest{
   /*当文件不太大时,指定的缓冲区可以为分割的文件大小*/
   public static void splitFile_1(String file)throws IOException{
    /*以切割图片为例*/
   FileInputStream fis=new FileInputStream(file);
   byte[] b=new byte[1024*1024];//每次最多分割1MB
   FileOutputStream fos=null;
   int bytes=0,count=1;
   while((bytes=fis.read(b))!=-1){
      fos=new FileOutputStream("f:\\part"+(count++)+".part");//每次新建一个流对象关联一个新的文件  
      fos.write(b,0,bytes); 
      fos.close();//每次循环关闭上次的流对象
   }
   fis.close();
 }
 /*
当文件特别大(例如2GB),而需要按400MB分割,不建议定义400MB缓冲区,定义小点缓冲区40MB,使切割文件满足制定大小*/
  public static File[] splitFile_2(File f)throws IOException{
   FileInputStream fis=new FileInputStream(f);
   byte[] byteArr=new byte[1024*1024];
   ArrayList<File> arrList=new ArrayList<File>();
   int bytes=0;
  int count=0;
  File wf=null;
  FileOutputStream fos=null;
  while((bytes=fis.read(byteArr))!=-1){
     if(wf==null||wf.length()==2*1024*1024){
        if(fos!=null)
            fos.close();//关闭与上一个文件相关联的写入流对象
         wf=new File("C:\\Users\\ZhangHongQ\\Desktop\\java复习\\"+(++count)+".part");
         arrList.add(wf);
         fos=new FileOutputStream(wf);
     }
     fos.write(byteArr,0,bytes);
  }
  fis.close();
  return arrList.toArray(new File[arrList.size()]);
 }
  
  /*对以上分割的文件进行合并*/
  public static void mergeFile(File[] file)throws IOException{
    ArrayList<FileInputStream>al=new ArrayList<FileInputStream>();
    for(File f : file)
      al.add(new FileInputStream(f));
    final Iterator<FileInputStream>it=al.iterator();
    SequenceInputStream sis=new SequenceInputStream(new Enumeration<FileInputStream>(){
     public boolean hasMoreElements(){
       return  it.hasNext();
     }
     public FileInputStream nextElement(){
     
       return it.next();
     }
    });
    FileOutputStream fos=new FileOutputStream(new File("C:\\Users\\ZhangHongQ\\Desktop\\java复习\\Bandari.mp3"));
    int aByte=0;
    while((aByte=sis.read())!=-1)
      fos.write(aByte);
    sis.close();
    fos.close();
  }
  public static void main(String[] args)throws IOException{
    
    File[] file=splitFile_2(new File("F:\\SONG\\Bandari - 爱尔兰风笛.mp3"));//例如:8.93MB的MP3文件
    System.out.println(Arrays.toString(file));//2MB 2MB 2MB 2MB 934KB
    
    mergeFile(file);
  }
}
posted @ 2013-05-30 18:45  伊秋  阅读(373)  评论(0编辑  收藏  举报