Filter Streams

Filter Streams:

 

InputStream和OutputStream逐个或一组字节读写。这些字节的意思是什么?——是整数或IEEE 754浮点数或Unicode文本?——这些完全由程序员和代码来决定。Java提供了一些filter类,你可以将它们对接到raw streams,实现raw bytes和其他格式间的相互转换。

(1)many intergers passed as parts of network protocols are 32-bit big-endian integers

(2)much of the text send over the Web is either 7-bit ASCII, 8-bit latin-1, or multibyte UTF-8

(3)Many files transferred by FTP are stored in the ZIP format

 

filters分2种版本:filter streams;readers and writers

(1)filter streams主要还是以字节来处理raw data

(2)readers and writers 处理各种编码的文本

 

=======1、Chaining Filters Together

Filters are connected to streams by their constructors:

 

FileInputStream     fin = new FileInputStream("data.txt");

BufferedInputStream bin = new BufferedInputStream(fin);

 

这个时候,用fin或者bin都能从文件data.txt读取数据,但不要混合用它们,因为这违背了filter streams的若干隐性的契约。Most of the time, you should only use the last filter in the chain to do the actual reading

or writing。为了避免这种问题,你可以这么写:

InputStream in = new FileInputStream("data.txt");

in = new BufferedInputStream(in);

这样,你就没法访问底层的文件输入流了。

 

但是如果有必要使用filter流中的额外方法,这些额外方法当然在超类中是没有的,那就应该这么写:

DataOutputStream dout = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("data.txt")));

 

=======2、Buffered Streams

    BufferedOutputStream将写出的数据放到一个buffer中直到buffer满了或流被flushed,然后,它会将这些数据一次性写到底层的输出流上。一次写一组字节性能更高,对于网络连接就更是这样。

    BufferedInputStream内部也有一个字节数组叫作buf,当它的某个read方法被调用时 ,它首先会到buffer中去获取请求的数据,只有当buffer中没有数据时,它才到底层的源上读取。这个时候,它会从底层源上尽量多读一些数据到buffer中。

    public BufferedInputStream(InputStream in)

    public BufferedInputStream(InputStream in, int bufferSize)

    public BufferedOutputStream(OutputStream out)

    public BufferedOutputStream(OutputStream out, int bufferSize)

 

    BufferedInputStream自己没有声明任何新方法。它只是重写了InputStream中的方法。它支持marking和reseting。BufferedOutputStream同样自己也没有声明任何新方法。

 

=======3、PrintStream

    PrintStream是大多数程序员都会遇到的首个filter输出流,因为System.out就是一个PrintStream。不过,其他输出流也可以被链接到print流上,用这2个构造方法:

public PrintStream(OutputStream out)

public PrintStream(OutputStream out, boolean autoFlush)

    默认,print流应当显式flushed。然而,如果autoFlush是true,那么每次当写入一个字节数组或一个换行,或调用了println()方法,流就会被flushed。

    不但有常用的write,flush,close方法,PrintStream还有9个重载的print方法和10个重载的println方法。

每个print方法都会将其参数转换为一个字符串,然后将该字符串写到底层输出流上去,用的是默认的编码。

println做的是同样的事,只不过它还要追加一个特定于平台的行分隔符。在Unix(包括Mac OS X)上是一个linefeed(\n),在Mac OS 9上是carriage return(\r),在Windows上是carriage return/linefeed对(\r\n)

    网络程序员不要用PrintStream:

(1)第1个问题是println的输出是特定于平台的。根据你的程序在什么样的系统上运行,换行可能是linefeed或是carriage return或是carriage return/line feed对。如果是写到控制台上,这倒不是个问题,但当编写必须遵循一个精确的协议的网络客户端和服务器时就是个灾难了。大多数网络协议,如HTTP和Gnutella,它们都是指定行的中断用的是carriage return/linefeed对,所以,用println可以很容易地写出能够在Windows上正常工作的程序,但是在Unix和Mac上就会出错。尽管许多服务器和客户端能够处理不正确的行终止符,但也会有偶尔的异常发生。

(2)第2个问题是PrintStream假设的默认编码是它运行的那个平台的编码。但是这样的编码可能不是服务器或客户端所期望的。比如说,一个web浏览器接收XML文件将期望文件用UTF-8或UTF-16来编码。然而,一个使用了PrintStream的服务器,如果服务器是一个来自美国的Windows系统,那么发送的文件将用CP1252编码,如果服务器是一个来自日本的系统,那么编码就是SJIS,不管客户端是否期望就是这样的编码。PrintStream没有提供任何机制用于改变这种默认编码。

