流之过滤器流(数据流)

DataInputStreamDataOutputStream类提供了一些方法,可以用二进制格式读/写Java的基本数据类型和字符串。所用的二进制格式主要用于在两个不同的Java程序之间交换数据(可能通过网络连接、数据文件、管道或者其他中间介质)。输出流写入什么数据,输入流就能读取什么数据。不过,这碰巧与大多数交换二进制数的Internet协议所用的格式相同。例如,时间协议使用32位big-endian整数,类似于Java的int数据类型。负载受控的网络元素服务使用32位IEEE754浮点数,类似于Java的float数据类型(这是有关联的,而不只是巧合。Java和大多数网络协议都是由UNIX程序员设计的,因此都会倾向于使用大多数UNIX系统中的常见格式)。然而,这并不适用于所有网络协议,所以请检查你使用的协议的具体细节。例如,网络时间协议(NTP)会把时间表示为64位无符号定点数,前32位是整数部分,后32位是小数部分。这与所有常见编程语言中的基本数据类型都不相同,不过处理起来相当简单。

DataOutputStream类提供了下面11种方法,可以写入特定的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格式写入。整数用尽可能少的字节写为2的补码。因此,byte会写为1字节,short写为2字节,int写为4字节,long写为8字节。浮点数和双精度数分别写为4字节和8字节的IEEE754格式。布尔数写为1字节,0表示false,1表示true。字符写为两个无符号字节。

最后三个方法有些棘手writeChars()方法只是对String参数迭代(循环)处理,将各个字符按顺序写为一个2字节的big-endian Unicode字符(确切地讲是UTF-16码点)。writeBytes()方法迭代处理String参数,但只写入每个字符的低字节。因此,如果字符串中包含有Latin-1字符集以外的字符,其中的信息将会丢失。对于一些指定了ASCII编码的网络协议来说,这个方法或许有用,但多数情况下都应当避免使用

writeChars和writeBytes都不会对输出流的字符串的长度编码因此,你无法真正区分原始字符和作为字符串一部分的字符。writeUTF()方法则包括了字符串的长度。它将字符串本身用Unicode UTF-8编码的一个变体进行编码。由于这个变体编码方式与大多数非Java软件有点不兼容,所以应当只用于与其他使用DataInputStream读取字符串的Java程序进行数据交换为了与所有其他软件交换UTF-8文本,应当使用有适当编码的InputStreamReader

除了这些写入二进制数字和字符串的方法,DataOutputStream当然还有所有OutputStream类都有的平常的write()、flush()和close()方法

DataInputStream与DataOutputStream是互补的。DataOutputStream写入的每一种格式,DataInputStream都可以读取。此外,DataInputStream还有通常的read()、available()、skip和close()方法 ,以及读取整个字节数组和文本行的方法。

有9个读取二进制数据的方法,这些方法对应于DataOutputStream的11个方法(writeBytes()或writeChars()没有相应的读取方法,这要通过一次读取1字节和字符来处理):

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提供了两个方法,可以读取无符号字节无符号短整数,并返回等价的intJava没有这些数据类型,但在读取C程序写入的二进制数据时会遇到

public final int readUnsignedByte() throws IOException
public final int readUnsignedShort() throws IOException

DataInputStream有两个通常的多字节read()方法,可把数据读入一个数组或子数组,并返回读取的字节数。它还有两个readFully()方法,会重复地从底层输入流向一个数组读取数据,直到读取了所请求的字节数为止如果不能读取到足够的数据,就会抛出IOException异常如果你能提前知道要读取多少字节,这些方法尤其有用。例如,如果你已经从HTTP首部读取了Content-length(内容长度)字段,就能知道有多少字节的数据,这种情况下就可以很好地利用这些方法:

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

最后,DataInputStream还提供了流行的readLine()方法,它读取用行结束符分隔的一行文本,并返回一个字符串:

public final String readLine() throws IOException

不过,任何情况下都不要使用这个方法,不仅是因为它已被废弃,而且它还有bug之所以将这个方法废弃,是因为在大多数情况下它不能正确地将非ASCII字符转换为字节。这个任务现在由BufferedReader类的readLine()方法来处理。不过,这两个方法都存在同一个隐含的bug它们并不总能把一个回车识别为行结束。实际上,readLine()只能识别换行回车/换行对。在流中检测到回车时,readLine()在继续之前会等待,查看下一个字符是否为换行。如果是换行,就抛掉这个回车和换行,把这一行作为String返回。如果不是换行,就抛掉这个回车,把这一行作为String返回,刚读取的这个额外的字符会成为下一行的一部分。不过,如果回车是流的最后一个字符那么readLine()会挂起,等待最后一个字符的出现,但这个字符永远也不会出现

这个问题在读取文件时不太明显,因为几乎可以肯定会有下一个字符:如果没有别的字符,那么会由 -1 表示流结束不过,在持久的网络连接中(如用FTP和新型HTTP的连接),服务器或客户端可能只是在最后一个字符之后停止发送数据,并等待响应,而不会真正关闭连接。如果幸运,最终可能某一端的连接超时,你将得到一个IOException异常,不过这可能至少要花费几分钟,而且会使你丢失流的最后一行数据。如果不够幸运,程序将永远挂起

我们来看一个DataInputStream与DataOutputStream的示例:

package test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataInputSreamTest1 {
    public static void main(String[] args) {
        String fileUrl = "F:/data.txt";
        try(DataOutputStream out = new DataOutputStream(
                new BufferedOutputStream(new FileOutputStream(fileUrl)))){
            out.writeInt(123);
            out.writeLong(4567890L);
            out.writeBoolean(false);
            out.writeByte(-3);
            out.writeUTF("中国人民的老朋友");
            out.writeByte(-10);
            out.writeShort(-10);
            out.flush();
        }catch(IOException e){
            e.printStackTrace();
        }
        try(DataInputStream in = new DataInputStream(
                new BufferedInputStream(new FileInputStream(fileUrl)))){
            System.out.println(in.readInt());
            System.out.println(in.readLong());
            System.out.println(in.readBoolean());
            System.out.println(in.readByte());
            System.out.println(in.readUTF());
            System.out.println(in.readUnsignedByte());
            System.out.println(in.readUnsignedShort());
        }catch(IOException e){
            e.printStackTrace();
        }
    }        
}

一般来说,DataOutputStream与DataInputStream成对出现的机率高

你可能感兴趣的:(java-IO与网络编程)