每日一狗(田园犬西瓜瓜)
文章目录- 0803(023天 输入输出流03 节点流+过滤流)
- 节点流
- 1. 文件节点流
- 1.1 构造方法
- 1.2 基本方法
- 2. 内存流
- 2.1 可变长数组
- 2.2 内存字符串流(来源和去处都是字符串)
- 2.3 缓冲区
- 过滤流类型
- 1. 过滤流的实现
- 2.1 装饰模式复习
- 2.2 案例:自定义流实现循环13加密:
- 2. 读取中文处理乱码
- 2.1 FileReader
- 2.2 new String(btye[])
- 2.3 桥接流
- 3. 缓冲流
- 3.1 缓冲流
- 3.2 构造方法
- 3.2 缓冲输入流
- 键盘录入
- 为文本文件打上行标签
- 4. 数据流
- 4.1 一些方法
- 4.2 操作字符串
- 6. 打印流
- 扩展小芝士
复习:
应用场景
- 字节流:针对二进制位文件 (音频,视频)
- 字符流:针对文本文件(txt文件。。。)
课前小练习(文件的词频统计)
package com.yang1; import java.io.FileReader; import java.io.Reader; public class 课前小练习 { public static void main(String[] args) { myRead(); } // 读取并进行词频统计 public static void myRead() { ArrayList myArr = new ArrayList(40); try (Reader in = new FileReader("G:\蓝鸥\0704(000天 )\笔记\课堂笔记.md")) { int tmp = 0; while ((tmp = in.read()) > 0) { if (tmp == 'n' || tmp == 'r' || tmp == 't') { tmp = ' '; } myArr.add((char) tmp); } } catch (Exception e) { e.printStackTrace(); } myArr.sorted(); myArr.show(); } } class MyChar { private char myChar; private int count; public MyChar(char myChar) { this.myChar = myChar; this.count = 1; } // 频次加一 public void plusOne() { count++; } @Override public String toString() { return myChar + "(" + count + ")"; } / public char getMyChar() { return myChar; } public int getCount() { return count; } } class ArrayList { private MyChar[] arr; private int len; public ArrayList() { this(10); } public ArrayList(int lenght) { arr = new MyChar[lenght]; len = 0; } // 按照出现频率进行排序 public void sorted() { for (int i = 1; i < len; i++) { for (int j = 0; j < len - i; j++) { if (big(j, j + 1)) { MyChar tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } } // 判定是否需要交换 public boolean big(int i, int j) { boolean res = false; if (arr[i].getCount() < arr[j].getCount()) { res = true; } else if (arr[i].getCount() == arr[j].getCount()) { res = arr[i].getMyChar() > arr[j].getMyChar(); } return res; } // 展示词频统计结果 public void show() { for (int i = 0; i < len; i++) { System.out.print(arr[i]); } } // 传入一个字符,实现词频追加统计 public void add(char c) { MyChar myChar = select(c); if (myChar != null) { myChar.plusOne(); } else { if (len == arr.length) { swell(); } arr[len] = new MyChar(c); len++; } } // 数组扩容 public void swell() { MyChar[] newArr = new MyChar[len * 3 / 2]; for (int i = 0; i < len; i++) { newArr[i] = arr[i]; } arr = newArr; } // 字符查询 public MyChar select(char c) { MyChar res = null; for (int i = 0; i < len; i++) { if (arr[i].getMyChar() == c) { res = arr[i]; break; } } return res; } }
节点流的分类
类型 | 字符流 | 字节流 |
---|---|---|
File文件 | FileReader、FileWriter | FileInputStream、FileOutputStream |
内存数组 | CharArrayReader、 CharArrayWriter | ByteArrayInputStream、 ByteArrayOutputStream |
内存字串 | StringReader、 StringWriter | |
管道 | PipedReader、 PipedWriter | PipedInputStream、 PipedOutputStream |
FileInputStream和FileOutputStream是文件字节流,是一种节点流
1.1 构造方法输入流
-
FileInputStream(“文件名称”),如果文件不存在则报错FileNotFoundException
-
FileInputStream(File)
输出流
- FileOutputStream(“文件名称”) 如果文件不存在则新建文件,如果文件存在则覆盖文件内容
- FileOutputStream(String name文件名称, boolean append是否采用追加方式)
输入read
- read():读取并返回整形数据
- read(byte[]):将数据读取之字节数组中,返回值为int,有用数据的最后索引
输出write
- write(int):将(char)int 写入到输出流中
- write(byte[]):将字节数组中的数据写入到输出流中
用于适配不同已经封装好的方法中参数的类型;
这个工具方法要的是输入流,另一个工具方法又要的是输出流(方法内部无法重写)
可以用一个内存输入流先充当这个输入流,在获取到这个输入流的字节数组,在把他封装到一个输出流,传入到另一个工具方法中使用。
使用中间流来进行数据格式的转换
CharArrayReader(char[] buf); CharArrayRead(char[] buf, int offset, int length);
CharArrayWriter用于实现向一个字符数组中写入数据,这个数组可以自动调整大小
ByteArrayInputStream、ByteArrayOutputStream和CharArrayReader以及CharArrayWriter类似,支 持操作的内容不同而已,操作byte[]与char[]
案例:读取输入字符流,将其转存至变长数组中,在变长数组末尾追加指定字符串,将其转换为输出流,在终端输出输出流存储的数据
public class Test2 { public static void main(String[] args) throws Exception { String str="abc中国人民解放军123"; char[] arr=str.toCharArray(); Reader r=new CharArrayReader(arr); while(true) { int kk=r.read(); if(kk==-1) break; System.out.println((char)kk); } System.out.println("键盘录入数据"); Scanner sc=new Scanner(System.in); Writer w=new CharArrayWriter(); while(true) { String ss=sc.nextLine(); if("quit".equals(ss)) break; w.write(ss); } //需要使用CharArrayWriter中定义的特殊方法,获取输出的目标数组 if(w instanceof CharArrayWriter) { CharArrayWriter caw=(CharArrayWriter)w; caw.append("asdfasd");//向输出的数组末尾添加内容 StringBuilder //获取输出的目标数组 char[] target=caw.toCharArray(); System.out.println(new String(target)); } } }2.2 内存字符串流(来源和去处都是字符串)
- StringReader(可读)
- in.read():int 获取一个整型值(字符)
- StringWriter(可写)
- out.write(int):将整型值代表的字符写入调用者中
- out.getBuffer():StringBuffer,返回当前对象中所存储的字符串
package com.yang4; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; public class T01 { public static void main(String[] args) throws IOException { String ss = "中华家test"; Reader in = new StringReader(ss); while (true) { int i = in.read(); if (i == -1) { break; } System.out.println((char) i); } Writer out = new StringWriter(); for (int i = 0; i < 27; i++) { out.write('a' + i); } if (out instanceof StringWriter) { StringWriter sw = (StringWriter) out; StringBuffer sb = sw.getBuffer(); System.out.println(sb); } } }2.3 缓冲区
部分文件需要进行临时存储,但是文件级别的转存,是要经过硬盘的,也就是外存,效率较低;
这时可以使用内存流来维护缓冲区,这就可以大大的提升数据处理的效率
过滤流类型
过滤流就是在节点流的基础上附加功能,这边使用的就是装饰模式来进行实现。
装饰模式的4个角色:通用接口、被装饰对象、抽象装饰、装饰器
处理类型 | 字符流 | 字节流 |
---|---|---|
缓存 | BufferedReader、BufferedWriter | BufferedInputStream、 BufferedOutputStream |
过滤处理 | FilterReader、FilterWriter | FilterInputStream、 FilterOutputStream |
桥接处理 | InputStreamReader、 OutputStreamWriter | |
对象序列化 处理 | ObjectInputStream、 ObjectOutputStream | |
数据转换 | DataInputStream、 DataOutputStream | |
行数统计 | LineNumberReader | LineNumberInputStream |
回滚处理 | PushbackReader | PushbackInputStream |
打印功能 | PrintWriter | PrintWriter |
就是decorate模式中的抽象装饰角色
FilterInputStream/FilterOutputStream和FilterReader/FilterWriter
2.1 装饰模式复习4个角色:通用接口、被装饰对象、抽象装饰、装饰器
public class FilterInputStream extends InputStream { //典型的装饰模式 protected volatile InputStream in; //被装饰目标 //通过构造器组装被装饰对象 protected FilterInputStream(InputStream in) { this.in = in; } //调用Filter中的read方法时实际操作是由被装饰对象实现的 public int read() throws IOException { return in.read(); } }
所谓的过滤流实际上就是类似上面的加密处理,在输入之后(后置处理,被装饰对象先执行)或者输出 之前(前置处理,先处理然后被装饰对象执行)进行一下额外的处理,最终实际操作是调用被装饰对象 的方法完成工作,依靠这种装饰模式实现在节点流的基础上附加额外功能.当然也允许多个过滤流嵌套从 而达到功能累加的目的
FilterInputStream实际上就是一个装饰抽象角色
2.2 案例:自定义流实现循环13加密:读取数据不变:FileReader—BufferedReader
写出数据自定义过滤流SecurityWriter(FilterWriter)
package com.yang4; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FilterReader; import java.io.IOException; import java.io.Reader; public class T02 { public static void main(String[] args) throws FileNotFoundException, IOException { try (SecurityWriter in = new SecurityWriter(new FileReader("data/Test01.java"))) { while (true) { int i = in.read(); if (i < 0) { break; } System.out.print((char) i); } } } } class SecurityWriter extends FilterReader { public SecurityWriter(Reader in) { super(in); } public int read() throws IOException { int k = super.read(); if (k >= 'A' && k <= 'Z') { k = (k - 'A' + 13) % 26 + 'A'; } else if (k >= 'a' && k <= 'z') { k = (k - 'a' + 13) % 26 + 'a'; } return k; } }
2. 读取中文处理乱码 2.1 FileReader
使用字符流进行操作
2.2 new String(btye[])读取字节流,使用String给的方法进行字符拼接
package com.lianxiti; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; public class Test03 { public static void main(String[] args) throws FileNotFoundException, IOException { try (InputStream in = new FileInputStream("data/output.txt")) { byte[] arr = new byte[1024]; int len = in.read(arr); System.out.println(new String(arr, 0, len)); // 种花家 } } }2.3 桥接流
InputStreamReader和OutputStreamWriter是java.io包中用于处理字符流的最基本的类,
用来在字节流和字符流之间作为中介:从字节输入流读入字节,并按编码规范转换为字符;往字节输出流写字符时先将字符按编码规范转换为字节。使用这两者进行字符处理时,在构造方法中应指定一定的平台规范,以便把以字节方式表示的流转换为特定平台上的字符表示。转换流可以在构造时指定其编码字符集
InputStreamReader用于将一个InputStream类型的输入流自动转换为Reader字符流
OutputStreamWriter用于将一个Writer字符输出流转换为OutputStream字节输出流
package com.yang4; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; public class T03 { public static void main(String[] args) throws Exception { // 桥接流 // 输入字节流 转 输入字符流 InputStream in = new FileInputStream("data/Test01.java"); Reader r = new InputStreamReader(in); while (true) { int k = r.read(); if (k == -1) { break; } System.out.print((char) k); } // 输出字节流 转 输出字符流 OutputStream out = new FileOutputStream("data/output.txt"); Writer w = new OutputStreamWriter(out); w.write("种花家"); w.flush(); w.close(); } }3. 缓冲流
只要能想办法减少对硬盘的操作,就一定可以提高执行效率
3.1 缓冲流缓冲流是套接在响应的节点流之上,对续写的数据提供缓冲的功能,提高读写的效率,同时增加了一些新方法
以介质是硬盘为例,字节流和字符流的弊端:在每一次读写的时候,都会访问硬盘。
如果读写的频率比较高的时候,其性能表现不佳。为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。
3.2 构造方法缓冲字符流
- BufferedReader(Reader)不定义缓存大小,默认8192
- BufferedReader(Reader in, int size)size为自定义缓冲区的大小
- BufferedWriter(Writer)
- BufferedWriter(Writer out, int size)size为自定义缓冲区的大小
缓冲字节流
- BufferedInputStream(InputStream)
- BufferedInputStream(InputStream in, int size)size为自定义缓冲区的大小
- BufferedOutputStream(OutputStream)
- BufferedOutputStream(OuputStream out, int size)size为自定义缓冲区的大小
对数据流进行整行读取
readLine():String 获取输入流的单行数据,会吃掉行未的换行符
键盘录入BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int age = 0; while (true) { String ss = br.readLine(); try { age = Integer.parseInt(ss); if (age < 18 || age > 150) { System.out.println("您输入的年龄不合法"); } else { break; } } catch (Exception e) { System.out.println("您输入了非法字符"); } } System.out.println("年龄为" + age);为文本文件打上行标签
package com.yang4; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class T05 { public static void main(String[] args) throws FileNotFoundException, IOException { try (BufferedReader rb = new BufferedReader(new FileReader("data/Test01.java")); BufferedWriter bw = new BufferedWriter(new FileWriter("data/output01.txt"))) { int hang = 0; while (true) { String ss = rb.readLine(); if (ss == null) { break; } bw.write((++hang + "t")); bw.write(ss); bw.newLine(); } } } }4. 数据流
只有字节流,没有对应的字符流
DataInputStream和DataOutputStream两个类创建的对象分别被称为数据输入流和数据输出流。所以比较适合于网络上的数据传 输。这两个流也是过滤器流,常以其它流如InputStream或OutputStream作为它们的输入或输出 DataInputStram和DataOutputStream分别继承自InputStream和OuputStream,属于过滤流,需要分别套接在InputStream和OutputStream类型的节点流上
DataInputStream和DataOutputStream提供了可以存取与机器无关的Java原始类型数据的方法
4.1 一些方法数据存取方法
- 有各种针对java中的基本数据类型的读写操纵
构造方法
- DataInputStream(InputStream)
- DataOutputStream(OutputStream)
size():int 获取当前操作的位置
4.2 操作字符串就用这俩,其他的反正就是不合适
readUTF
writeUTF
public class Test83 { public static void main(String[] args) throws Exception { DataOutputStream dos = new DataOutputStream(new FileOutputStream("data/out3.dat")); System.out.println(dos.size());//可以获取当前的输出位置 dos.writeInt(123);//写出一个整型数 System.out.println(dos.size()); dos.writeDouble(12.34); System.out.println(dos.size()); String ss="abcdef"; // dos.writeBytes(ss); dos.writeUTF(ss); //操作字符串的方法 // dos.writeChars(ss); dos.close(); DataInputStream dis=new DataInputStream(new FileInputStream("data/out3.dat")); // dis.skip(4);//跳过4个字节开始读取数据 // double dd=dis.readDouble(); // System.out.println(dd); dis.skip(12); // byte[] data=dis.readAllBytes(); // 读取效果如下图所示存在乱码 String s1=dis.readUTF(); System.out.println(s1); } }6. 打印流
PrintStream和PrintWriter都属于输出流,分别针对字节和字符 PrintWriter和
PrintStream都提供了重载的print和println方法用于输出多种类型数据
print(Object):void
public void println(Object x) { String s = String.valueOf(x); //调用String类中的静态方法将object类型的数据转换为字符串 synchronized (this) { print(s); newLine(); //print('n') } } //String中的valueOf方法的定义 public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); //如果输出对象非空,则调用对象的toString方法 }
println表示输出后自动换行
- PrintWriter和PrintStream的输出操作不会抛出异常,用户通过检测错误状态获取错误信息
- PrintWriter和PrintStream有自动的flush功能 textOut.flushBuffer();
PrintWriter(Writer)
PrintWriter(Writer out, boolean autoFlush)自动刷新----println
PrintWriter(OutputStream out) //参数是一个字节流,但是不需要通过桥接处理 PrintWriter(OutputStream out, boolean autoFlush) PrintStream(OutputStream) PrintStream(OutputStream out, boolean autoFlush)扩展小芝士
- 图片文件格式
- png
- gif
- jpg:控制文件大小