字符流

昨天我们学习了字节输入流的学习,我们对字节流已经全部学习完成啦,但是对于IO流来说,我们还需要学习另外一种流——字符流。

而字符流也可以分为字符输入流和字符输出流,所以今天我们准备对字符输入流进行一个详细的了解吧!

字符输入输出流与字节输入输出流有相同的功能,但传送数据的方式不一样,字节流以字节为单位传送数据,可以使任何类型的数据,例如文本、音频、视频、图片等。字符流以字符为单位传送数据,只能传送文本类型的数据。使用字符输入输出流的好处是,当读取中文时不会出现乱码问题,而使用字节输入输出流时,却不能保证这一点。因为一个中文字符可能占用了多个字节存储。

具体会发生什么问题呢,我们来看看吧,那有办法解决吗,

public class CharaterTest {
    public static void main(String[] args) throws IOException {
        //FileInputStream为操作文件的字符输入流
        FileInputStream fis = new FileInputStream("E:\\demo\\testTxt.txt");

        int len;
        while ((len = fis.read()) != -1) {
            System.out.print((char) len);
        }

        fis.close();
    }
}

程序运行结果: 
hello±±¼«ÐÇÏòÄã·¢³öÊÓƵÁÄÌì

这。。。都是些什么,我猜你肯定在怀疑我是不是坑你,是不是故意这么写的?当然,不是的啦,像我这么善良、纯真、质朴的集优秀品质为一身的这么一个人,会坑你?还是来看下我的文件信息吧。

因为,字节流读取中文字符时,可能不会显示完整的字符,那是因为一个中文字符占用多个字节存储。可是可是,我没办法使用字节流来读取了吗,那还有什么办法读取啊?

方法肯定还是有的,只是对于读取中文来说,不是特别的方便,而且后面我们也进行学习字符流,这个对读取中文来说,特别的方便,对于这种纯文本来说,首选是字符流。

那我们先来看看怎么解决吧

public class CharaterTest2 {
    public static void main(String[] args) throws IOException {
        //FileInputStream为操作文件的字符输入流
        FileInputStream fis = new FileInputStream("E:\\demo\\testTxt.txt");

        byte[] b = new byte[1024];
        int len = 0;
        while ((len = fis.read(b)) != -1){
            System.out.print(new String(b, 0, len));
        }

        fis.close();
    }
}

程序运行结果:
    hello北极星向你发出视频聊天

可以读取到了,你说,要是女神突然发信息说要找你视频聊天,可是你的电脑或者手机显示的是一堆乱码,你的脑袋是否有一堆问号。

可能你还在想女神发的是啥,是不是在骂你。但是可能这就是你错过了拥有女神的机会了,所以要学好Java啊。

回到正题,那为什么使用了String了就可以正常显示了呢,不要着急,容我带大家一起来看看。

public String(byte bytes[], int offset, int length) {
    checkBounds(bytes, offset, length);
    this.value = StringCoding.decode(bytes, offset, length);
}

static char[] decode(byte[] ba, int off, int len) {
    ....  
}

可以看到String的构造方法,有decode解码功能, 默认为utf-8,也是现在默认的编码格式,如果各位通过String的构造方法还是乱码的话,就代表了可能你的文本不是utf-8格式,可能是GBK或者是ANSI格式等其他格式编码,后面再具体讲解如何解决编码格式的问题。现在可以先这样:

可以这样认为:字符流 = 字节流 + 编码表。

字符输入流的父类(Reader)

Reader 是所有的输入字符流的父类,它是一个抽象类。主要作用是可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。

体系结构

  • InputStreamReader是一个连接字节流和字符流的桥梁,它读取字节,并使用指定的字符集将其解码为字符。
  • FileReader 是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
  • BufferedReader 字符缓冲流,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
  • FilterReader 是所有自定义具体装饰流的父类。

Reader中的方法

在我们学完字节流后,可以发现IO流中输入流都是一样有read()。字符输入流后续继承的子类都有这些方法,需要read()方法进行读取文件,使用close()方法关闭资源。

FileReader

