黑马程序员_IO流(三)

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

十一、递归

概述

就是函数自身调用自身

当一个功能被重复使用,并且每次使用时参与运算的结果和上一次运算有关,这时候就可以用递归来解决问题


注意!

一定要定义递归的条件

递归的次数不要太多,否则容易出现StackOverflowError栈内存溢出错误


代码示例列出指定目录下所有的文件和文件夹(带层次)

import java.io.File;

public class FileListDemo{
       public static void main(String[] args){
       			//关联指定路径
            File dir = new File("D:\\Java\\jdk1.6.0_02\\include" );
            //列出目录下所有文件或者文件夹
            listAll(dir,0);
      }

       public static void listAll(File dir, int level) {
       			//有层次的输出
            System.out.println(getSpace(level) + "dir:" + dir.getAbsolutePath());
            //获取指定目录下当前的所有文件夹或者文件对象
            level++;
            File[] files = dir.listFiles();

             for (int x = 0; x < files.length; x++){
             		//如果目录下还有目录继续调用listAll函数
                   if (files[x].isDirectory()){
                        listAll(files[x],level);
                  }
                  System.out.println(getSpace(level) + "file:" + files[x].getAbsolutePath());
            }
      }
				//带层次的列表
       private static String getSpace( int level){
            StringBuilder sb = new StringBuilder();

            sb.append( "|--" );
             for (int x = 0; x < level; x++){
                  sb.append( "|-- " );
            }

             return sb.toString();
      }
}

需求:利用递归求1到10的和

public class SumDemo {
	public static void(String args[]) {
		int sum = getSun(10);
			System.out.println(sum);
	}
	
	public static int getSum(int num) {
		if (num == 1);
			return 1;
		return num + getSum(num - 1);//利用递归,不断使用这个函数完成10到1的求和
	}
}


需求:利用递归方法删除带内容的目录(Windows中删除目录是由里到外删除)
import java.io.File;

public class RemoveDirTest{
       public static void main(String[] args){
       			//指定要删除的目录
            File dir = new File("d:\\demo" );
            //删除目录
            removeDir(dir);
      }

       public static void removeDir(File dir){
       			//列出所有文件和文件夹
            File[] files = dir.listFiles();
						//遍历所有文件夹和文件夹
             for(File file : files){
             			//检查是否是文件夹
                   if(file.isDirectory()){
                   	//如果是继续调用remove函数删除文件夹中内容
                        removeDir(file);
                  } 
                  else{
                        System.out.println(file + ":" + file.delete());//删除文件
                  }
            }
            System.out.println(dir + ":" + dir.delete());//删除目录
      }
}

十二、Properties类

概述

一个可以将键值进行持久化存储的对象。是Map--Hashtable的子类。

用于属性配置文件,键和值都是字符串型


特点

可以持续化存储数据。

键值都是字符串

一般用于配置文件


常用方法

1、设置

Object setProperty(String key,String value); //设置键和值,调用Hashtable的方法put

2、获取

String getProperty(String key); //指定key搜索value

Set<String> stringPropertyName();//去除所有元素,存入Set集合,返回的是键的集合


需求:获取一个应用程序运行的次数,如果超过5次,给出使用次数已到请注册的提示,并不要再运行程序。

/*思路:很容易想到用一个计数器来记录使用的次数,但是一旦关闭程序计数器就会被初始化,
				因此要建立一个配置文件,用于记录使用次数。该配置文件使用键值对的形式储存,
				键值对是map集合,数据以文件形式储存使用io流,IO+map=Properties
*/


import java.io.*;
import java.util.Properties;

public class PropertiesTest{
       public static void main(String[] args) throws IOException {
            getAppCount();
      }
       public static void getAppCount() throws IOException {
             //将配置文件封装成File对象
            File confile = new File("count.properties" );
						//如果不存在 新建一个配置文件
             if(!confile.exists()) {
                  confile.createNewFile();
            }
						
            FileInputStream fis = new FileInputStream(confile);
      
            Properties prop = new Properties();
						//将读取流和指定文件相关联,并读取一行数据,将键值储存到properties集合中
            prop.load(fis);

             //从集合中通过键获取次数
            String value = prop.getProperty( "time");
            
             //定义计数器,记录获取到的次数
             int count = 0;
             if(value != null) {
                  count = Integer.parseInt(value);
                   if(count >= 5){
                         throw new RuntimeException("使用次数已到,请注册,给钱!" );
                  }
            }
            count++;

             //将改变后的次数重新存储到集合中。
            prop.setProperty( "time",count + "" );

            FileOutputStream fos = new FileOutputStream(confile);

            prop.store(fos, "");

            fos.close();
            fis.close();
      }
}

