流模型

流模型

  • 主要目的:屏蔽具体实现的区别,使用统一的方法进行编程
  • 输入和输出
  • 字节和字符
  • 节点和过滤 装饰模式
  • BIO NIO AIO

字节流

  • 父类InputStream和OutputStream
  • 一次一字节的操作方式,一般用于声音、图像、视频之类的二进制文件

InputStream

方法: - read():int -1 - read(byte[]):int -1 - close():void - FileInputStream主要用于操作文件 - System.in 主要用于接收用户输入

OutputStream

方法: - write(int):void - write(byte[],int,int):void - close():void - FileOutputStream主要用于操作文件 - new FileOutputStream(“文件名称”)采用文件覆盖的方式操作 - new FileOutputStream(“文件名称”,boolean是否采用追加操作) - System.out和System.err 用于输出到标准输出设备

样例

文件的拷贝



public class T4 {
	public static void main(String[] args)throws IOException {
        //try/resource的写法,会自动执行关闭操作,但是要求实现closeable接口
		try (OutputStream os=new FileOutputStream("data.txt",true);
		InputStream is=new FileInputStream("T4.java");){
			int kk;
			while((kk=is.read())>-1){
				os.write(kk);
			}
		}
	}
    //由于是一次读取一字节的操作,所以在操作输出时会有问题,但是文件拷贝不会有任何问题
}


上面的方法采用的是一次一字节的操作方法,效率较低,可以考虑引入byte[]缓存的方式提高执行效率

package com.kang;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class T5 {
	public static void main(String[] args)throws IOException {
		try(OutputStream os = new FileOutputStream("daata.txt",true);
				InputStream is = new FileInputStream("T5.java")){
			int kk;
			byte[] buffer = new byte[8192];
            //如果不确定缓存的大小,建议使用8192,即8kB
			while((kk=is.read(buffer))>-1){//read()从输入流中读取下一个字节。如果没有字节可读(也就是read()读到文件最后了)read()返回-1.
				String ss = new String(buffer,0,kk);//主要解决多字节的字符组装问题
				System.out.println(ss);
				os.write(buffer,0,kk);//读取多少个字节则写出多少个字节
			}
		}
	}
}

复杂样例

  • 实现文件夹的拷贝和移动

文件夹的深度无法提前预知,所以这里采用递归调用的方式进行操作

package com.kang;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class T6 {
	private static String source;
	private static String target;

	public static void main(String[] args) throws IOException {
		source = "C:\\software\\EditPlus 3";
		target = "c://sss";
		File ff = new File(source);
		copy(ff);

	}

	private static void copy(File file) throws IOException {
		if (file != null && file.exists()) {
			if (file.isDirectory()) {
				String path = file.getAbsolutePath();
				String newPath = path.replace(source, target);
				File tmp = new File(newPath);
				if (!tmp.exists())
					tmp.mkdirs();
				File[] fs = file.listFiles();
				if (fs != null && fs.length > 0)
					for (File temp : fs)
						copy(temp);
			} else if (file.isFile()) {
                //事实上有简化写法
				String path = file.getAbsolutePath();
				String newPath = path.replace(source, target);
				try (InputStream is = new FileInputStream(file); OutputStream os = new FileOutputStream(newPath);) {
					byte[] buffer = new byte[8192];
					int len = 0;
					while ((len = is.read(buffer)) > 0)
						os.write(buffer, 0, len);
				}
			}
		}
	}
}

字符流

一次操作一个字符 一般用于操作文本文件,注意word文档不是字符文件

