InputStream&OutputStream 与 Reader&Writer

Java 中的字符是Unicode编码的,是双字节的(即:1char = 2bytes)。而InputStream与OutputStream是用来处理字节或字节数组的,在处理字符文本的时候时不太方便,需要编写额外的程序代码。

Java为字符文本的输入输出专门提供了一套单独的类,Reader、Writer两个抽象类与InputStream、OutputStream两个抽象类相对应,他们都有很多的子类,对具体的I/O设备进行字符输入输出,如:FileReader就是用来读取文件流中的字符的。

Reader、Writer与InputStream、OutputStream用法相类似,唯一区别是前者处理字符方便,后者处理字节方便。

另外:FileReader和FileWriter使用了缓冲区,而FileInputStream和FileOutputStream没有使用缓冲区。

例:

InputStream、OutputStream

package cn.edu.bupt;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileStream
{
    public static void main(String[] args)
    {
        File f = new File("Hello.txt");
        
        try
        {
            OutputStream os = new FileOutputStream(f);
            byte[] txt = "hello world".getBytes();
            os.write(txt);
            os.close();
        }
        catch (IOException e) 
        {
            // TODO: handle exception
            e.printStackTrace();
        }
        
        try
        {
            InputStream is = new FileInputStream(f);
            byte[] txt = new byte[100];
            int len = is.read(txt);
            String s = new String(txt, 0, len);
            System.out.println(s);
            is.close();
        }
        catch (IOException e) 
        {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}

Reader、Writer

package cn.edu.bupt;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

public class ReaderAndWriter
{
    public static void main(String[] args)
    {
        File f = new File("hello.txt");
        
        try
        {
            Writer writer = new FileWriter(f);
            writer.write("hello,mylady");
            writer.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        
        try
        {
            Reader reader = new FileReader(f);
            char[] c = new char[100];
            int len = reader.read(c);
            String s = new String(c, 0, len);
            System.out.println(s);
            reader.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

现在介绍一些方法:

InputStream(abstract class):

该类中的所有方法在遇到错误的时候都会引发IOException异常.

1> abstract int read()

  返回下一个输入字节(byte)的整型表示,如果返回-1表示遇到了流的末尾.

2> int read(byte[] b)

  在InputStream中读入b.length个字节放到b中,并返回实际读入的字节数.

3> int read(byte[] b, int off, int len)

  这个方法表示把流中的数据读到数组b中从下标为off开始的len个数组元素中.

4> long skip(long n)

  跳过输入流上的n个byte并返回实际跳过的字节数.

5> int available()

  返回当前输入流中的可读的字节数.

6.1> void mark(int readlimit)

  在输入流的当前位置出放置一个游标,允许最多再读入readlimit个字节.

6.2> void reset()

  把输入流中的指针返回到以前所做的游标处.

6.3> boolean markSupported()

  如果当前流支持mark/reset操作就返回true.

7> void close()

  在操作完一个流后要使用此方法将相关的系统资源进行释放.

OutputStream(abstract class):

这个类中的方法均返回void,并在遇到错误的时候引发IOException异常

1> void write(int b)

  讲一个字节写到输入流,注意这里的参数是int型,它允许write使用表达式而不用强制转换成为byte类型.

  你写入的数值转化成Unicode了,例如你用write(65),你在文件里便就会看到一个大写字母'A'.

  写入的是字节,即使大于8位,只写低8位.

2> void write(byte[] b)

  将整个字节数组写到输出流中.

3> void write(byte[] b, int off, int len)

  将字节数组b中的从off开始的len个字节写到输出流中.

4> void flush()

  彻底完成输出并清空缓冲区.

5> void close()

  关闭输出流.

例:

package cn.edu.bupt;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


public class FileStream
{
    public static void main(String[] args)
    {
        new FileStream().transform(System.in, System.out);
    }
    
    private void transform(InputStream in, OutputStream out)
    {
        try
        {
            int c = -1;
            while ((c = in.read()) != -1)
            {
                out.write(c);
                out.flush();
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            System.out.println("close");
            try
            {
                out.close();
            }
            catch (IOException e)
            {
                // TODO Auto-generated catch block
                System.out.println("cannot close");
                e.printStackTrace();
            }
        }
    }
}

具体的基本节点流类有:

InputStream:

  1> FileInputStream

  2> PipedInputStream

  3> ByteArrayInputStream

OutputStream:

  1> FileOutputStream

  2> PipedOutputStream

  3> ByteArrayOutputStream

Reader:

  1> FileReader

  2> PipedReader 

  3> StringReader

Writer:

  1> FileWriter

  2> PipedWriter

  3> StringWriter

流的包装类:

包装类是针对基本流类而言的,因为基本流类仅仅提供了读写字节(byte)的方法,我们通过他们只能往文件中写入字节或从文件中读取字节,但是在实际应用中,我们要往文件中写入或读取各种类型的数据,我们就必须先将其他类型的数据转换成为字节数组后写入文件,或是将从文件中读取到的字节数组转换成为其他类型,这个就需要流包专类的帮助。

包装类提供了读写各种类型数据的各种方法,当我们需要写入其他类型的数据的时候,只要调用中间类中的对应的方法即可,在这个中间类的方法内部,他将其他数据类型转换成为字节数组,然后调用底层的节点流类将这个字节数组写入目标设备,这个中间类就是包装类。

例如io包中的DataOutputStream包装类中的方法如下:

public final void writeBoolean(boolean v) throws IOException;
public final void writeShort(short v) throws IOException;
public final void writeChar(char v) throws IOException;
public final void writeInt(int v) throws IOException;
public final void writeLong(long v) throws IOException;
public final void writeFloat(float v) throws IOException;
public final void writeDouble(double v) throws IOException;
public final void writeBytes(String s) throws IOException;

我们还可以用包装类去包装另外一个包装类。

创建包装类对象的时候,必须制定它要调用的那个底层流对象,也就是这些包装类的构造函数中,都必须接受另外一个流对象作为参数。例如:

public DataOutputStream(OutputStream out);

其中的out传入参数可以是OutputStream的任何一个子类或是包装类(其实也是他的子类,查看参考手册可以知道),这样最上层的包装类可以具有下层包装类的性质。

解析DataInputStream与DataOutputStream

DataOutputStream提供了三种写入字符串的方法:

public final void writeBytes(String s) throws IOException
public final void writeChars(String s) throws IOException
public final void writeUTF(String str) throws IOException

区别:

Java中的字符是Unicode编码,是双字节的,writeBytes()值时将字符串中的每一个字符的低字节位内容写入到目标设备中;而writeChars()将字符串中的每一个字符的两个字节内容都写入到目标设备中;writeUTF对字符串按照UTF的格式写入目标设备,UTF是带有长度头的,最开始的两个字节是对字符串进行UTF编码后的字节长度,然后才是每个字符的UTF编码。

在于DataOUtputStream类对应的输入流DataInputStream类中只提供了一个readUTF方法返回字符串,而没有提供其他两个与之对应的方法读入字符串,这是因为我们要在一个连续的字节流读取一个字符串(只是六种的一段内容),如果没有特殊的标记作为一个字符串的结尾,而且我们实现也不知道这个字符串的长度,我们是没有办法知道读取到什么位置才是这个字符串的结束。

在DataoutputStream类中只有writeUTF方法项目表设备中写入了字符串的长度,所以我们只能准确的读回这个方法写入的字符串。

package cn.edu.bupt;

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 FileStream
{
    public static void main(String[] args)
    {
        try
        {
            FileOutputStream fos = new FileOutputStream("hello.txt");    //基本节点流类
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            DataOutputStream dos = new DataOutputStream(bos);
            
            dos.writeUTF("abc 中国");
            dos.writeBytes("abc 中国");
            dos.writeChars("abc 中国");
            
            dos.close();
            
            FileInputStream fis = new FileInputStream("hello.txt");
            BufferedInputStream bis = new BufferedInputStream(fis);
            DataInputStream dis = new DataInputStream(bis);
            
//            byte[] b = new byte[100];
//            int len = fis.read(b);
//            String s = new String(b, 0, len);    //按照字节数组读入,会出现乱码,因为读入的byte类型数据的机制不同
            String s = dis.readUTF();
            System.out.println(s);
            dis.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

PrintStream包装类:

PrintStream类提供了一些列的print和println方法

posted @ 2023-01-31 20:44  不穿衣服去逛街  阅读(39)  评论(0)    收藏  举报