十三、打印流

概述

PrintWriter与PrintStream可以直接操作输入流和文件

提供了更多功能,比如打印方法。可以直接打印任意类型的数据

他有一个自动刷新机制,创建该对象,指定参数,对于指定方法可以自动刷新

它使用的苯基默认的字符编码

该流的print方法不抛出IOException


字节打打印流:PrintStream

构造函数可以接收的参数类型:

File对象:File;

字符串路径:String;

字节输出流:OutputStream


字符串打印流:PrintWriter

构造方法中可接受的参数类型

File对象:File

字符串路径:String

字节输出流:OutputStream

字符输出流:Writer


代码示例

import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.IOException;

//写入到out.txt文件中
public class PrintWriterDemo{
       public static void main(String[] args) throws IOException {
       			//一行录入
            BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
             //PrintWriter构造函数的第二个参数设置为true,表示自动刷新,所以不用flush
            PrintWriter out = new PrintWriter(new FileWriter("out.txt" ),true);

            String line = null;
             while((line = bufr.readLine()) != null){
             	//读取到over结束
                   if("over" .equals(line))
                         break;
                  out.println(line.toUpperCase());
            }

            out.close();
            bufr.close();
      }
}


十三、序列流

概述

作用就是将多个读取流合并成一个读取流。SequenceInputStream实现数据合并


构造函数

SequenceInputStream(Enumeration<?extends FileInputStream> e)


合并原理

多个读取流对应一个输出流

切割原理

一个读取流对应多个输出流


合并流代码示例

import java.io.*;
import java.util.*;
class SequenceDemo {
	public static void main(String[] args) throws IOException {
		Vector<FileInputStream> v=new Vector<FileInputStream>();//创建集合对象
		//将流对象添加进集合
		v.add(new FileInputStream("e:\\javaLX\\1.txt"));
		v.add(new FileInputStream("e:\\javaLX\\2.txt"));
		v.add(new FileInputStream("e:\\javaLX\\3.txt"));
		//创建Enumeration对象,将集合元素加入
		Enumeration<FileInputStream> en=v.elements();
		//创建SequenceInputStream对象,合并流对象
		SequenceInputStream sis=new SequenceInputStream(en);
		FileOutputStream fos=new FileOutputStream("e:\\javaLX\\4.txt");
		byte[] buf=new byte[1024];
		int len=0;
		while((len=sis.read(buf))!=-1) {
			fos.write(buf,0,len);
		}
		fos.close();
		sis.close();
	}
}

切割代码示例

import java.io.*;
import java.util.*;
class SplitFile {
	public static void main(String[] args) throws IOException {
		spliFile();
	}
	public static void spliFile() throws IOException {
		FileInputStream fis=new FileInputStream("e:\\javaLX\\1.gif");
		FileOutputStream fos=null;
		byte[] buf=new byte[1024*1024];//碎片文件大小引用
		int len=0;
		int count=1;
		while((len=fis.read(buf))!=-1) {
			//每循环一次都创建一个格式为part的文件
			fos=new FileOutputStream("e:\\javaLX\\"+(count++)+".part");
			fos.write(buf,0,len);
			fos.close();
		}
		fis.close();
	}
}

十四、RandomAccessFile

概述

随机访问文件,自身具备读写方法

通过skipBytes(int x),seek(int x)等方法来达到随机访问


特点:

1. 该对象即能读,又能写。

2. 该对象内部维护了一个byte数组,并通过指针可以操作数组中的元素。

3. 可以通过getFilePointer方法获取指针的位置,和通过seek方法设置指针的位置。

4. 其实该对象就是将字节输入流和输出流进行了封装。

5. 该对象的源或者目的只能是文件。通过构造函数就可以看出。


注意!

实现随机访问,数据最好有规律


十五、管道流

概述

PipedInputStreamt和PipedOutputStream:输入输出可以直接进行连接,通过结合线程使用。