Reader字符输入流

  • read():int 0-65535 -1

  • read(char[]):int -1

  • close():void

  • FileReader用于操作文件,属于节点流

    读取指定文件并在控制台上进行显示

    package com.kang;
    
    import java.io.File;
    import java.io.FileReader;
    import java.io.Reader;
    
    public class Test7 {
    	public static void main(String[] args)throws Exception {
    		File f = new File("Test1.java");
    		if(f.exists()){
                //如果文件不存在,则在执行read操作时会抛出FileNotFoundException
    			try(Reader r =new FileReader(f);){
    				int cc;
    				while((cc=r.read())!=-1){
    					System.out.println((char)cc);
                        //windows下的换行和linux中的换行不一样
    				}
    			}
    		}else{
    			System.out.println("文件不能读取!");
    		}
    	}
    }
    
    

    Writer字符输出流###

    • write(int):void
    • write(char[],int,int):void
    • close()
    • FileWriter用于操作文件

    new FileWriter(String fileName)

    new FileWriter(String fileName, boolean append)默认覆盖,boolean表示是否追加

    public class Test2 {
        public static void main(String[] args)throws Exception {
            File f=new File("Test1.java");
            if(f.exists()) { //如果文件不存在,则在执行read操作时会抛出FileNotFoundException 
                try(Reader r=new FileReader(f); 
                    Writer w=new FileWriter("c:/bbb.txt");         ){ 
              int len=0; 
                    char[] buffer=new char[8192]; while((len=r.read(buffer))!=-1) { System.out.print(buffer);//windows下的换行和linux中的换行不一样 
                                                 w.write(buffer,0,len);//读取多少字符则写出多少字符
                                                                }
            } 
            }else { System.out.println("文件不能读取!")
                 }
        }
    

    小结

    在学些BIO时记忆父类的方法,区分子类的实现不同

    • InputStream中的方法 read(byte[]):int; Reader中方法read(char[]):int 如果到达流末尾则-1
    • OutputStream中的方法 write(byte[],0,len):void;Writer中的方法write(char[],0,len)/write(String)

    一般在使用中,如果读取数据使用字节流,则写出数据采用的也是字节流;不建议混用,除非引入桥接流文件流

    • FileInputStream(“file-name”) FileInputStream(File) FileNotFoundException

    • FileReader(“file-name”) FileReader(File) FileNotFoundException

    • FileOutputStream(“file-name”) FileOutputStream(“file-name”,true) 默认文件覆盖,如果参数true表示追加

    • FileWriter(“file-name”) FileWriter(“file-name”,true)

      一般不使用单字节或者单字符的操作方法,使用数组

注意:try(){}是推荐写法,否则应该使用try{}finally{}结构保证流的关闭

针对二进制文件不建议使用字符流,建议使用字节流进行操作,否则有可能拷贝文件出现问题:

如果针对文本文件则建议使用字符流,因为编码使用比较方便

编写一个程序实现如下功能,文件Õn.txt是无行结构(无换行符)的汉语文件,从fin中读取字符,写入文件

fout.txt中,每40个字符一行(最后一行可能少于40个字)

public class Test3 { 
    public static void main(String[] args) throws IOException{
        File f=new File("c:/fin.txt");
        if(f.isFile()&& f.exists()) {
            try(
                Reader r=new FileReader("c:/fin.txt"); Writer w=new FileWriter("c:/fout.txt");
            ){ 
                int counter=0;
                int cc;
                while((cc=r.read())!=-1) { 
                    counter++; 
                    System.out.print((char)cc);                              w.write(cc);
                    if(counter%40==0) {                                        System.out.println();
                      w.write("\n"); 
                  } 
                }
            }
        } 
    } 
}

统计一个文件calcCharNum.txt中字母’A’和’a’出现的总次数

package com.kang;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class T9 {
	public static void main(String[] args)throws IOException {
		File f = new File("c:/calcCharNum.txt");
		if(f.isFile() && f.exists()){
			try(
					Reader r = new FileReader(f);
					){
				int cc;
				int counter=0;
				while((cc=r.read())!=-1){
					if(cc=='a' || cc=='A')
						counter++;
				}
				System.out.println("A或者a出现的次数为:"+counter);
			}
		}
	}
}

统计一个文件calcCharNum.txt中各个字母出现次数:A(8),B(16),C(10)…,a(12),b(10),c(3)…, 括号内代表字符出现次数; 存储数据的方法1:采用数组,例如数组1中存储大写字母,数组2中存储小写字母

package com.kang;

import java.io.File;
import java.io.IOException;
import java.io.Reader;

public class T10 {
	public static void main(String[] args) throws IOException {
		File f = new File("c:/calcCharNum.txt");
		if (f.isFile() && f.exists()) {
			try (Reader r = new FileReader(f);) {
				int[] carr1 = new int[26];//存储大写字母的出现次数,每个位置对应一个字符
				int[] carr2 = new int[26];//存储小写字母的出现次数,每个位置对应一个字符
				int cc;
				while ((cc = r.read()) != -1) {
					if (cc >= 'a' && cc <= 'z')
						carr2[cc - 'a']++;;//将读取的字符转换为下标,然后对应位 置加1,默认初始值为0
					if (cc >= 'A' && cc <= 'Z')
						carr1[cc - 'A']++;
				}
				for (int i = 0; i < carr1.length; i++)
					System.out.print((char) ('A' + i) + "(" + carr1[i] + "),");
				System.out.println();
				for (int i = 0; i < carr2.length; i++)
					System.out.print((char) ('a' + i) + "(" + carr2[i] + "),");
			}
		}
	}
}

方法2:采用自定义类的方式记录字符和对应的出现次数

package com.kang;

public class CharNum {
	private char c;//对应出现的字符
	private int num=1;//该字符出现的次数
	
	public CharNum(char c){//构建对象时必须告知所封装的字符,而且不允许修改
		this.c=c;
	}

	public char getC() {
		return c;
	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}
}

最多可以有52【26*2】个对象,可以定义一个数组用于存储数据CharNum[] cn=new CharNum[52];

package com.kang;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class T13 {
	public static void main(String[] args)throws IOException {
		File f = new File("c:/calcCharNum.txt");
		if(f.isFile()&& f.exists()){
			try(
			Reader r=new FileReader(f);		
		){
		CharNum[] carr=new CharNum[52];
				int cc;
			while((cc=r.read())!=-1){//从流中读取字符
			if((cc>='A' && cc<='Z')||(cc>='a'&&cc<='z')){//判断英文字母
	boolean bb =false;
		int i=0;
		for(;i<carr.length;i++){//遍历数组,判断是否已经存储这个字符
		if(carr[i]==null){//已经遍历了前面的所有存储内容,该字符不存在
			bb=true;//表示需要新增到数组中
			break;
	}else{									if(cc==carr[i].getC()){	//如果读取字符和第i个位置上存放的字符相同								
            carr[i].setNum(carr[i].getNum()+1);//统计次数+1
		break;
		    }
		  }
		}
	if(bb) carr[i]=new CharNum((char)cc);
	}
}
		for(CharNum tmp:carr)
			if(tmp!=null)
			System.out.println(tmp+",");
		
	}
}
}
}

文件节点流

  • FileInputStream和FileOutputStream是文件字节流,是一种节点流

文件字节输入流的构造方法:

  • FileInputStream(“文件名称”),如果文件不存在FileNotFoundException
  • FileInputStream(File)

文件字节输出流的构造方法:

  • FileOutputStream(“文件名称”) 如果文件不存在则新建文件,如果文件存在则覆盖文件内容
  • FileOutputStream(String name文件名称, boolean append是否采用追加方式)

FileReader和FileWriter类似

内存数组节点

如果文本则使用char[],如果二进制则使用byte[]

构造器方法

CharArrayReader(char[] buf)其中char[]就是数据的来源,也就是说Reader就是从char[]中读取数据

CharArrayRead(char[] buf, int oàset, int length)

CharArrayWriter用于实现向一个字符数组中写入数据,这个数组可以自动调整大小

ByteArrayInputStream、ByteArrayOutputStream和CharArrayReader以及CharArrayWriter类似,支持操作的

内容不同而已,操作byte[]与char[]

从一个文件中读取内容并写入到char[]中

package com.kang;

import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.FileReader;
import java.io.Reader;
import java.io.Writer;

public class T15 {
	public static void main(String[] args)throws Exception {
		Reader r = new FileReader("T1.java");
	//实际上就是一个writer,向CharArrayWriter写出数据,实际上会自动写入一个相关联的char数组中	
		Writer w = new CharArrayWriter();
		int cc;
		while((cc=r.read())!=-1){
			w.write(cc);
		}
		r.close();
		w.close();
		//CharArrayWriter中的特殊方法,用于获取CharArrayWriter的输出数组
		char[] arr=((CharArrayWriter)w).toCharArray();
		System.out.println(arr);
	}

}

从char数组中读取数据

package com.kang;

import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Reader;

public class T16 {
	public static void main(String[] args)throws IOException {
		String str="黛博拉";
		Reader r = new CharArrayReader(str.toCharArray());
		int kk;
		while((kk=r.read())!=-1){
			System.out.println((char)kk);
		}
		r.close();
	}
}

内存字串流

StringReader用于从一个字串String中读取数据,StringWriter用于给一个StringBuàer中写入数据,实现一个可变长的字串

package com.kang;

import java.io.Reader;
import java.io.StringReader;

public class T17 {
	public static void main(String[] args)throws Exception {
		String str="Amapola";
		Reader r = new StringReader(str);
		int kk;
		while((kk=r.read())!=-1){
			System.out.println((char)kk);
		}
		r.close();
	}
}

键盘录入内容,并缓存在内存中,输入quit表示输入完成,输入完成后再在控制台上显示所有输入的内容

package com.kang;

import java.io.StringWriter;
import java.io.Writer;
import java.util.Scanner;

public class T18 {
	public static void main(String[] args)throws Exception {
		Scanner sc = new Scanner(System.in);
		Writer w = new StringWriter();
		int counter=0;
		while(true){
			String str=sc.nextLine();
			if("quit".equals(str))
				break;
			w.write("第"+(++counter)+"次输入为:"+str+"\n");
		}
		w.close();
		StringBuffer sb=((StringWriter)w).getBuffer();
		System.out.println(sb);
	}

}

总结

  • 读写文件使用节点流FileInputStream/FileOutputStream和FileReader/FileWriter,如果操作文本文件,建议使用FileReader/FileWriter,如果操作二进制文件建议使用FileInputStream/FileOutputStream
  • 需要建立缓冲区(建立临时文件的方式效率低),可以考虑使用内存节点,例如CharArrayReader/CharArrayWriter、StringReader/StringWriter和 ByteArrayInputStream/ByteArrayOutputStream
  • 如果需要一个二进制缓冲区可以使用ByteArrayInputStream/ByteArrayOutputStream,如果需要一个字符缓存可以使用CharArrayReader/CharArrayWriter、StringReader/StringWriter
  • 如果数据量不是特别大使用CharArrayReader/CharArrayWriter更为方便,如果数据量大而且可能需要直接操作缓冲区则使用StringReader/StringWriter
  • StringWriter中提供了方法getBuàer():StringBuàer
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值