(3)第3个问题是PrintStream会吃掉所有的异常。这使得它非常适合教科书程序,例如HelloWorld,因为讲授这样的简单控制台输出,就不会要求学生们先学异常处理了。但网络肯定不比控制台稳定,所以你应该处理数据传输过程中发生的异常,但是PrintStream已经捕捉了来自底层输出流上的异常了,你会发现PrintStream中那几个标准的输出流方法并没有声明throws IOException。相反,PrintStream依靠的是一个过时的并且还是不够充分的错误标记。如果底层的流抛出一个异常,该内部标记就会被设置。程序员就需要用checkError()方法来检查这个标记。代码必须显式检查每一个调用。而且,一旦发生了一个错误,是没有办法重置这个标记的,所以后续发生的错误也没法检测了。关于错误的额外信息也是没有的。

 

=======4、Data Streams

DataInputStream和DataOutputStream提供了一些方法用于以二进制格式读写Java的原始数据类型以及String类型。用binary formats主要目的是用于在2个不同的java程序间交换数据,这2个java程序交换数据用的是一个网络连接、一个datafile、管道、或是其他中间媒介。一个数据输出流写了什么,一个数据输入流就能读入什么。然而这是恰巧大多数交换binary numbers的Internet协议用的都是相同的格式。Java和大多数网络协议都是Unix程序员设计的,因此,它们都趋向于使用Unix系统常用的格式。然而,并不是所有的网络协议都是这样的,所以你要检查一下你所使用的协议的细节。

-------------------------------DataOutputStream类提供的用于写入特定Java数据类型的方法---------------

public final void writeBoolean(boolean b) throws IOException

public final void writeByte(int b) throws IOException

public final void writeShort(int s) throws IOException

public final void writeChar(int c) throws IOException

public final void writeInt(int i) throws IOException

public final void writeLong(long l) throws IOException

public final void writeFloat(float f) throws IOException

public final void writeDouble(double d) throws IOException

public final void writeChars(String s) throws IOException

public final void writeBytes(String s) throws IOException

public final void writeUTF(String s) throws IOException

    所有的数据都是以big-endian格式写入的。Integers are written in two's complement in the minimum number of bytes possible。这样的话,一个byte被写为一个字节,一个shore被写为2个字节,int就是4个字节,long就是8个字节。Floats和doubles用的是IEEE 754格式,分别是4个和8个字节。Booleans是1个字节。Chars是2个unsigned字节。

    后面的3个方法,其中writeChars()就是顺序迭代那个String参数中的每个字符,写为2个字节、big-endian Unicode 字符(a UTF-16 code point, to be absolutely precise)。writeBytes()方法也是迭代String参数中的每个字符,但只是写入每个字符的least significant byte。这样,一个含有来自于Latin-1字符集之外字符的任何字符串的信息就会丢失。该方法对于一些指定ASCII编码的网络协议来说可能是有用的,但大多数时候应避免用它。

    writeChars()和writeBytes()都没有将字符串的长度编码在输出流中。结果就是,你就不能区分raw characters和那些只是一个字符串的一部分的那些characters了。writeUTF()就包含了字符串的长度信息,它将字符串本身以Unicode中的UTF-8编码来编码。由于UTF-8有点不兼容大多数非java软件,所以它只应当被用在和其他用了DataInputStream的java程序之间交换数据。要和其他软件交换UTF-8文本的话,你应当用InputStreamReader再配上合适的编码。

    DataInputStream提供了9个对应于DataOutputStream的方法,但没有提供对应writeBytes和WriteChars的方法,而是你应该通过每次读一个字节或一个字符来处理。

public final boolean readBoolean() throws IOException

public final byte readByte() throws IOException

public final char readChar() throws IOException

public final short readShort() throws IOException

public final int readInt() throws IOException

public final long readLong() throws IOException

public final float readFloat() throws IOException

public final double readDouble() throws IOException

public final String readUTF() throws IOException

    另外,DataInputStream还提供了用于读取unsigned bytes和unsigned shorts并返回等价的int值的方法,这2种数据类型,Java都没有,但你如果读取由C语言写的binary data时可能就会遇到:

public final int readUnsignedByte() throws IOException

public final int readUnsignedShort() throws IOException

    DataInputStream还提供了这几个方法:

public final int read(byte[] input) throws IOException

public final int read(byte[] input, int offset, int length) throws IOException

public final void readFully(byte[] input) throws IOException

public final void readFully(byte[] input, int offset, int length) throws IOException

用于将数据读入一个数组或子数组中,并返回它读到的字节数。起中readFully方法就是不断地从底层输入流读取数据,直到达到它请求的字节数,但是如果不能读取你要求的字节数,就会抛IOException。这样的方法是用在当你事先知道你需要读多少字节,比如,你从一个HTTP头中的Content-length字段得知了字节数。

    最后,DataInputStream还提供了一个受欢迎的readLine()方法用于读一行文本:

public final String readLine() throws IOException

但是,在任何情况下都不要使用该方法,不仅仅是它是废弃的有bug的。过时的是因为它在大多数情况下都不能正确的将non-ASSCII字符转换为bytes。该任务现在由BufferedReader来的readLine()来处理,但这2个方法都存在同样的隐秘的bug。

猜你喜欢

转载自zsjg13.iteye.com/blog/2226743