代码示例

import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class PipedStream{
       public static void main(String[] args) throws Exception {
            PipedInputStream input = new PipedInputStream();
            PipedOutputStream output = new PipedOutputStream();

            input.connect(output);//链接管道读取流和管道写入流


						//加入多线程技术,因为单线程,先执行read,会发生死锁,
						//因为read方法是阻塞式的,没有数据read方法会让线程等待
						
             new Thread(new Input(input)).start();
             new Thread(new Output(output)).start();
      }
}

class Input implements Runnable{
       private PipedInputStream in;
      
      Input(PipedInputStream in){
             this.in = in;
      }

       public void run(){
             try{
                   byte[] buf = new byte[1024];
                   int len = in.read(buf);

                  String s = new String(buf,0,len);
                  System.out.println( "s=" + s);
                  in.close();
            } catch(Exception e){
                  e.printStackTrace();
            }
      }
}

class Output implements Runnable{
       private PipedOutputStream out;
      
      Output(PipedOutputStream out){
             this.out = out;
      }

       public void run(){
             try{
                  out.write( "hi,管道来了!" .getBytes());
                  out.close();
            } catch(Exception e){
                  e.printStackTrace();
            }
      }
}


十六、字符编码

概述

计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。

        将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。


常见的编码表

ASCII:美国标准信息交换码,用一个字节的7位可以表示。

ISO8859-1:拉丁码表。欧洲码表,用一个字节的8位表示。

GB2312:中国的中文编码表。

GBK:中国的中文编码表升级,融合了更多的中文文字符号。

Unicode:国际标准码,融合了多种文字。

所有文字都用两个字节来表示,Java语言使用的就是unicode

UTF-8:最多用三个字节来表示一个字符。


代码示例

 public static void main(String[] args) throws IOException{
             //字符串-->字节数组:编码
             //字符数组-->字符串:解码
            String str = "您好";

             //编码
             byte[] buf1 = str.getBytes("GBK" );
            printBytes(buf1);

             byte[] buf2 = str.getBytes("UTF-8" );
            printBytes(buf2);

             //解码,GBK是默认编码表不需要注释
            String s1 = new String(buf1);
            System.out.println( "s1 = " + s1);

            String s2 = new String(buf2,"UTF-8" );
            System.out.println( "s2 = " + s2);
      }

       private static void printBytes(byte[] buf){
             for(byte b : buf){
                  System.out.print(b + " ");
            }
            System.out.println();
      }
}

练习

需求:编写函数,从一个字符串中按字节数截取一部分,但不能截取出半个中文(GBK码表),例如:从“HM程序员”中截取2个字节是“HM”,截取4个则是“HM程”,截取3个

字节也要是"HM"而不要出现半个中文

<span style="font-size:10px;"></span><pre name="code" class="java">import java.util.*;
import java.io.*;

public class Test {
	public static void main(String args[])
			throws UnsupportedEncodingException {
		//从键盘录入字符串和要截取的字符串长度
		String str = null;
		int length = 0;
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入字符串");
		str = sc.nextLine();
		System.out.println("请输入要截取的字符串长度");
		//将输入信息的下一个标记扫描为一个 int。
		length = sc.nextInt();
		sc.close();
	  
		
		System.out.println("截取后的结果是"+cutStringByByte(str,length));		
	}
	
	private static String cutStringByByte(String str, int length) 
			throws UnsupportedEncodingException {  
        
        //将字符串转换为字符数组  
        byte[] buf=str.getBytes("gbk");  
        int count=0;  
          
        //length-1为最后一个角标 ,判断最后一个字节是否是中文
        for(int x=length-1;x>0;x--){ 
        //gbk编码的值两个字节值一般都为负,记录连续的负数个数,如果为奇数,则舍弃	
            //如果字节负数,说明是汉字  
            if(buf[x]<0) {  
                //记录负数个数  
                count++;  
            }  
            else {  
                break;  
            }  
        }  
        //如果截取的数,能整除2,说明是汉字的后半部分,不需要截取  
        if(count%2==0) {  
            return new String(buf,0,length,"gbk");  
        }  
        else {  
            //如果截取的数,不能整除2,说明是汉字的前半部分,需要截取  
            return new String(buf,0,length-1,"gbk");  
        }  
    }   
}

 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值