• 【Java学习笔记九】——I/O流之字符流与数据流


    声明:本文章内容主要摘选自尚硅谷宋红康Java教程、《Java核心卷二》、廖雪峰Java教程,示例代码部分出自本人,更多详细内容推荐直接观看以上教程及书籍,若有错误之处请指出,欢迎交流。

    I/O流的笔记附带比较长的实例代码,所以分成几篇发。

    一、字符流

    1.Reader

    Reader是Java的IO库提供的另一个输入流接口。和InputStream的区别是,InputStream是一个字节流,即以byte为单位读取,而Reader是一个字符流,即以char为单位读取。

    java.io.Reader是所有字符输入流的超类,它最主要的方法是:

      public int read() throws IOException;
    

    这个方法读取字符流的下一个字符,并返回字符表示的int,范围是0~65535。如果已读到末尾,返回-1。

    2.Reader实现类

    FileReader是Reader的一个子类,它可以打开文件并获取Reader。下面的代码演示了如何完整地读取一个FileReader的所有字符:

    public void readFile() throws IOException {
        // 创建一个FileReader对象:
        Reader reader = new FileReader("src/readme.txt"); // 字符编码是???
        for (;;) {
            int n = reader.read(); // 反复调用read()方法,直到返回-1
            if (n == -1) {
                break;
            }
            System.out.println((char)n); // 打印char
        }
        reader.close(); // 关闭流
    }
    

    如果我们读取一个纯ASCII编码的文本文件,上述代码工作是没有问题的。但如果文件中包含中文,就会出现乱码,因为FileReader默认的编码与系统相关,例如,Windows系统的默认编码可能是GBK,打开一个UTF-8编码的文本文件就会出现乱码。

    要避免乱码问题,我们需要在创建FileReader时指定编码:

      Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8);
    

    和InputStream类似,Reader也是一种资源,需要保证出错的时候也能正确关闭,所以我们需要用try (resource)来保证Reader在无论有没有IO错误的时候都能够正确地关闭:

    try (Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8) {
        // TODO
    }
    

    Reader还提供了一次性读取若干字符并填充到char[]数组的方法:

      public int read(char[] c) throws IOException
    

    它返回实际读入的字符个数,最大不超过char[]数组的长度。返回-1表示流结束。

    利用这个方法,我们可以先设置一个缓冲区,然后,每次尽可能地填充缓冲区:

    public void readFile() throws IOException {
        try (Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8)) {
            char[] buffer = new char[1000];
            int n;
            while ((n = reader.read(buffer)) != -1) {
                System.out.println("read " + n + " chars.");
            }
        }
    }
    
    • CharArrayReader

    CharArrayReader可以在内存中模拟一个Reader,它的作用实际上是把一个char[]数组变成一个Reader,这和ByteArrayInputStream非常类似:

    try (Reader reader = new CharArrayReader("Hello".toCharArray())) {
    }
    
    • StringReader

    StringReader可以直接把String作为数据源,它和CharArrayReader几乎一样:

    try (Reader reader = new StringReader("Hello")) {
    }
    

    3.InputStreamReader

    Reader和InputStream有什么关系?

    除了特殊的CharArrayReader和StringReader,普通的Reader实际上是基于InputStream构造的,因为Reader需要从InputStream中读入字节流(byte),然后,根据编码设置,再转换为char就可以实现字符流。如果我们查看FileReader的源码,它在内部实际上持有一个FileInputStream。

    既然Reader本质上是一个基于InputStream的byte到char的转换器,那么,如果我们已经有一个InputStream,想把它转换为Reader,是完全可行的。InputStreamReader就是这样一个转换器,它可以把任何InputStream转换为Reader。示例代码如下:

    // 持有InputStream:
    InputStream input = new FileInputStream("src/readme.txt");
    // 变换为Reader:
    Reader reader = new InputStreamReader(input, "UTF-8");
    

    构造InputStreamReader时,我们需要传入InputStream,还需要指定编码,就可以得到一个Reader对象。上述代码可以通过try (resource)更简洁地改写如下:

    try (Reader reader = new InputStreamReader(new FileInputStream("src/readme.txt"), "UTF-8")) {
        // TODO:
    }
    

    上述代码实际上就是FileReader的一种实现方式。

    使用try (resource)结构时,当我们关闭Reader时,它会在内部自动调用InputStream的close()方法,所以,只需要关闭最外层的Reader对象即可。

    使用InputStreamReader,可以把一个InputStream转换成一个Reader。

    //转换流
          @Test
        public void test1() throws IOException{
            FileInputStream fis = new FileInputStream("a.txt");
    
            InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
    
            char[] cbuf = new char[20];
            int len;
            while((len = isr.read(cbuf)) != -1){
                String str = new String(cbuf, 0, len);
                System.out.print(str);
            }
            isr.close();
        }
    

    4.Writer

    Reader是带编码转换器的InputStream,它把byte转换为char,而Writer就是带编码转换器的OutputStream,它把char转换为byte并输出。

    Writer是所有字符输出流的超类,它提供的方法主要有:

    • 写入一个字符(0~65535):void write(int c)
    • 写入字符数组的所有字符:void write(char[] c)
    • 写入String表示的所有字符:void write(String s)

    如果流使用的构造器是:FileWriter(file,false)/FileWriter(file):对原有文件进行覆盖
    如果流使用的构造器是:FileWriter(file,true):不会对原有文件爱益,而是在原有文件基础上追加内容

    @Test
        public void test1() throws IOException {
            File file = new File("a2.txt");
    
            FileWriter fw1 = new FileWriter(file, true);
            fw1.write("Hello World!");
    
            fw1.close();
    
            File file1 = new File("a3.txt");
            FileWriter fw2 = new FileWriter(file1, false);
            fw2.write("Hello World!");
    
            fw2.close();
        }
    

    5.Writer实现类

    • FileWriter

    FileWriter就是向文件中写入字符流的Writer。它的使用方法和FileReader类似:

    try (Writer writer = new FileWriter("readme.txt", StandardCharsets.UTF_8)) {
        writer.write('H'); // 写入单个字符
        writer.write("Hello".toCharArray()); // 写入char[]
        writer.write("Hello"); // 写入String
    }
    
    • CharArrayWriter

    CharArrayWriter可以在内存中创建一个Writer,它的作用实际上是构造一个缓冲区,可以写入char,最后得到写入的char[]数组,这和ByteArrayOutputStream非常类似:

    try (CharArrayWriter writer = new CharArrayWriter()) {
        writer.write(65);
        writer.write(66);
        writer.write(67);
        char[] data = writer.toCharArray(); // { 'A', 'B', 'C' }
    }
    
    • StringWriter

    StringWriter也是一个基于内存的Writer,它和CharArrayWriter类似。实际上,StringWriter在内部维护了一个StringBuffer,并对外提供了Writer接口。

    • OutputStreamWriter

    除了CharArrayWriter和StringWriter外,普通的Writer实际上是基于OutputStream构造的,它接收char,然后在内部自动转换成一个或多个byte,并写入OutputStream。因此,OutputStreamWriter就是一个将任意的OutputStream转换为Writer的转换器:

    try (Writer writer = new OutputStreamWriter(new FileOutputStream("readme.txt"), "UTF-8")) {
        // TODO:
    }
    

    上述代码实际上就是FileWriter的一种实现方式。这和上一节的InputStreamReader是一样的。

    二、数据流

    • DataInputStream 和 DataOutputStream

    • 作用:用于读取或写出基本数据类型(如布尔型,字节,整数和浮点数)的变量或字符串

     @Test
        public void test1() throws IOException{
            DataOutputStream dos = null;
    
            try{
                dos = new DataOutputStream(new FileOutputStream("a.txt"));
    
                dos.writeUTF("晓潇");
                dos.flush();//立即在文件中刷新
                dos.writeInt(18);
                dos.writeBoolean(true);
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        @Test
        public void test2() throws IOException {
            DataInputStream dis = null;
            try {
                dis = new DataInputStream(new FileInputStream("a.txt"));
    
                String name = dis.readUTF();
                int age = dis.readInt();
                boolean isMan = dis.readBoolean();
                System.out.println("姓名:" + name);
                System.out.println("年龄:" + age);
                System.out.println("是否为单身:" + isMan);
    
            } finally {
                try {
                    dis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    

    此笔记仅针对有一定编程基础的同学,且本人只记录比较重要的知识点,若想要入门Java可以先行观看相关教程或书籍后再阅读此笔记。

    最后附一下相关链接:
    Java在线API中文手册
    Java platform se8下载
    尚硅谷Java教学视频
    《Java核心卷二》

  • 相关阅读:
    loading加载动画
    js获取文本的行数
    获取浏览器结尾生成的参数,提交的时候对其做判断
    js获取当前浏览器地址栏的链接,然后在链接后面加参数
    a链接在新窗口打开
    js随机出现2个数字
    css进行中打点效果
    jqeury显示前几个,隐藏后几个,点击后隐藏前几个显示后几个
    jQuery 源码解析(七) jQuery对象和DOM对象的互相转换
    jQuery 源码解析(六) $.each和$.map的区别
  • 原文地址:https://www.cnblogs.com/66ccffly/p/13537083.html
Copyright © 2020-2023  润新知