1.构造方法

  • public FileReader(File file): 创建一个新的FileReader ,给定要读取的File 对象。

  • public FileReader(String fileName): 创建一个新的 FileReader ,给定要读取的文件的名称。

    创建代码演示:

    public class FileReaderTest {
        public static void main(String[] args) throws FileNotFoundException {
    
            // 使用File对象创建流对象
            File file = new File("e:\\demo\\a.txt");
            FileReader fr = new FileReader(file);
    
            // 使用文件名称创建流对象
            FileReader fr2 = new FileReader("e:\\demo\\b.txt");
            
            //FileReader fr3 = new FileReader("e:\\demo\\c.txt");
            //一样如果读取不到文件就会报异常,
            //Exception in thread "main" java.io.FileNotFoundException: e:\demo\c.txt (系统找不到指定的文件。)
        }
    }

    可以看到和字节流的创建方式基本一样,读取不到文件会报异常。

2. 读取字符数据

字符流就是用来读取文本文件的,那接下来我们来测试下吧:

  1. public int read() :每次可以读取一个字符的数据,提升为int 类型,读取到文件末尾,返回-1 。可以测试得出和字节流一样,如果读到没有数据,就会返回-1 。直接使用循环读取:

    public class ReaderTest {
        public static void main(String[] args) throws IOException {
            // 使用文件名称创建流对象
            FileReader fr = new FileReader("e:\\demo\\testTxt.txt");
            // 定义变量,保存数据
            int ch = 0;
            // 循环读取
            //public int read()
            while ((ch = fr.read())!=-1) {
                System.out.print((char) ch);
            }
            // 关闭资源
            fr.close();
        }
    }
    
    程序执行结果:
       hello北极星向你发出视频聊天
    

    注意:

    • 这里一样读取得到还是int 类型,需要强转为字符类型。才能显示为原本文件的信息。
    • 还是像之前一样,必须得关闭资源。
  2. public int read(char cbuf[]):每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1 。直接使用循环演示:

    public class ReaderTest2 {
        public static void main(String[] args) throws IOException {
            // 使用文件名称创建流对象
            FileReader fr = new FileReader("e:\\demo\\testTxt.txt");
            // 定义变量,保存有效字符个数
            int len = 0;
            // 定义字符数组,作为装字符数据的容器
            char[] cbuf = new char[2];
            // 循环读取
            //public int read(char cbuf[])
            while ((len = fr.read(cbuf)) != -1) {
                System.out.println(new String(cbuf));
            }
            // 关闭资源
            fr.close();
        }
    }
    
    程序执行结果:
    北极
    星向
    你发
    出视
    频聊
    天聊

    可以看到,他还是多了一个字符,跟字节输入流一样的问题,那我们使用获取有效的字符改进:

    public class ReaderTest3 {
        public static void main(String[] args) throws IOException {
            // 使用文件名称创建流对象
            FileReader fr = new FileReader("e:\\demo\\testTxt.txt");
            // 定义变量,保存有效字符个数
            int len = 0;
            // 定义字符数组,作为装字符数据的容器
            char[] cbuf = new char[2];
            // 循环读取
            //public int read(char cbuf[])
            while ((len = fr.read(cbuf)) != -1) {
                System.out.println(new String(cbuf, 0, len));
            }
            // 关闭资源
            fr.close();
        }
    }
    
    程序执行结果:
    北极
    星向
    你发
    出视
    频聊
    天

    小结下:

    我们发现其实大多数方法我们在字节流都使用过,基本来说用起来基本都差不多。对于纯文本来说优先考虑字符流,而其他情况就只能用字节流了。

总结

相信各位看官都对IO流中字符输入流类有了一定了解,期待等待下一章的字符输出流教学吧!

当然还有很多流等着下次一起看吧!欢迎期待下一章的到来!

学到这里,今天的世界打烊了,晚安!虽然这篇文章完结了,但是我还在,永不完结。我会努力保持写文章。来日方长,何惧车遥马慢!

感谢各位看到这里!愿你韶华不负,青春无悔!

注: 如果文章有任何错误和建议,请各位大佬尽情留言!如果这篇文章对你也有所帮助,希望可爱亲切的您给个三连关注下,非常感谢啦!

文章分类


作者:***爷哪吒
链接:https://juejin.cn/post/6993972029665837087
来源:掘金