流模型【一】

今天整理和回顾了java中的流模型的使用方法,针对一些练习题,编辑了针对性的代码,解决问题,加深了对流模型的理解和记忆

流模型

主要目的:屏蔽具体实现的区别,使用统一的方法进行编程

  • 输入和输出
  • 字节和字符
  • 节点和过滤 装饰模式
  • 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主要用于操作文件 - newFileOutputStream(“文件名称”)采用文件覆盖的方式操作 - new FileOutputStream(“文件名称”,boolean是否采用追加操作) - System.out和System.err 用于输出到标准输出设备

样例

文件的拷贝

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

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

public class Test01 {
    
    
	public static void main(String[] args) throws IOException {
    
    
		try (OutputStream os = new FileOutputStream("text.txt"); 
				InputStream is = new FileInputStream("Test01.java")) {
    
    
			int kk;
			byte[] a  =new byte[8192];
			while ((kk = is.read(a)) > -1) {
    
    
				String ss = new String(a,0,kk);
				System.out.println(ss);
				os.close();
			}
		}
	}
}

复杂样例

  • 实现文件夹的拷贝和移动
  • 文件夹的深度无法提前预知,所以这里采用递归调用的方式进行操作
public class Test02 {
    
    
	private static String source;
	private static String target;

	public static void main(String[] args) {
    
    
		source = "G:/Users/52255/Desktop/PS";
		target = "G:/";
	}

	public 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用于操作文件,属于节点流

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

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

public class Test03 {
    
    
	public static void main(String[] args) throws FileNotFoundException, IOException {
    
    
		File f =new File("Test01.java");
		if(f.exists()){
    
    
			try(Reader r = new FileReader(f);
					Writer w = new FileWriter("C:/bbb.txt")
					){
    
    
				int cc=0;
				char[] buffer = new char[8192];
				while((cc = r.read(buffer))!=-1){
    
    
					System.out.print((char)cc);
					w.write(buffer,0,cc);
				}
			}
		}else {
    
    
			System.out.println("文件不能读取");
		}
	}
}

Writer字符输出流

  • write(int):void
  • write(char[],int,int):void
  • close()
  • FileWriter用于操作文件
  • new FileWriter(String leName)
  • new FileWriter(String leName, boolean append)默认覆盖,boolean表示是否追加

小结

在学些BIO时记忆父类的方法,区分子类的实现不同
InputStream中的方法 read(byte[]):int; Reader中方法read(char[]):int 如果到达流末尾则-1
OutputStream中的方法 write(byte[],0,len):void;Writer中的方法write(char[],0,len)/write(String)
一般在使用中,如果读取数据使用字节流,则写出数据采用的也是字节流;不建议混用,除非引入桥接流
文件流
FileInputStream(“le-name”) FileInputStream(File) FileNotFoundException
FileReader(“le-name”) FileReader(File) FileNotFoundException
FileOutputStream(“le-name”) FileOutputStream(“le-name”,true) 默认文件覆盖,如果参数true表
示追加
FileWriter(“le-name”) FileWriter(“le-name”,true)
一般不使用单字节或者单字符的操作方法,使用数组
注意:try(){}是推荐写法,否则应该使用try{}nally{}结构保证流的关闭
针对二进制文件不建议使用字符流,建议使用字节流进行操作,否则有可能拷贝文件出现问题:
如果针对文本文件则建议使用字符流,因为编码使用比较方便

例题
编写一个程序实现如下功能,文件n.txt是无行结构(无换行符)的汉语文件,从n中读取字符,写入文件
fout.txt中,每40个字符一行(最后一行可能少于40个字)

public class Test04 {
    
    
	public static void main(String[] args) throws IOException {
    
    
		File f = new File("G:/Users/52255/Desktop/Test.txt");
		if (f.isFile() && f.exists()) {
    
    
			try (Reader r = new FileReader("G:/Users/52255/Desktop/Test.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’出现的总次数

public class Test05 {
    
    
	public static void main(String[] args) throws FileNotFoundException, IOException {
    
    
		File f = new File("Test01.java");
		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(counter);
		}
	}
}

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

public class Test06 {
    
    
	public static void main(String[] args) throws IOException {
    
    
		File f = new File("Test01.java");
		if (f.isFile() && f.exists()) {
    
    
			try (Reader r = new FileReader(f);) {
    
    
				int[] arr1 = new int[26];
				int[] arr2 = new int[26];
				int cc;
				while ((cc = r.read()) != -1) {
    
    
					if (cc >= 'a' && cc <= 'z')
						arr1[cc - 'a']++;
					if (cc >= 'A' && cc <= 'Z')
						arr2[cc - 'A']++;
				}
				for (int i = 0; i < arr1.length; i++)
					System.out.print(((char) ('a' + i)) + "(" + arr1[i] + ")");
				for (int j = 0; j < arr2.length; j++)
					System.out.print(((char) ('A' + j)) + "(" + arr1[j] + ")");
			}
		}
		else{
    
    
			System.out.println("1");
		}
	}
}

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

private char c;
private int counter;
public CharNum(char c) {
    
    
	super();
	this.c =c;
}
public char getC() {
    
    
	return c;
}
public void setC(char c) {
    
    
	this.c = c;
}
public int getCounter() {
    
    
	return counter;
}

public void setCounter(int counter) {
    
    
	this.counter = counter;
}
@Override
public String toString() {
    
    
	return c + "(" + counter + ")";
}
}
public class Test08 {
    
    
	public static void main(String[] args) throws IOException {
    
    
		File f = new File("Test01.java");
		if (f.isFile() && f.exists()) {
    
    
			try (Reader r = new FileReader(f)) {
    
    
				CharNum[] arr = 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 < arr.length; i++) {
    
    
							if (arr[i] == null) {
    
    
								bb = true;
								break;
							} else {
    
    
								if (cc == arr[i].getC()) {
    
    
									arr[i].setCounter(arr[i].getCounter() + 1);
									break;
								}
							}
						}
						if (bb)
							arr[i] = new CharNum((char) cc);

					}
				}
				for (CharNum temp : arr)
					if (temp != null)
						System.out.print(temp + ",");
			}
		}
	}
}

文件夹的深度未知
FilenameFilter

public class Test10 {
    
    
	private static String sourse="G:\\com.Test2101";
	private static String target="G:\\aaa"; 
	public static void main(String[] args) throws Exception {
    
    
		File f = new File(sourse);
		if(f!=null)
			copyJavaFile(f);
	}

