0803(023天 输入输出流03 节点流+过滤流)
每日一狗(田园犬西瓜瓜)
文章目录
复习:
应用场景
- 字节流:针对二进制位文件 (音频,视频)
- 字符流:针对文本文件(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 |
1. 文件节点流
FileInputStream和FileOutputStream是文件字节流,是一种节点流
1.1 构造方法
输入流
-
FileInputStream(“文件名称”),如果文件不存在则报错FileNotFoundException
-
FileInputStream(File)
输出流
- FileOutputStream(“文件名称”) 如果文件不存在则新建文件,如果文件存在则覆盖文件内容
- FileOutputStream(String name文件名称, boolean append是否采用追加方式)
1.2 基本方法
输入read
- read():读取并返回整形数据
- read(byte[]):将数据读取之字节数组中,返回值为int,有用数据的最后索引
输出write
- write(int):将(char)int 写入到输出流中
- write(byte[]):将字节数组中的数据写入到输出流中
2. 内存流
2.1 可变长数组
用于适配不同已经封装好的方法中参数的类型;
这个工具方法要的是输入流,另一个工具方法又要的是输出流(方法内部无法重写)
可以用一个内存输入流先充当这个输入流,在获取到这个输入流的字节数组,在把他封装到一个输出流,传入到另一个工具方法中使用。
使用中间流来进行数据格式的转换
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 {
/**
中
华
家
t
e
s
t
abcdefghijklmnopqrstuvwxyz{
*/
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 |
1. 过滤流的实现
就是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为自定义缓冲区的大小
3.2 缓冲输入流
对数据流进行整行读取
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:控制文件大小