深度遍历演示:
package day22;
import java.io.File;
public class FileTest {
/**
* 需求:对指定目录进行所有内容的列出。(包含子目录,前面学的都只能列出当前目录内容,子目录不行)
* 也可以理解为深度遍历。
*/
public static void main(String[] args) {
File dir=new File("e:\\javatest");
listAll_2(dir,0);
}
public static void listAll_1(File dir) {
System.out.println("dir:"+dir.getAbsolutePath());
//获取指定目录下当前的所有文件或者文件对象
File[] files=dir.listFiles();
for(int x=0;x if(files[x].isDirectory()){ listAll_1(files[x]); //这就是递归 } else System.out.println("file:"+files[x].getAbsolutePath()); } } public static void listAll_2(File dir, int level) { //定义一个计数器,记录当前文件的层级,从而决定前面加几个空格,为什么不在方法中定义?这就和递归有关了。 System.out.println(getSpace(level)+dir.getName()); level++; File[] files=dir.listFiles(); for(int x=0;x if(files[x].isDirectory()){ listAll_2(files[x],level); //这就是递归 } else System.out.println(getSpace(level)+files[x].getName()); } } private static String getSpace(int level) { StringBuilder sb=new StringBuilder(); for(int x=0;x sb.append(" "); } return sb.toString(); } } 上面代码中涉及到了递归,接下来就学习一下递归的知识。 递归: 函数自身直接或者间接地调用到了自身。 * 什么时候使用? * 一个功能在被重复使用,并且每次使用时,参与运算的结果和上一次调用有关。 * 这时可以用递归来解决问题。 * 注意:1.递归必须明确条件,否则容易栈溢出 * 2.递归的次数别太多,否则栈溢出异常。 package day22; public class DiGuiDemo { /** * 递归演示。 */ public static void main(String[] args) { /* * 递归:函数自身或者直接或者间接调用到了自身 * 什么时候使用? * 一个功能在被重复使用,并且每次使用时,参与运算的结果和上一次调用有关。 * 这时可以用递归来解决问题 * 注意:1.递归必须明确条件,否则容易栈溢出 * 2.递归的次数别太多,否则栈溢出异常。 */ show(); } public static void show(){ toBin(6); //把一个数转换成二进制 } //直接调用自身 public static void toBin(int i) { if(i>0){ toBin(i/2); System.out.print(i%2); } } //间接调用自身 public static void method(){ bala(); } public static void bala(){ method(); } } Properties集合 * Map * |--Hashtable; * |--Properties; * * Properties集合特点: * 1.该集合中的键和值都是字符串。 * 2.集合中的数据可以保存到流中,或者从流中获取出来。 * * 通常该集合用于操作以键值对形式存在的配置文件。 package day22; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.Properties; import java.util.Set; public class PropertiesDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { //properties集合的基本功能存和取演示 propertiesDemo(); //properties集合和流对象相结合的演示 propertiesDemo2(); //持久化,将集合的数据存储到文件中 propertiesDemo3(); //把文件中的数据读取到集合中。 propertiesDemo4(); //修改文件的配置信息 propertiesDemo5(); } public static void propertiesDemo() { //创建一个Properties集合 Properties prop=new Properties(); //存储元素 prop.setProperty("zhangsan", "30"); prop.setProperty("lisi", "32"); prop.setProperty("wangwu", "36"); prop.setProperty("zhaoliu", "20"); //修改元素 prop.setProperty("wangwu", "26"); //取出所有元素 Set for(String name:names){ String value=prop.getProperty(name); System.out.println(name+":"+value); } } public static void propertiesDemo2() { Properties prop=new Properties(); prop.setProperty("zhangsan", "30"); prop.setProperty("lisi", "32"); prop.setProperty("wangwu", "36"); prop.setProperty("zhaoliu", "20"); prop.list(System.out); //list方法,列出集合中的键和值,一般调试时用。 } public static void propertiesDemo3() throws IOException{ Properties prop=new Properties(); prop.setProperty("zhangsan", "30"); prop.setProperty("lisi", "32"); prop.setProperty("wangwu", "36"); prop.setProperty("zhaoliu", "20"); // 持久化 //需求:集合中的这些字符串键值信息方法一结束就会消失,想要将它们持久化存储到文件中。 // 就要用到store方法,这需要关联输出流,所以创建一个。 FileOutputStream fos=new FileOutputStream("info.txt"); // 将集合数据存储到文件中,使用store方法。 prop.store(fos, "name+age"); fos.close(); } public static void propertiesDemo4() throws IOException{ Properties prop=new Properties(); //需求:与Demo3相反,这次要把硬盘中的数据读到集合中。 //注意:文件中的数据必须是键值对。 FileInputStream fis=new FileInputStream("info.txt"); //使用load方法。 prop.load(fis); fis.close(); prop.list(System.out); //这个list就是验证一下确实读取到集合中了。 } public static void propertiesDemo5() throws IOException{ /* * 需求:对已有的配置文件信息进行修改。 * 步骤:先读取这个文件,并将这个文件的键值数据存储到集合中。 * 再通过集合对数据进行修改,最后通过流将修改后的数据存储到文件中。 */ File file=new File("info.txt"); if(!file.exists()){ file.createNewFile(); } FileReader fr=new FileReader("info.txt"); Properties prop=new Properties(); prop.load(fr); prop.setProperty("wangwu", "16"); FileWriter fw=new FileWriter(file); prop.store(fw, ""); fr.close(); fw.close(); prop.list(System.out); } } properties的一个练习: package day22; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Properties; public class PropertiesTest { /** * 需求:定义一个功能,获取一个应用程序运行的次数,若超过5次, * 给出 “使用次数已到,请注册” 的提示,并不要再运行程序。 * * 思路:1.得有一个计数器,每次程序启动就计数一次,并且是在原有的基础上计数。 * 2.计数器就是一个变量。突然冒出一个想法,程序启动时进行计数,计数器必须存在于内存并进行运算。 * 可是程序一结束,计数器不就消失了吗?再次启动程序,计数器不就重置了嘛,但我们需要多次启动同一个 * 应用程序,启动的是同一个计数器,这就需要计数器的生命周期变长,从内存存储到硬盘文件中。 * 3.如何使用这个计数器呢? * 首先,程序启动时,应该先读取这个用于记录计数器信息的配置文件,获取上一次计数器次数, * 其次,对该次数就行自增,并且自增后的次数要重新存储到配置文件中。 * 4.文件中的信息该如何进行存储呢? * 直接存储次数值可以,但不明确该数据的含义,所以起名字就很重要了。这就有了名字和值得对应,就要用键值对。 * 可是映射关系map搞定,又要读取硬盘上的数据,所以map+IO=Properties。 * @throws IOException */ public static void main(String[] args) throws IOException { getAppCount(); } public static void getAppCount() throws IOException{ //将配置文件封装成对象 File confile=new File("count.properties.txt"); if(!confile.exists()) confile.createNewFile(); FileInputStream fis=new FileInputStream(confile); Properties prop=new Properties(); prop.load(fis); //从集合中通过键获取次数 String value=prop.getProperty("time"); //定义计数器,记录获取到的次数 int count=0; if(value!=null){ count=Integer.parseInt(value); if(count>=5){ //System.out.println("使用次数已到,请注册"); //return; //这样写不好,函数结束了,其他程序还在运行。 throw new RuntimeException("使用次数已到,请注册"); } } count++; //将改变后的次数重新存储到集合中 prop.setProperty("time", count+""); FileOutputStream fos=new FileOutputStream(confile); prop.store(fos, ""); fos.close(); fis.close(); } } 练习: package day22; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class Test { /** * 需求:获取指定目录下,指定扩展名的文件(包含子目录中的), * 这些文件的绝对路径写入到一个文本文件中。 * 简单说,就是建立一个指定扩展名的文件的列表。 * * 思路:1.必须进行深度遍历。 * 2.要在遍历的过程中进行过滤。将符合条件的内容都存储到容器中。 * 3.对容器中的内容进行遍历并将绝对路径写入到文件中。 */ public static void main(String[] args) { File dir=new File("e://javatest"); FilenameFilter filter=new FilenameFilter(){ @Override public boolean accept(File dir, String name) { return name.endsWith(".java"); } }; List getFiles(dir,filter,list); File destFile=new File(dir,"javalist.txt"); writeToFile(list,destFile); } public static void getFiles(File dir,FilenameFilter filter,List /* * 对指定目录的内容进行深度遍历,并按照制定过滤器进行过滤, * 将过滤后的内容存储到指定容器list中。 */ File[] files=dir.listFiles(); for(File file:files){ if(file.isDirectory()){ //递归 getFiles(file,filter,list); } else{ //对遍历到的文件进行过滤。将符合条件的File对象存储到List集合中 if(filter.accept(dir,file.getName())){ list.add(file); } } } } public static void writeToFile(List BufferedWriter bufw=null; try { bufw=new BufferedWriter(new FileWriter(destFile)); for(File file:list){ bufw.write(file.getAbsolutePath()); bufw.newLine(); bufw.flush(); } } catch (IOException e) { throw new RuntimeException("写入失败"); } finally{ if(bufw!=null) try { bufw.close(); } catch (IOException e) { throw new RuntimeException("关闭失败"); } } } } IO包中的其他类: 打印流:PrintWriter和PrintStream。可以直接操作输入流和文件。 1.它提供了打印方法可以对多种数据类型值进行打印,并保持数据的表示形式。 2.它不抛IOException. 3.他的构造函数接收三种类型的值: a.字符串路径 b.File对象 c.字节输出流 序列流:SequenceInputStream,对多个流进行合并。 练习:文件切割器和文件合并器 package day22; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Properties; public class splitFileDemo { /** * 练习:文件切割器 * @throws IOException */ private static final int SIZE = 1024*1024; //这就是1M。 public static void main(String[] args) throws IOException { File file=new File("e:\\javatest\\eclipse\\HelloJava\\LoginScreenLoop.mp3"); splitFile(file); } public static void splitFile(File file) throws IOException{ //用读取流关联源文件 FileInputStream fis=new FileInputStream(file); //定义一个1M的自定义缓冲区 byte[] buf=new byte[SIZE]; //定义一个指定大小的缓冲区,待会就把文件要切成这么大的。 //创建目的 FileOutputStream fos=null; int len=0; int count=1; /* * 切割文件时,必须要记录被切割文件的名称和切割出来的碎片文件的个数,以方便合并。 * 这个信息为了进行简单的描述,使用键值对的方式,用到了properties对象。 */ Properties prop=new Properties(); File dir=new File("e:\\javatest\\eclipse\\HelloJava\\partfiles"); if(!dir.exists()){ dir.mkdirs(); } while((len=fis.read(buf))!=-1){ fos=new FileOutputStream(new File(dir,(count++)+".part")); //碎片文件文件名要注意,类型也别直接就写txt fos.write(buf,0,len); fos.close(); } fos.close(); fis.close(); //将被切割文件的信息保存到prop中。 prop.setProperty("partcount", count+""); prop.setProperty("filename", file.getName()); //将prop的数据存储到文件中。 fos=new FileOutputStream(new File(dir,count+".properties")); prop.store(fos, "save file information"); } } package day22; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.SequenceInputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.Properties; public class MergerDemo { /** * 文件合并器 * * 文件切割器切了之后肯定还要合并嘛 * @throws IOException */ public static void main(String[] args) throws IOException { File dir=new File("partfiles"); mergeFile(dir); } public static void mergeFile(File dir) throws IOException{ //先拿被切割文件的配置信息。不知道这个文件叫啥,只知道后缀名是.properties。所以要用过滤器 File[] files=dir.listFiles(new SuffixFilter(".properties")); if(files.length!=1) throw new RuntimeException(dir+",该目录下没有properties扩展名的文件或者不唯一"); //记录配置文件对象,并获取文件中的信息 File confile=files[0]; Properties prop=new Properties(); FileInputStream fis=new FileInputStream(confile); prop.load(fis); String filename=prop.getProperty("filename"); int count =Integer.parseInt(prop.getProperty("partcount")); //获取该目录下的碎片文件 File[] partFiles=dir.listFiles(new SuffixFilter(".part")); if(partFiles.length!=count-1){ throw new RuntimeException("碎片文件不符合要求"); } //将碎片文件和流对象关联并存储到集合中 ArrayList for(int x=1;x<=partFiles.length;x++){ al.add(new FileInputStream(new File(dir,x+".part"))); } //将多个流合并成一个序列流 Enumeration SequenceInputStream sis=new SequenceInputStream(en); FileOutputStream fos=new FileOutputStream(new File(dir,filename)); byte[] buf=new byte[1024]; int len=0; while((len=sis.read(buf))!=-1){ fos.write(buf,0,len); } fos.close(); sis.close(); } } IO包中的其他类: 操作基本数据类型: DataInputStream和DataOutputStream 操作字节数组: ByteArrayInputStream和ByteArrayOutputStream 操作字符数组: CharArrayReader和CharArrayWriter 操作字符串: StringReader和StrinWriter DataInputStream和DataOutputStream: package day22; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class DataStreamDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { writeData(); readData(); } public static void readData() throws IOException { DataInputStream dis=new DataInputStream(new FileInputStream("data.txt")); String str=dis.readUTF(); System.out.println(str); } public static void writeData() throws IOException { DataOutputStream dos=new DataOutputStream(new FileOutputStream("data.txt")); dos.writeUTF("你好"); dos.close(); } } ByteArrayInputStream和ByteArrayOutputStream: 这两个类关闭无效,关闭后还可以调用且不抛异常。因为此类中的方法不调用底层资源,只是在内存中操作一个数组。 package day22; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; public class ByteArrayStream { /** * @param args */ public static void main(String[] args) { ByteArrayInputStream bis=new ByteArrayInputStream("abcdefg".getBytes()); ByteArrayOutputStream bos=new ByteArrayOutputStream(); int ch=0; while((ch=bis.read())!=-1){ bos.write(ch); } //bis这里不用关 ,关了也没用。 System.out.println(bos.toString()); } } CharArrayReader和CharArrayWriter StringReader和StrinWriter 这四个和ByteArrayInputStream,ByteArrayOutputStream类似,类比学习即可。 编码表: ASCII:美国标准信息交换码 用一个字节的7位可以表示。所以0开头的一般是美国码表。 ISO8859-1:拉丁码表。欧洲码表,用一个字节的8位表示。 GB2312:中国的中文编码表 GBK:中国的中文编码表的升级,融合了更多的中文文字符号。 Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,java语言使用就是unicode。 UTF-8:最多用三个字节来表示一个字符。 package day22; import java.io.IOException; public class EncodeDemo { /** * 字符串--> 字节数组 编码 * 字节数组--> 字符串 解码 * @throws IOException * */ public static void main(String[] args) throws IOException { String str="你好"; //编码 byte[] buf=str.getBytes("GBK"); //你好对应的编码(GBK): -60 -29 -70 -61 //你好对应的编码(UTF-8): -28 -67 -96 -27 -91 -67 printBytes(buf); //解码 String s1=new String(buf,"GBK"); System.out.println("s1="+s1); } public static void printBytes(byte[] buf) { for(Byte b:buf){ System.out.print(b+" "); } } } 一个练习: package day22; import java.io.IOException; public class EncodeTest { /** * 在java中,字符串“abcd”与字符串“ab你好”的长度是一样的,都是四个字符。 * 但对应的字节数不同,因为一个汉字占两个字节。 * 定义一个方法,按照最大字节数来去子串。 * 如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个 * 那么半个就要舍弃。如果取四个字节就是“ab你”,取五个就是“ab你”。 * @throws IOException */ public static void main(String[] args) throws IOException { String str="ab你好cd谢谢"; int len=str.getBytes("GBK").length; for(int x=0;x System.out.println("截取"+(x+1)+"个字节的结果是:"+cutStringByU8Byte(str,x+1)); } } //用U8码表做的方法 public static String cutStringByU8Byte(String str, int len) throws IOException { byte[] buf=str.getBytes("UTF-8"); int count=0; for(int x=len-1;x>=0;x--){ if(buf[x]<0) count++; else break; } if(count%3==0) return new String(buf,0,len,"utf-8"); else if(count%3==1) return new String(buf,0,len-1,"utf-8"); else return new String(buf,0,len-2,"utf-8"); } //用GBK做的方法 public static String cutStringByByte(String str,int len) throws IOException{ byte[] buf=str.getBytes("gbk"); int count=0; for(int x=len-1;x>=0;x--){ if(buf[x]<0) count++; else break; } if(count%2==0) return new String(buf,0,len); else return new String(buf,0,len-1); } }