第4节:InputStreamReader和OutputStreamWriter

上一节我们知道了用Reader和Writer可以帮助我们直接将字节流转换成字符流。但是Reader和Writer始终是个抽象类啊,而抽象类是不可以直接被使用的,它必须要创建子类,继承他以后创建子类对象才能用。输入输出流的家族非常的庞大,我并不想在这里展示它的家族体系,因为没有意义。我想通过一篇讲一种流的方式,最后再总结它们的家族体系,这样思路会清晰的多。所以,这一篇我们讲字符读写流。

字符读取流


JDK提供了一个InputStreamReader类供我们进行字符流读操作。它被定义在jdk的java.io包内。我们先来看一看它的定义!

public class InputStreamReader extends Reader {}

显然,它继承了Reader类。我们知道,Reader类中定义了两个抽象方法,我们来看看它是如何实现的!

private final StreamDecoder sd;

public int read(char cbuf[], int offset, int length) throws IOException {
    return sd.read(cbuf, offset, length);
}

public void close() throws IOException {
    sd.close();
}

可以看出来,这个类依赖于StreamDecoder,读操作和关闭操作都是通过这个类的对象来完成的。本想去仔细挖一挖它是如何实现的,但是今天时间有限,我们先定性的认识一下这个类,有时间我们再定量的分析其原理。先来看看如何构建这个类的对象吧!

public InputStreamReader(InputStream in) {}

public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException {}

public InputStreamReader(InputStream in, Charset cs) {}

public InputStreamReader(InputStream in, CharsetDecoder dec) {}

它提供了4个构造方法供我们构建这个类的对象。可以看出,每一个构造方法都必须要一个InputStream对象。第二个参数都和字符编码相关,可以使用字符串说明,也可以用Charset或者CharsetDecoder对象说明。第一个构造方法没有让我们指定字符编码,这样他会使用系统默认的字符编码进行读取。我个人不太推荐这种写法,显式的声明一下字符编码比较好。另外,它还重写了Reader的read()方法,我们来看看!

public int read() throws IOException {
    return sd.read();
}

依然使用了StreamDecoder来读取的,有时间我们来研究一下这个类。现在,我们直接看一下如何用InputStreamReader来对文件进行读取操作吧!

用法


InputStream inputStream = null;
InputStreamReader reader = null;
try {
    File file = new File("MyFile.txt");
    inputStream = new FileInputStream(file);
    reader = new InputStreamReader(inputStream, "UTF-8");
    StringBuilder builder = new StringBuilder();
    char[] buff = new char[4];
    while (true) {
        int length = reader.read(buff);
        if (length != -1) {
            builder.append(Arrays.copyOf(buff, length));
            continue;
        }
        break;
    }
    System.out.println(builder.toString());
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        if (inputStream != null) inputStream.close();
        if (reader != null) reader.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

同样的,我在MyFile.txt里写了Hello,InputStreamReader!这几个字。然后我想用InputStreamReader来读取文件里的内容。但是InputStreamReader需要一个InputStream类型的对象作为参数,但是我们学过FileInputStream啊,它正好是InputStream的子类,而参数是可以以多态的形式存在的。所以我将文件用FileInputStream来读取,用InputStreamReader来包装它,并且声明用UTF-8编码来读取它。另外,我创建了一个长度为4的char类型数组作为缓冲。随后将读取到的字符追加到StringBuilder里,因为我们碰到过数组覆盖的问题,所以我特意用了Arrays的copy方法,读到多少,拷贝多少。最后将内容输出,完美!来看看输出什么吧!

Hello,InputStreamReader!

完全没问题!我们换成中文试试!我把文件内容换成了哈喽,字符读取流!

哈喽,字符读取流!

也没有什么问题!这样,我把字符编码改为GBK,我们再来读取一遍!

// 改动这行代码
reader = new InputStreamReader(inputStream, "GBK");

看看输出:

鍝堝柦锛屽瓧绗﹁鍙栨祦锛�

不出所料,乱码!因为我的文件是按照UTF-8编码存储的,但是用GBK编码来读取的话,肯定是会乱码的。

字符写入流


与之对应的,JDK提供了一个InputStreamWriter类供我们进行字符流读写操作。它也被定义在jdk的java.io包内。我们先来看一看它的定义!

public class OutputStreamWriter extends Writer {}

OutputStream继承了Writer类,而Writer类定义了三个抽象方法,我们来看看它是如何实现的:

private final StreamEncoder se;

public void write(char cbuf[], int off, int len) throws IOException {
    se.write(cbuf, off, len);
}

public void flush() throws IOException {
    se.flush();
}

public void close() throws IOException {
    se.close();
}

和InputStreamReader差不多,OutputStramWriter依赖于一个StreamEncoder对象,而InputStream依赖于一个StreamDecoder对象。其实很容易理解,我们读取数据的时候,我们作为数据的接收方,是被动者,我们当然需要按照一定的规则解码解密,所以是Decoder;而我们发送数据的时候,我们是数据发送方,数据是我们主动发出去的,所以我们可以定义一系列我们自己的规则,而这里的规则就是字符编码表。这就是编码加密的过程,也就是Encoder。就好比我们说的语言,我们中国人说中文,听到并且会中文的人用中文语法来翻译。而美国人说英语,听到并且会英语的人用英语来翻译。而翻译就是将听到的内容用一种语言解密,然后再用另外一种语言加密说出去。语言就是其中的加密解密算法。

再来看看它有没有提供其他的非抽象方法:

public void write(int c) throws IOException {
    se.write(c);
}

public void write(String str, int off, int len) throws IOException {
    se.write(str, off, len);
}

别的非抽象方法并没有,倒是重写了Writer的两个方法。这两个方法都是依赖于StreamEncoder对象来执行的,我们暂且不深究其原理。先来看看如何构造OutputStreamWriter对象:

public OutputStreamWriter(OutputStream out) {}

public OutputStreamWriter(OutputStream out, Charset cs) {}

public OutputStreamWriter(OutputStream out, CharsetEncoder enc) {}

public OutputStreamWriter(OutputStream out, String charsetName) throws UnsupportedEncodingException {}

和InputStreamReader差不多,OutputStreamWriter需要一个OutputStream对象,并且第二个参数是字符编码。如果不写的话,默认用系统默认编码,还是那句话,不推荐!直接看用法:

用法


OutputStream outputStream = null;
OutputStreamWriter writer = null;
try {
    File file = new File("MyFile.java");
    if (!file.exists()) file.createNewFile();
    outputStream = new FileOutputStream(file);
    writer = new OutputStreamWriter(outputStream, "GBK");
    writer.write("Hello,OutputStreamWriter!");
    writer.flush();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        if (outputStream != null) outputStream.close();
        if (writer != null) writer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

常规操作!不过我用的是GBK编码写入的!至此,我们学过的IO流家族体系又多了两个了,来看看!

IO流家族体系


转载于:https://my.oschina.net/u/3962062/blog/3097895

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值