java 节点流

  • Post author:
  • Post category:java


一、节点流

1、文件节点流

FileInputStream和FileOutputStream是

文件字节流,是一种节点流

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

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

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

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






FileReader和FileWriter类似。如果文件内容是文本则使用xxxReader和xxxWriter;如果文件内容是二进制则使用xxxInputStream和xxxOutputStream。


FileInputStream和FileOutputStream两个类属于结点流,第一个类的源端和第二个类的目的端都是磁盘文件,它们的构造方法允许通过文件的路径名来构造相应的流。

如:

FileInputStream infile = new FileInputStream(“myfile.dat”);
FileOutputStream outfile = new FileOutputStream(“results.dat”);

要注意的是,构造FileInputStream, 对应的文件必须存在并且是可读的,而构造FileOutputStream时,如输出文件已存在,则必须是可覆盖的。

要求:将d盘上的TestFile.java拷贝到e盘,并命名为dd.txt

 try (InputStream is = new FileInputStream("d:/TestFile.java");
      OutputStream os=new FileOutputStream("e:\\dd.txt");
 ) {
     byte[] buffer=new byte[8192];
     int len=is.read(buffer);
     while(len>0){
         os.write(buffer,0,len);
         len=is.read(buffer);
     }
 }catch(IOException e){
     e.printStackTrace();
 }
 //构造输出文件流时可以使用两种不同的方式
 OutputStream os=new FileOutputStream("e:\\dd.txt"); //如果文件不存在则自动创建;如果文件存在则进行覆盖
 OutputStream os=new FileOutputStream("e:\\dd.txt",true) //这里的boolean类型参数表示是否采用追加的方式写入文件

需求1:拷贝c:\windows到d:\ddd文件夹

需求2:设计程序将aa.java和bb.java两个文件合并成一个文件,命名为cc.txt。

需求3:设计程序将cc.txt按尺寸等分拆分成两个文件c1.txt和c2.txt。

2、内存数组节点流


如果内存数组内容是文本则使用char[],是二进制则使用byte[]。

构造器方法

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

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

ByteArrayInputStream、ByteArrayOutputStream和CharArrayReader以及CharArrayWriter类似,支持操作的内容不同而已,操作byte[]与char[]。

3、内存

数组

节点

StringReader用于从一个字串String中读取数据。

String str="亚洲说:‘我爱小黑’";
StringReader sr=new StringReader(str);
int cc;
while((cc=sr.read())!=-1)
System.out.print((char)cc);
sr.close();

StringWriter用于给一个StringBuffer中写入数据,实现一个可边长的字串

Scanner sc=new Scanner(System.in);
try(
        StringWriter sw=new StringWriter();
        Writer fw=new FileWriter("c:/console.txt")
){
    String temp=sc.nextLine();
    while(!temp.equals("quit")){
        if(temp!=null && temp.trim().length()>0)
            sw.write(temp+"\n");
        temp=sc.nextLine();
    }
    fw.write(sw.toString());
}

总结

  • 读写文件使用节点流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中提供了方法getBuffer():StringBuffer

二、过滤流类型

过滤流就是在节点流的基础上附加功能。
在这里插入图片描述

1、过滤流

就是decorate模式中的抽象装饰角色FilterInputStream/FilterOutputStream和FilterReader/FilterWriter。

public class FilterInputStream extends InputStream { //典型的装饰模式
            protected volatile InputStream in; //被装饰目标
            protected FilterInputStream(InputStream in) { //通过构造器组装被装饰对象
                this.in = in;
            }
            public int read() throws IOException {//调用Filter中的read方法时实际操作是由
                被装饰对象实现的
                return in.read();
            }
        }

所谓的过滤流实际上就是类似上面的加密处理,在输入之后(后置处理,被装饰对象先执行)或者输出之前(前置处理,先处理然后被装饰对象执行)进行一下额外的处理,最终实际操作是调用被装饰对象的方法完成工作,依靠这种装饰模式实现在节点流的基础上附加额外功能.当然也允许多个过滤流嵌套从而达到功能累加的目的

FilterInputStream实际上就是一个装饰抽象角色。

自定义流实现循环13加密:

读取数据不变:FileReader—BufferedReader

写出数据自定义过滤流SecurityWriter(FilterWriter)

public class SecurityWriter extends FilterWriter {
    protected SecurityWriter(Writer out) {
        super(out);
    }
    public void write(int c) throws IOException {
        if (c >= 'a' && c <= 'z') {
            c = (c - 'a' + 13) % 26 + 'a';
        } else if (c >= 'A' && c <= 'Z') {
            c = (c - 'A' + 13) % 26 + 'A';
        }
        super.write(c);
    }
}

