概述
当使用字节流读取文本文件时,读出来的数据是一个个字节,不方便查看和操作,并且一个字符可能会由多个字节组成。例如,中文字符。JavaAPI中提供了字符流,可以用字符的形式,读写数据,专门用于处理文本数据。
java.io.Reader是所有字符输入流的抽象父类型
java.io.Writer是所有字符输出流的抽象父类型。
Reader中最核心的三个read方法
//每次读取一个字符,返回这个字符的编码值
public int read() throws IOException {
char cb[] = new char[1];
if (read(cb, 0, 1) == -1)
return -1;
else
return cb[0];
}
//每次读取多个字符,并存放到指定字符数组中,返回值是本次读取到的字符个数
public int read(char cbuf[]) throws IOException {
return read(cbuf, 0, cbuf.length);
}
//每次读取多个字符,并存放到指定字符数组中,返回值是本次读取到的字符个数
//同时可以指定从数组的什么位置开始存放,以及在数组中最多存放多个字符
abstract public int read(char cbuf[], int off, int len) throws IOException;
Wireter中最常用的write方法
//写出去一个字符,注意字符可以使用int值来表示
public void write(int c) throws IOException {
synchronized (lock) {
if (writeBuffer == null){
writeBuffer = new char[WRITE_BUFFER_SIZE];
}
writeBuffer[0] = (char) c;
write(writeBuffer, 0, 1);
}
}
//写出去数组中的多个字符
public void write(char cbuf[]) throws IOException {
write(cbuf, 0, cbuf.length);
}
//写出去数组中的多个字符
//可以指定从数组的什么位置开始写,以及多少个字符
abstract public void write(char cbuf[], int off, int len) throws IOException;
//写出去一个字符串
public void write(String str) throws IOException {
write(str, 0, str.length());
}
//写出去一个字符串
//可以指定从字符串的什么位置开始写,以及多少个字符
public void write(String str, int off, int len) throws IOException {
synchronized (lock) {
char cbuf[];
if (len <= WRITE_BUFFER_SIZE) {
if (writeBuffer == null) {
writeBuffer = new char[WRITE_BUFFER_SIZE];
}
cbuf = writeBuffer;
} else {
cbuf = new char[len];
}
str.getChars(off, (off + len), cbuf, 0);
write(cbuf, 0, len);
}
}
可以看到字符流中的读和写的方法,与字节流中的读和写方法类似
字符数组
使用字符流,从字符数组中读取数据,以及向字符数组中写数据。
java.io.CharArrayReader 负责从字符数组中读取数据。
java.io.CharArrayWriter 负责把数据写入到字符数组中。
和字节数组一样使用
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Arrays;
public class Test01 {
public static void main(String[] args) {
//1、声明流
Reader in = null;
Writer out = null;
char[] arr = "hello 中国".toCharArray();
//2、创建流
in = new CharArrayReader(arr);
out = new CharArrayWriter();
//3、使用流
int len = -1;
char[] cbuf = new char[1024];
try {
len = in.read(cbuf);
out.write(cbuf,0,len);
out.flush();
//CharArrayWriter中的toCharArray方法,可以将写入到out对象中的数据返回
char[] charArray = ((CharArrayWriter)out).toCharArray();
//使用Arrays工具类中的toString方法接收数组并输出
System.out.println(Arrays.toString(charArray));
System.out.println();
//普通for循环输出也可以
for(int i = 0;i<charArray.length; i++) {
System.out.print(charArray[i] + " ");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
//4、关闭流
if(in!=null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
管道字符流
使用字符流,可以从管道中读取数据,以及向管道中写数据。
java.io.PipedReader负责从管道中读取数据
java.io.PipedWriter负责将数据写入到管道中
使用起来好管道字节流一样,一般可以在一个线程中,使用管道输出流,将数据写入到管道中,在另一个线程中,读取管道中的数据。
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
public class Test02 {
public static void main(String[] args) {
PipedReader in = null;
PipedWriter out = null;
in = new PipedReader();
out = new PipedWriter();
try {
//管道对接
in.connect(out);
Thread t1 = new writerThread(out);
Thread t2 = new readerThread(in);
//启动线程
t1.start();
t2.start();
t1.join();
t2.join();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
//将数据写入到管道中
class writerThread extends Thread{
private Writer out;
//构造器:用于创建线程对象后进行创参
public writerThread(Writer out) {
this.out = out;
}
@Override
public void run() {
//将字符转换为字节存放到字节数组中
char[] arr = "hello world java".toCharArray(); //这个是要输入的内容
try {
for(int i = 0; i<arr.length; i++) {
out.write(arr[i]);
out.flush();
Thread.sleep(1000); //让线程每次写一个字符,休息1秒
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
} finally {
if(out != null) {
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
//从管道中读取数据并且输出
class readerThread extends Thread{
private Reader in;
public readerThread(Reader in) {
this.in = in;
}
@Override
public void run() {
int len = -1;
try {
while((len = in.read())!=-1) {
//第一种输出方式:输出字节
// System.out.print(len);
//第二种输出方式:输出字符
System.out.write(len);
//别忘了刷新
System.out.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(in!= null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
结果嘛,就是每隔一秒输出一个字符
文件字符流
使用字符流,可以从文件中读取数据,以及向文件中写数据。
java.io.FileReader,负责从文件中读取数据
java.io.FileWriter,负责把数据写入到文件中
这俩个其实是JavaAPI中封装好的便捷类,里面使用了文件字节流来实现的,方便我们操作字符文件,操作起来和文件字节流相似
代码演示:
a.txt文件的路径如下:src/Test/test2/files/a.txt,内容为蓝瘦香菇 爱了爱了
将a.txt中的内容输出到同级文件b.txt中
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
public class Test03 {
public static void main(String[] args) {
Reader in = null;
Writer out = null;
try {
String fileName = "src/Test/test2/files/a.txt";
String fileNameCopy = "src/Test/test2/files/b.txt";
in = new FileReader(fileName);
out = new FileWriter(fileNameCopy);
int len = -1;
char[] cbuf = new char[1024];
while((len = in.read(cbuf))!=-1) {
out.write(cbuf,0,len);
out.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out!=null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
结果如下:
其他
与非节点流搭配使用了,具体看非节点流章节