龙空技术网

简单的字符流|全面整理Java字符流知识点,上手容易,出口成章

石添的编程哲学 392

前言:

而今兄弟们对“java文件流字符流”可能比较注意,咱们都需要了解一些“java文件流字符流”的相关知识。那么小编在网上收集了一些关于“java文件流字符流””的相关知识,希望各位老铁们能喜欢,看官们快快来了解一下吧!

[啤酒]满怀忧思,不如先干再说!做干净纯粹的技术分享!欢迎评论区或私信交流!

​[微风]​​代码已上传【gitee】,因平台限制,无法贴出链接,可私信免费获取仓库链接!

如果你还不了解IO流或者字节流可以查阅个人主页【IO合集】内容,本文将介绍字符流,通过本文可以掌握:

字符流的作用,以及与字节流区别字符流的体系结构,即都有哪些常用的API字符流与字节流之间的转换字符流乱码问题产生的原因字符流

在java.io包下操作文件主要有两大类:字节流和字符流,两者都有输入流和输出流,其中还包含了节点流【低级流】和处理流【高级流】

字节流中,输出数据主要使用OutputStream完成,输入数据主要使用InputStream完成

字符流中,输出数据主要使用Writer完成,输入数据主要使用Reader完成。【这四者都是抽象类】

字节流和字符流的关系

字节流是最基本的,所有的InputStrem和OutputStream的子类都是字节流,主要用在处理二进制数据,按字节来处理数据,但很多的数据是文本,就提出了字符流的概念,它是按虚拟机的encode【编码】来处理,也就是要进行字符集的转化,这两个之间通过 InputStreamReader,OutputStreamWriter来关联,记住最底层操作的还是字节

与字节流一问相同,贴出IO体系结构

InputStreamReader和OutputStreamWriter

首先说一下字节流和字符流之间的桥梁,InputStreamReader和OutputStreamWriter,要知道的是计算机存储数据都是通过字节存储,不可能直接存储字符,那么在字节和字符之间一定存在一种转换机制,也就是通过这种机制让字节和字符关联起来。

InputStreamReader

作用是读取数据时,将字节流转换为字符流,并且能为字节流指定字符集,可输出一个个的字符。

读取数据

如有以下文件,其中内容为汉字,使用的编码表为UTF-8

此时使用下方代码读取文件,并输出

public class InputStreamReaderTest {    public static void main(String[] args) throws IOException {        FileInputStream fileInputStream = new FileInputStream("D:\\iotest\\1.txt");        InputStreamReader isr = new InputStreamReader(fileInputStream);        StringBuilder sb = new StringBuilder();        char[] buff = new char[1024];        int read;        while ((read = isr.read(buff)) != -1) {            sb.append(buff,0,read);        }        isr.close();        System.out.println(sb.toString());    }}

执行结果,发现输出的数据没有问题,那是因为读取数据时需要将文件中的数据由字节解码成字符时使用的码表和文件存储时的码表都是UTF8不会乱码

注意:如果解码和编码时使用的码表不同,就会乱码,比如我在读取数据时使用GBK解码,就会出现以下乱码情况

// 指定码表InputStreamReader isr = new InputStreamReader(fileInputStream,"GBK");

乱码现象,有很多看不懂的符号,就是解码和编码时使用的码表不同

OutputStreamWriter

作用是在写出数据时,将字符流转换为字节流,继承结构如下:

写数据到文件中案例

public class OutputStreamWriterTest {    public static void main(String[] args) throws IOException {        FileOutputStream fileOutputStream = new FileOutputStream("D:\\iotest\\1.txt");        OutputStreamWriter osw = new OutputStreamWriter(fileOutputStream);        osw.write("青蜂侠");        osw.flush();        osw.close();    }}

运行结果,数据写入到文件中

Reader

Reader是字符输入流的顶级抽象类,定义了输入流的基础方法,其结构如下:

基本方法:

// 从输入流读取一个字符read()// 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 read(char cbuf[])// 从offset位置开始,读取length个字符到cbuf中,返回结果是实际读取的字符数,到达流的末尾时,返回-1read(char cbuf[], int off, int len)// 跳过指定字符数skip(long n)// 告诉此流是否可以读取ready()// 判断此流是否支持mark操作【标记操作】markSupported()// 标记流中的当前位置mark(int readAheadLimit)// 重置流reset()// 关闭此流并释放与此流相关联的任何系统资源close()
Writer