public class SecurityReader extends FilterReader {
    protected SecurityReader(Reader in) {
        super(in);
    }
    public int read() throws IOException {
        int c = super.read();
        if (c >= 'a' && c <= 'z') {
            c = (c - 'a' + 13) % 26 + 'a';
        } else if (c >= 'A' && c <= 'Z') {
            c = (c - 'A' + 13) % 26 + 'A';
        }
        return c;
    }
}

加密算法:

数字:如果不是9的数字,在原来的基础上加1,比如5变成6, 3变成4,如果是9的数字,变成0。

字母字符:如果是非z字符,向右移动一个,比如d变成e, G变成H,如果是z,z->a, Z-A。字符需要保留大小写

非字母字符:比如‘,&^ 保留不变,中文也保留不变

数字字符解密-1可以等价于+9,字母解密-1可以等价于+25

2、桥接转换流

InputStreamReader和OutputStreamWriter是java.io包中用于处理字符流的最基本的类

,用来在字节流和字符流之间作为中介:从字节输入流读入字节,并按编码规范转换为字符;往字节输出流写字符时先将字符按编码规范转换为字节。使用这两者进行字符处理时,在构造方法中应指定一定的平台规范,以便把以字节方式表示的流转换为特定平台上的字符表示


转换流可以在构造时指定其

编码字符集。

  • InputStreamReader用于将一个InputStream类型的输入流自动转换为Reader字符流
  • OutputStreamWriter用于将一个Writer字符输出流转换为OutputStream字节输出流


InputStreamReader构造器

  • InputStreamReader(InputStream)
  • InputStreamReader(InputStream, String)
  • InputStreamReader(InputStream, Charset)
  • InputStreamReader(InputStream, CharsetDecorder)
Reader r=new InputStreamReader(System.in);
int kk=r.read(); //例如输入的是“中国”,这里实际读取的是"中"
//因为这里读取的是一个字节,所以输入"中国",实际读取的是"中"的一个字节,输出显示为?
kk=System.in.read();
System.out.println("输入的是:"+(char)kk);


InputSteram is=new InputStreamReader(System.in,”iso8859-1”);

Reader r=new InputStreamReader(System.in, "gbk");
int kk=r.read(); //例如输入的是"中国",实际读取的是"中"
System.out.println("输入的是:"+(char)kk);


注意:一般不建议自行设置编码字符集,除非是必须的


在这里插入图片描述

3、 缓冲流

缓冲流是套接在响应的节点流之上,对续写的数据提供缓冲的功能,提高读写的效率,同时增加了一些新方法。

以介质是硬盘为例,字节流和字符流的弊端:在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。为了解决以上弊端,采用缓存流。



缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取

InputStream is = new FileInputStream("d:\\FileTest.java");
long start=System.currentTimeMillis();//获取从1970-1-1 0:0:0到当前的毫米值
int con = is.read();
while (con != -1) {
    System.out.write(con);
    con = is.read();
}
long end=System.currentTimeMillis();
is.close();
System.out.println("统计时间为:"+(end-start)+"ms");
60ms


构造方法

  • 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为自定义缓冲区的大小
InputStream is = new BufferedInputStream(new FileInputStream("d:\\FileTest.java"));
long start=System.currentTimeMillis();
int con = is.read();//这里并不是直接从文件中进行读取,而是从缓存中进行读
while (con != -1) {
    System.out.write(con);
    con = is.read();
}
long end=System.currentTimeMillis();
is.close();
System.out.println("统计时间为:"+(end-start)+"ms");
is.close();
10ms


缓冲输入流的方法

BuffedReader提供了一个方法readLine():String,但是BufferedInputStream中并没有这个

  • BufferedReader提供了readLine方法用于读取一行字符串,以\r或\n分割(换行符)

如果读取内容为null,则表示读取到了流的末尾

readLine方法会自动剔除本行内容末尾的换行符

  • BufferedWriter提供了newLine方法用于写入一个行分隔符


对于输出的缓冲流,写入的数据会先在内存中缓存,使用flush方法会使内存中的数据立即写出

4、键盘录入

System.in:InputStream用于指代

系统默认的输入设备—键盘。方法read():int 可以实现代码执行到这里则会阻塞等待,只要输入数据为止

BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入数据");
String temp="";
while((temp=br.readLine()).trim().length()>0){
    if("quit".equals(temp)) break;
    System.out.println(temp);
}
br.close();
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
bw.write("只有缓冲区满才自动进行输出显示");
bw.flush(); //刷新缓冲区,否则看不到输出内容
System.in.read();
bw.close(); //关闭输出时会首先自动进行刷新缓冲区


强调:

使用缓存流并没有添加什么额外方法,只是它能够在执行过程中自动引入缓存,从而提高执行效率

