java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java字符流

详解Java I/O流中的字符流有哪些

作者:一一哥Sun

字节流的功能已经十分强大,几乎可以直接或间接地处理任何类型的输入/输出操作,但它却不能直接操作16位的Unicode字符,这就需要使用字符流,所以在今天的内容中,小编会给大家讲解IO流中的字符流,希望各位能够继续耐心学习

Java中的字符流,可以分为字符输入流(Reader)和字符输出流(Writer),输入流用于读取数据,输出流用于写入数据,接下来就让我们来逐个了解吧。

一. Reader字符输入流

1. 简介

Reader是I/O库中用于读取字符流的抽象类,它提供了一组方法用于读取字符流中的字符,并支持不同的字符编码。

实际上,除了特殊的CharArrayReader和StringReader,普通的Reader本质上就是一个带有编码转换器的InputStream,它可以把byte转换为char。也就是说,普通的Reader就是基于InputStream构造出来的,比如在FileReader的源码内部就有一个FileInputStream对象。而我们也完全可以把一个InputStream转换为Reader,比如:

// InputStream对象
InputStream is = new FileInputStream("a.txt");
// InputStream转为Reader
Reader reader = new InputStreamReader(is, "UTF-8");

在这里,InputStreamReader就是一种用于将字节流转换为字符流的转换流,使用转换流可以在一定程度上避免乱码,还可以指定输入输出所使用的字符集。其中,InputStreamReader用于将字节输入流转换为字符输入流,OutputStreamWriter用于将字节输出流转换为字符输出流

Reader和InputStream之间有关系,当然也有很多区别,比如:

  • InputStream是一个字节流,即以byte为单位读取,读取的字节范围是(-1,0~255),数据会读到字节数组中;
  • 而Reader是一个字符流,要以char为单位读取,读取的字符范围是(-1,0~65535),数据会读到字符数组中。

2. 常用子类

因为Reader是抽象类,所以我们不能直接对其进行实例化,如果我们想使用字符输入流,需要使用Reader的某个具体子类来实例化对象。Reader的常用子类有如下几个:

  • InputStreamReader类:将字节输入流转换为字符输入流,可以指定字符编码;
  • BufferedReader类:为其他字符输入流提供读缓冲区;
  • CharArrayReader类:将字符数组转换为字符输入流,从中读取字符;
  • StringReader类:将字符串转换为字符输入流,从中读取字符;
  • FileReader类:用于读取文本文件,并支持不同的字符编码;
  • PipedReader类:连接到一个PipedWriter。

3. 常用方法

Reader字符流中的常用方法,其实与InputStream中的常用方法基本一致,比如:

方法名及返回值类型说明
int read()从输入流中读取一个字符,并把它转换为 0~65535 之间的整数。如果返回 -1, 则表示已经到了输入流的末尾。该方法不常用。
int read(char[] cbuf)从输入流中读取若干个字符,并把它们保存到cbuf参数指定的字符数组中。最终返回读取的字符数,如果返回 -1,则表示已经到了输入流的末尾。
int read(char[] cbuf,int off,int len)从输入流中读取若干个字符,并把它们保存到cbuf参数指定的字符数组中。off表示字符数组中开始保存数据的起始下标,len表示读取的字符数。最后返回实际读取的字符数,如果返回 -1,则表示已经到了输入流的末尾。

4. 实现步骤

如果我们想使用Reader读取字符流,可以遵循以下基本步骤:

创建一个Reader对象,比如InputStreamReader、FileReader、StringReader等;

调用Reader的read()方法来读取字符流,该方法会返回一个整数,表示读取的字符数。如果已到达流的末尾,则read()方法返回-1;

处理读取的字符,可以将它们存储在数组或字符串中;

循环调用read()方法,直到读取完整个字符流。

5. 代码案例

