深度遍历演示:
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<files.length;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<files.length;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<level;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<String> names=prop.stringPropertyNames();
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<File> list=new ArrayList<File>();
getFiles(dir,filter,list);
File destFile=new File(dir,"javalist.txt");
writeToFile(list,destFile);
}
public static void getFiles(File dir,FilenameFilter filter,List<File> 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<File> list,File destFile) {
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<FileInputStream> al=new ArrayList<FileInputStream>();
for(int x=1;x<=partFiles.length;x++){
al.add(new FileInputStream(new File(dir,x+".part")));
}
//将多个流合并成一个序列流
Enumeration<FileInputStream> en=Collections.enumeration(al);
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<len;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);
}
}