过滤流使用必须有对应的节点流,因为过滤流是装饰节点流,不是有具体的操作目标

new BufferedReader(new FileReader(…))或new BufferedWriter(new FileWriter())实际上使用的还是Reader/Writer那些方法,这里从编码的角度上说,没有任何区别,但是从执行性能上说,比FileReader/Writer效率高,可以减少磁盘的读写次数

执行close方法会自动关闭被装饰的对象,所以不需要再关闭FileReader和FileWriter

执行flush会自动刷新数据到节点流上,但是并没有执行关闭流。针对输出流关闭时会自动先flush缓存再执行关闭

5、数据流

DataInputStream和DataOutputStream两个类创建的对象分别被称为数据输入流和数据输出流。这是很有用的两个流,它们允许程序按与机器无关的风格读写Java数据。

所以比较适合于网络上的数据传输

。这两个流也是过滤器流,常以其它流如InputStream或OutputStream作为它们的输入或输出

DataInputStram和DataOutputStream分别继承自InputStream和OuputStream,属于过滤流,需要分别套接在InputStream和OutputStream类型的节点流上。



只有字节流,没有对应的字符流




DataInputStream和DataOutputStream提供了可以存取与机器无关的Java原始类型数据的方法

DataInputSteram和DataOutputStream构造方法为

  • DataInputStream(InputStream)
  • DataOutputStream(OutputStream)

1、用数据输出流将菲波那契级数的前二十项存放在fei.dat文件中。

2、从fei.dat文件中读出数列,显示输出,并计算累加和。

读取、写出一个double数据到文件中

//使用数据流就可以直接操作简单类型数据
double dd=123.4567;
FileOutputStream fos=new FileOutputStream("d:\\a.data");
fos.write((dd+"").getBytes());
fos.close();
//如果不使用数据流,则需要额外编码进行数据类型转换
FileInputStream fis=new FileInputStream("d:/a.data");
byte[] buffer=new byte[8192];
int len=fis.read(buffer);
fis.close();
String str=new String(buffer,0,len);
double dd=Double.parseDouble(str);
System.out.println(dd);

假如需要写一个double,然后一个String,然后再一个int。需要将输入内容转换为String,并且为了区分数据需要引入特殊符号,例如@@,输入数据为123.456@@shi ya zhou@@12。从功能角度上说没问题,但是编码太复杂了,所以引入Data类型的输入输出流。

//这里不使用OutputStream定义变量的原因是:需要使用DataOutputStream中定义的特殊方法,而
//不是父类中定义的通用方法
DataOutputStream dos=new DataOutputStream(new
FileOutputStream("d:\\a.data"));
dos.writeDouble(123.456);
dos.writeChars("赵天爱小猴!");
dos.writeInt(12);
dos.close();

由于读取出现问题,针对中间的String数据引入一个额外的数据,其中存储String的char个数


写出数据

double salary=123.456;
String ss="赵天爱小猴,小猿爱小主,小胡招小天";
int age=12;
DataOutputStream dos=new DataOutputStream(new BufferedOutputStream(new
FileOutputStream("d:\\aa.data")));
dos.writeDouble(salary);
dos.writeInt(ss.length());
dos.writeChars(ss);
dos.writeInt(age);
dos.close();


读取数据

double salary=0;
String str="";
int age=0;
//读取数据的前提是必须知道数据的结构
DataInputStream dis=new DataInputStream(new BufferedInputStream(new
FileInputStream("d:\\aa.data")));
salary=dis.readDouble();
StringBuilder sb=new StringBuilder();
int len=dis.readInt();
for(int i=0;i<len;i++) sb.append(dis.readChar());
str=sb.toString();
age=dis.readInt();
System.out.println(salary+"---"+str+"---"+age);



注意

:读取数据判断文件结束EOFException,这里没有-1。在具体应用中建议针对字串使用

readUTF和writeUTF


DataOutputStream dos=new DataOutputStream(new FileOutputStream("data2.txt"));
dos.writeDouble(1234.56);
String str="猴子愛小終,小終愛信心";
dos.writeUTF(str);
dos.writeInt(99);
dos.close();


DataInputStream dis = new DataInputStream(new FileInputStream("data2.txt"));
double d1 = dis.readDouble();
String ss=dis.readUTF();
int kk = dis.readInt();
System.out.println(d1 + "\t" + ss + "\t" + kk);
dis.close();

6、打印流

PrintStream和PrintWriter都属于输出流,分别针对字节和字符。PrintWriter和PrintStream都提供了重载的print和println方法用于输出多种类型数据print(Object):void。



输出引用类型,实际上是调用对象的toString方法转换为String进行输出。

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)