	public static void copyJavaFile(File file) throws IOException {
    
    
		if (file != null && file.exists()) {
    
    
			if (file.isDirectory()) {
    
    
				File[] fs = file.listFiles((dir, name) -> {
    
    
					File temp = new File(dir, name);
					if (temp.isFile())
						return name.endsWith(".java");
					return true;
				});
				if (fs != null && fs.length > 0) {
    
    
					for (File temp : fs) {
    
    
						copyJavaFile(temp);
					}
				}
			} else if (file.isFile()) {
    
    
				String path = file.getAbsolutePath();
				String fileName = path.substring(path.lastIndexOf("\\"));
				fileName = target + fileName;
				try (Reader r = new FileReader(file); Writer w = new FileWriter(fileName,true)) {
    
    
					int cc = 0;
					char[] buffer = new char[8192];
					while ((cc = r.read(buffer)) != -1) {
    
    
						w.write(buffer, 0, cc);
					}
				}
			}
		}
	}
}

节点流类型 类型 字符流 字节流
File文件 FileReader、FileWriter FileInputStream、FileOutputStream
内存数组 CharArrayReader、 CharArrayWriter ByteArrayInputStream、 ByteArrayOutputStream 内存字串StringReader、 StringWriter
管道 PipedReader、 PipedWriter PipedInputStream、 PipedOutputStream
文件节点流
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 oset, int length)
    CharArrayWriter用于实现向一个字符数组中写入数据,这个数组可以自动调整大小
    ByteArrayInputStream、ByteArrayOutputStream和CharArrayReader以及CharArrayWriter类似,支持操作的内容不同而已,操作byte[]与char[]
    从一个文件中读取内容并写入到char[]中
public class Test11 {
    
    
	public static void main(String[] args) throws Exception {
    
    
		Reader r = new FileReader("Test01.java");
		Writer w = new CharArrayWriter();
		int cc;
		while ((cc = r.read()) != -1) {
    
    
			w.write(cc);
		}
		r.close();
		w.close();
		char[] arr = ((CharArrayWriter) w).toCharArray();
		System.out.println(arr);

	}
}

内存字串流
StringReader用于从一个字串String中读取数据,StringWriter用于给一个StringBuer中写入数据,实现一个可边长的字串

public class Test12 {
    
    
public static void main(String[] args)throws Exception {
    
    
	String ss = "我不i按噶不爱不够";
	Reader r = new CharArrayReader(ss.toCharArray());
	int cc;
	while((cc=r.read())!=-1){
    
    
		System.out.print((char)cc);
	}
	r.close();
}
}

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

public class Test13 {
    
    
public static void main(String[] args)throws Exception {
    
    
	String aa = "anfafnwoa";
	Reader r = new StringReader(aa);
	int kk;
	while((kk=r.read())!=-1){
    
    
		System.out.print((char)kk);
	}
}
}

总结

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

猜你喜欢

转载自blog.csdn.net/Lecheng_/article/details/113143552