Writer是字符输出流的顶级抽象类,定义了输出流的基础方法,内置一个保存字符串和单个字符写入的临时缓冲,大小为1024,其结构如下:

基本方法:

// 写出字符数组数据write(char cbuf[])// 写出字符数组的一部分write(char cbuf[], int off, int len)// 写出一个字符串write(String str)// 写出字符串一部分write(String str, int off, int len)// 将指定的字符序列附加到此编写器append(CharSequence csq)// 追加一部分数据append(CharSequence csq, int start, int end)// 追加字符append(char c)// 冲洗流。将数据刷新到目的地。 flush()// 关闭流close()
FileReader和FileWriter

FileReader和FileWriter是最基础的字符输入流和字符输出流

FileWriter

基础的字符输出流,可以将数据写出到文件中,继承关系如下

源码:该类源码比较简单,都是构造方法,没有自己的特有方法

public class FileWriter extends OutputStreamWriter {    // 接收文件名,构造FileOutputStream    public FileWriter(String fileName) throws IOException {        super(new FileOutputStream(fileName));    }    // 接收文件名,构造FileOutputStream,append为是否追加数据到文件末尾,true:追加,false:覆盖    public FileWriter(String fileName, boolean append) throws IOException {        super(new FileOutputStream(fileName, append));    }    // 根据File对象构造    public FileWriter(File file) throws IOException {        super(new FileOutputStream(file));    }    // 根据File对象构造,并传入是否追加数据    public FileWriter(File file, boolean append) throws IOException {        super(new FileOutputStream(file, append));    }    // 根据文件符构造【基本不用】    public FileWriter(FileDescriptor fd) {        super(new FileOutputStream(fd));    }}

写出数据案例:将键盘输入的字符串写入到文件中

// 1、创建键盘录入Scanner scanner = new Scanner(System.in);String inputStr = scanner.next();// 2、创建字符输出流FileWriter writer = new FileWriter(new File("D:\\iotest\\filewriter-test.txt"));// 3、写出数据writer.write(inputStr);// 4、刷新流,将数据从内存中刷新到目的地writer.flush();// 5、关闭流writer.close();

运行结果:文件不存在则创建文件,最终数据写入文件中,如果指定append参数为true,则会追加数据到文件中,否则就是覆盖数据

FileReader

基础的字符输入流,可以将数据读入到系统中,继承关系如下

源码:该类源码与FileWriter对应,都是构造方法,没有自己的特有方法

public class FileReader extends InputStreamReader {   // 读取指定路径的数据,通过构造FileInputStream转换为字符流    public FileReader(String fileName) throws FileNotFoundException {        super(new FileInputStream(fileName));    }   // 根据File构造    public FileReader(File file) throws FileNotFoundException {        super(new FileInputStream(file));    }   // 根据文件符构造【基本不用】    public FileReader(FileDescriptor fd) {        super(new FileInputStream(fd));    }}

读取数据案例:将上一个案例中数据读出到系统内并打印,如上一个案例中的文件比较小,或者说文件大小确认可以创建合适的字符数组暂存数据

// 1、创建读取目标文件File file = new File("D:\\iotest\\filewriter-test.txt");// 2、创建字符输入流FileReader reader = new FileReader(file);// 3、创建字符缓冲区,缓冲区大小与文件大小一致,即可以存放所有数据char[] buff = new char[(int) file.length()];// 4、读取数据,读取的字符存储进缓冲区中int read = reader.read(buff);// 5、关闭输入流reader.close();// 6、将字符数组转换为字符串String s = new String(buff, 0, read);// 7、输出数据System.out.println(s);

运行结果:文件不存在则报错,将读取到的数据成功打印在控制台

若文件大小不确定,创建一个自定义的字符数据,循环读取,追加到StringBuilder中

// 1、创建读取目标文件File file = new File("D:\\home\\micheng\\logs\\sys-info.2022-11-17.log");// 2、创建字符输入流FileReader reader = new FileReader(file);// 3、创建字符缓冲区char[] buff = new char[1024];StringBuilder sb = new StringBuilder();// 4、记录读取的数据量int read;// 5、读取数据,读取的字符存储进缓冲区中while ((read = reader.read(buff)) != -1) {    sb.append(buff,0,read);}// 6、关闭输入流reader.close();// 7、将字符数组转换为字符串String s = sb.toString();// 8、输出数据System.out.println(s);

读取文件结果没有问题

拷贝文件

字符流也可以向字节流那样拷贝文件,比如将日志文件拷贝到iotest目录下

// 1、指定源文件File srcFile = new File("D:\\home\\micheng\\logs\\sys-info.2022-11-17.log");// 2、指定目的文件File distFile = new File("D:\\iotest\\copy.log");// 3、创建输入流FileReader reader = new FileReader(srcFile);// 4、创建输出流FileWriter writer = new FileWriter(distFile);// 5、创建缓冲区char[] buff = new char[1024];// 6、记录读取数据量int read;System.out.println("开始拷贝");while ((read = reader.read(buff)) != -1) {    // 写数据    writer.write(buff,0,read);}// 7、刷新流writer.flush();// 8、关闭流reader.close();writer.close();System.out.println("拷贝完毕");

拷贝完成

此种方式读取数据还是按照字节读取,然后拼接起来转换成字符,我们可以通过下边的方式直接获取字符,不需要转换

BufferedReader和BufferedWriter

BufferedReader和BufferedWriter是处理流,接收基础的字符流进而转换方便我们对字符的读写操作

BufferedReader

可以把字符输入流进行封装,这里使用的还是装饰者模式,将数据进行缓冲,提高读取效率。它除了可以使用基类定义的函数,它自己还实现了以下函数:

// 读取一个文本行,以行结束符作为末尾,返回结果是读取的字符串。如果已到达流末尾,则返回 nullreadLine()
BufferedWriter

BufferedWriter比FileWriter还高级一点,它利用了缓冲区来提高写的效率。它还多出了一个函数:

// 写入一行数据newLine()
拷贝文件
// 1、指定源文件File srcFile = new File("D:\\home\\micheng\\logs\\sys-info.2022-11-17.log");File distFile = new File("D:\\iotest\\buffered-copy.log");BufferedReader bufferedReader = new BufferedReader(new FileReader(srcFile));BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(distFile));String line;// 循环读取,一次读取一行while ((line = bufferedReader.readLine()) != null) {    // 写如一行数据    bufferedWriter.write(line);    // 因为读取一行,写一行之后录入一个换行符    bufferedWriter.newLine();}bufferedWriter.flush();// 关闭流bufferedReader.close();bufferedWriter.close();
CharArrayReader 和 CharArrayWriter

CharArrayReader 和 CharArrayWriter 是字符数组输入流和字符数组输出流,它们同 ByteArrayIuputStream 和 ByteArrayOutputStream 类似,只不过一个是字节流,一个是字符流。CharArrayReader 和 CharArrayWriter 内部维护了一个字符数组,用来缓存数据。数据都在内存中,并不会操作磁盘

CharArrayReader

字符数组输入流,继承于 Reader,用于读取字符数组,操作的数据是以字符为单位。继承体系如下

构造方法如下:

// 从指定字符数组中创建CharArryaReaderpublic CharArrayReader(char buf[])// 从指定字符数组创建CharArrayReader,并设置开始位置和大小public CharArrayReader(char buf[], int offset, int length)

方法列表:

// 关闭此流,并释放与流相关联的任何系统资源。close()// 将流重新设置为最近的标记,或将其重新设置为从未被标记的开始reset()// 标记流中的当前位置mark(int readAheadLimit)// 告诉这个流是否支持mark() 操作。markSupporte()// 读一个字符read()// 将字符的一部分读入数组read(charl b, int off, int len)// 告诉这个流是否准备好被读取ready()// 跳过 n个字符skip(long n)

读取字符数组案例:

public class CharArrayReaderTest {    public static void main(String[] args) throws IOException {        // 定义字符数组        char[] charArr = {'c','h','a','r','a','r','r','a','y','r','e','a','d','e','r'};        CharArrayReader reader = new CharArrayReader(charArr);        int read;        while ((read = reader.read()) != -1) {            System.out.println((char) read);        }        reader.close();    }}
CharArrayWriter

字符数组输出流,继承于 Writer,用于写入字符数组,操作的数据是以字符为单位。继承体系如下

构造方法如下:

// 创建CharArrayWriter对象,默认缓冲区大小32public CharArrayWriter()// 指定初始化大小public CharArrayWriter(int initialSize)

方法列表:

// 关闭此流,并释放与流相关联的任何系统资源close()// 将指定的字符附加到此输出流append(char c)// 将指定的字符序列附加到此输出流append(CharSequence csq)// 将指定字符序列的子序列附加到此输出流append(CharSequence csq, int start, int end)// 刷新流    flush()// 重置缓冲区,以便您可以再次使用它,而不会丢弃已经分配的缓冲区reset()// 返回缓冲区的当前大小size()// 返回输入数据的副本toCharArray()// 将输入数据转换为字符串toString()// 标记流中的当前位置write(charll c, int off, int len)// 将一个字符写入缓冲区write(int c)// 将一部分字符串写入缓冲区write(String str, int off, int len)// 将缓冲区的内容写入另一个字符流writeTo(riter out)

将写入的数据转换为字符数组

public class CharArrayWriterTest {    public static void main(String[] args) throws IOException {                // 定义字符数组输出流        CharArrayWriter writer = new CharArrayWriter();        // 写数据到内存中        writer.write("CharArrayWriter");        writer.write("abcd");        // 将数据转换为字符数组        char[] chars = writer.toCharArray();        // 将数据转换成字符串输出        System.out.println(new String(chars));            }}

不禁问到这有什么用呢?字符数组流一般应用不多,如果你在做一个文字的聊天系统,那么就可以使用它了,接触过网络编程的朋友应该知道两个计算机聊天的文本内容可以通过字符数组传递,即发送方输出数据,将字符串转字符数组,接收方输入数据,将字符数组数据读出来再转换为字符串显示

接下来简单演示一下输出数据和接收数据

public class CharArrayWriterReaderTest {    // 暂存消息数组    public static char[] messageArr;        public static void main(String[] args) throws IOException, InterruptedException {        // 发送消息        sendMessage();        // 睡眠,等待上一个任务执行完毕        TimeUnit.SECONDS.sleep(1);        // 接收消息        receiveMessage();    }    // 发送消息    public static void sendMessage() throws IOException {        System.out.println("发送数据");        // 定义数据文本        String message = "这里是地球,人类经过漫长的劳动和创造,建立了灿烂的文明,涌现出丰富多彩的文化";        // 创建输出流        CharArrayWriter writer = new CharArrayWriter();        // 写出数据        writer.write(message);        // 通过输出流将数据转换成字节数组,因为没有网络编程,定义公共数组存放消息        messageArr = writer.toCharArray();        // 关闭流        writer.close();    }        // 接收消息    public static void receiveMessage() throws IOException {        System.out.println("接收数据");        CharArrayReader reader = new CharArrayReader(messageArr);        StringBuilder sb = new StringBuilder();        //        int read;        char[] buff = new char[1024];        while ((read = reader.read(buff)) != -1) {            sb.append(buff,0,read);        }        reader.close();        System.out.println(sb.toString());    }}

运行之后可以正常接收到发送出去的消息,如果是网络编程则可以实现网络通信聊天功能

字节流和字符流区别字节流数据单元由字节组成,可以处理任意类型的数据,主要用来处理二进制数据, 如视频,音频,图片等字符流数据单元由字符组成,只能处理字符和字符串,按虚拟机的encode【编码】处理,如:xml、yml、txt等,在读数据时将字节转换为字符,在写数据时将字符转成字节,转换过程均参考码表,如果读写数据使用的码表不同就会乱码字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,如果是关系到中文【文本】的,用字符流好点【1Unicode = 2字节 = 16位】

这就是本文介绍的字符流内容,下一篇将介绍管道流,记得关注一下哦,感谢您的支持!

标签: #java文件流字符流 #java字符流写文件 #java的字符