为了读取方便,Java给我们提供了用来读取字符文件的便捷类——FileReader,所以这里利用FileReader读取了一个文本文件。

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Demo08 {
    public static void main(String[] args) throws FileNotFoundException, IOException {
        // try(resource)的写法
        //1. 创建Reader对象
        try (FileReader reader = new FileReader("F:/a.txt")) {
            //设置编码为UTF-8
            //Reader reader = new FileReader("F:/a.txt", StandardCharsets.UTF_8);
            //2. 读取文件
            char[] cbuf = new char[1024];
            int len;
            while((len = reader.read(cbuf)) != -1) {
                //3.处理字符
                String str = new String(cbuf, 0, len);
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } //4.自动资源释放
    }
}

在上面的案例中,read()方法可以一次读取一个字符数组,并返回实际读入的字符个数,最大不会超过char[]数组的长度,如果读到流的末尾就返回-1。所以利用这个方法,我们可以先设置一个缓冲区,然后每次尽可能地填充缓冲区。

大家可以发现,其实Reader字符流的使用,与InputStream字节流的使用基本类似。

另外我们在构造FileReader对象时,默认的字符编码及字节缓冲区大小都是由系统设定好的,如果我们想自己指定这些值,可以在FilelnputStream上套接一个InputStreamReader。在上面的代码中,如果我们读取的是纯ASCII编码的文本文件,输出的内容不会产生乱码。但如果文件中包含中文,就会出现乱码,这就是因为FileReader采用的是操作系统默认的编码,在Windows中默认的是GBK,而打开UTF-8编码的文本文件可能就会出现乱码。所以为了避免读取中文时产生乱码,我们可以在创建FileReader时指定编码:

Reader reader = new FileReader("a.txt", StandardCharsets.UTF_8);

二. Writer字符输出流

1. 简介

Writer是I/O库中用于写入字符流的抽象类,它提供了一组方法用于将字符写入到字符流中,并支持不同的字符编码。除了CharArrayWriter和StringWriter之外,普通的Writer都是基于OutputStream构造的。所以本质上Writer是一个带有编码转换器的OutputStream,可以把char转换为byte并输出,即接收char,然后在内部会自动转换成一个或多个byte,并写入到OutputStream中。比如我们可以利用OutputStreamWriter,将任意的OutputStream转换为Writer,所以OutputStreamWriter是一种用于将字节输出流转换为字符输出流的转换流

//创建FileOutputStream对象
FileOutputStream fos=new FileOutputStream("readme.txt");
//将FileOutputStream转为Writer
Writer writer = new OutputStreamWriter(fos, "UTF-8")

同样的,Writer和OutputStream也具有一些区别:

  • OutputStream是一个字节流,即以byte为单位读取,读取的字节范围是0~255;
  • Writer是一个字符流,要以char为单位读取,读取的字符范围是0~65535。

2. 常用子类

Writer是所有字符输出流的父类,常用的Writer子类有如下这些:

  • OutputStreamReader类:将字节输出流转换为字符输出流,可以指定字符编码;
  • BufferedWriter类:为其他字符输出流提供写缓冲区;
  • CharArrayWriter类:向内存缓冲区的字符数组写数据;
  • StringWriter类:向内存缓冲区的字符串(StringBuffer)写入数据;
  • FileReader类:用于写入文本文件,并支持不同的字符编码;
  • PipedWriter类:连接到一个PipedReader对象。

3. 常用方法

同样的,我们也来看看Writer类中的常用方法:

方法名及返回值类型说明
void write(int c)向输出流中写入一个字符。
void write(char[] cbuf)把cbuf参数指定的字符数组中的所有字符,写到输出流中。
void write(char[] cbuf,int off,int len)把cbuf参数指定的字符数组中的若干字符写到输出流中。off 表示字符数组的起始下标,len表示元素的个数。
void write(String str)向输出流中写入一个字符串。
void write(String str, int off,int len)向输出流中写入一个字符串的部分字符。off表示字符串中的起始偏移量,len表示字符个数。
append(char c)将参数c指定的字符添加到输出流中。
append(charSequence esq)将参数esq指定的字符序列添加到输出流中。
append(charSequence esq,int start,int end)将参数esq指定的字符序列的子序列添加到输出流中。start表示子序列中的第一个字符索引,end表示子序列中最后一个字符后面的字符索引。即子序列的内容包含start索引处的字符,但不包括end索引处的字符。

大家要注意:Writer类中所有的方法在出错时都会引发IOException异常,如果我们关闭一个流后再对其进行任何操作,都会产生错误。

4. 代码案例

我们设计一个FileWriter的使用案例,代码如下:

import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
public class Demo09 {
	public static void main(String[] args) throws FileNotFoundException, IOException {
		// try(resource)的写法
		// 1. 创建Writer对象
		try (FileWriter writer = new FileWriter("F:/b.txt")) {
			// 利用Scanner进行内容的输入
			Scanner input = new Scanner(System.in);
			// 2. 写入文件
			for (int i = 0; i < 5; i++) {
				System.out.println("请输入第" + (i + 1) + "行内容:");
				// 读取输入的名称
				String content = input.next(); 
				// 循环写入到文件中
				writer.write(content + "\r\n"); 
                //writer.append(“追加新内容...”);
			}
			System.out.println("录入完毕");
			input.close();
		} catch (IOException e) {
			e.printStackTrace();
		} // 3.自动资源释放
	}
}

FileWriter是向文件中写入字符流的Writer,它的使用和FileReader类似。这里的FileWriter对象直接关联了一个文件,然后我们可以调用write()或append()方法进行内容的新增和追加了。我们在创建FileWriter对象时,默认的字符编码和字节缓冲区的大小都是由系统设定的。如果我们想要自己指定这些值,可以在FileOutputStream上套接一个OutputStreamWriter对象。

在创建FileWriter类对象时,如果关联的文件不存在,则会自动生成一个新的文件。在创建文件之前,FileWriter会在创建对象时打开该文件作为输出目的地,但如果试图打开的是一个只读文件,会引发IOException异常。

三. 字符缓冲流

1. 简介

与字节缓冲流类似,我们在进行大文件读写操作时,也可以使用字符缓冲流来减少访问磁盘的次数,提高IO访问效率。Java中的字符缓冲流包括BufferedReader和BufferedWriter两大类,分别负责文件的读取和写入。

2. 常用子类

Java中的字符缓冲流可以分为缓冲的字符输入流BufferedReader和缓冲的字符输出流BufferedWriter。

  • BufferedReader:继承自InputStreamReader 类, 用于读取二进制数据,并将数据存储在内部缓冲区中;
  • BufferedWriter:继承自OutputStreamWriter类,用于写入二进制数据,并将数据存储在内部缓冲区中。

3. BufferedReader的用法

BufferedReader是一个带有缓冲区的输入流,主要用于辅助其他的字符输入流。BufferedReader可以先将一批数据读到内存缓冲区,然后接下来的读操作就可以直接从该缓冲区中获取数据,并进行字符编码转换,这样就可以提高数据的读取效率。

另外BufferedReader还提供了一个readLine()方法,该方法会返回包含所读内容的字符串,但该字符串中不包含任何终止符,如果已到达了流的末尾,就返回null。readLine()方法表示每次读取一行的文本内容,当遇到换行(\n)、回车(\r)或回车后直接跟着换行标记符,即认为某行终止。接下来我们就来看看BufferedReader是怎么使用的吧。

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Demo11 {
	public static void main(String[] args) throws FileNotFoundException, IOException {
		// try(resource)的写法
		// 1. 创建Reader对象
		try(BufferedReader reader=new BufferedReader(new FileReader("F:\a.txt"))) {
            String strLine = "";
            while ((strLine = reader.readLine()) != null) { 
                //循环读取每行数据
                System.out.println(strLine);
            }
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }  // 3.自动资源释放
	}
}

4. BufferedWriter的用法

BufferedWriter则是一个带有缓冲区的输出流,主要用于辅助其他的字符输出流。BufferedWriter同样带有缓冲区,可以先将一批数据写入到缓冲区,当缓冲区满了以后,再将缓冲区里的数据一次性写到字符输出流,这样也提高了数据的写入效率。

BufferedWriter类中提供了一个新的方法newLine(),该方法用于写入一个行分隔符。行分隔符字符串由系统属性 line.separator定义,且不一定是单个新行(\n)符。BufferedWriter类的用法如下:

import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
public class Demo12 {
	public static void main(String[] args) throws FileNotFoundException, IOException {
		// try(resource)的写法
		// 1. 创建Writer对象
		try (BufferedWriter writer = new BufferedWriter(new FileWriter("F:/c.txt"))) {
			//2.写入内容
			writer.write("Hello, world!");
			//3.换行
			writer.newLine();
			writer.write("Welcome to learn Java!");
			// writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

在上面的代码中,我们使用了BufferedWriter类来写入字符流,使用newLine()方法添加一个新行,在写入完成后可以关闭写入器对象。

5. 综合案例

在学习了上面的字符输入流、字符输出流、转换流以及缓冲流等内容之后,最后给大家设计一个综合案例,来把这几个知识点糅合在一起。

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
public class Demo13 {
	public static void main(String[] args) throws FileNotFoundException, IOException {
		try {
	        //将System.in对象转换成InputStreamReader对象
	        InputStreamReader reader = new InputStreamReader(System.in);
	        // 将Reader对象包装成BufferedReader
	        BufferedReader br = new BufferedReader(reader);
	        String line = null;
	        //利用循环方式逐行读取
	        while ((line = br.readLine()) != null) {
	            // 如果读取到“exit”,则程序退出
	            if (line.equals("exit")) {
                    //退出程序
	                System.exit(1);
	            }
	            // 打印出读取到的内容
	            System.out.println("输入的内容为:" + line);
	        }
	    } catch (IOException e) {
	        e.printStackTrace();
	    }
	}
}

在上面的案例中,我们使用了System.in获取到键盘的输入信息,该信息其实是InputStream的实例。但该实例使用不太方便,我们使用 InputStreamReader 将其转换成字符输入流,因为Reader读取输入内容时依然不太方便,我们继续把Reader包装成BufferedReader。因为BufferReader具有readLine()方法,可以非常方便地一次读入一行内容。而且BufferReader具有缓冲功能,一次读取一行文本,以换行符为标志,如果它没有读到换行符,则程序堵塞,等到读到换行符为止。运行该程序后,在控制台执行输入时,只要按下回车键,程序就会打印出刚刚输入的内容。

四. 结语

到此这篇关于详解Java I/O流中的字符流有哪些的文章就介绍到这了,更多相关Java字符流内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文