code学习

java io Reader 详解

作者:程序员COW哥

概述

Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流,和InputStream的唯一的区别就在于读的数据单位不同。

16位:一个字符也就是两个字节,使用Reader流读取数据时都是两个字节两个字节往外读的,为什么还要有这两种两个字节的读取方式呢? 因为有些字符是占2个字节的,如我们的中文字符在Java里面就是占两个字节的。如果采用一个字节一个字节往外读的方式,那么读出来的就是半个汉字,这样子Java就没有办法正确的显示中文字符的,所以有必要存在这种流,一个字符一个字符地往外读。

构造方法:

protected Reader() 创建一个字符流reader,其关键部分将在读取器本身上同步

protected Reader(Object lock) 创建一个字符流reader,其临界区将同步给定对象。

常用方法:

void close() 关闭该文件并释放与之关联的所有系统资源

void mark(int readAheadLimit) 标记文件流的当前位置。

boolean markSupported() 是否支持mark()操作。

int read() 读取单个字符。

int read(char[] b, int off, int len) 读取字符到数组的一部分。

boolean ready() 是否已准备好被读。

void reset() 重置流至最新的标记,如果没有被标记从头开始。

整个框架结构图如下:

java io Reader 详解

框架结构图

BufferedReader子类

BufferedReader由Reader类扩展而来,提供通用的缓冲方式文本读取,而且提供了很实用的readLine,读取一个文本行,从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。

构造方法:

BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。

BufferedReader(Reader in, int sz) 创建一个使用指定大小输入缓冲区的缓冲字符输入流。

常用方法:

void close() 关闭该流。

void mark(int readAheadLimit) 标记流中的当前位置。

boolean markSupported() 判断此流是否支持 mark() 操作(它一定支持)。

int read() 读取单个字符。

int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。

String readLine() 读取一个文本行。

boolean ready() 判断此流是否已准备好被读取。

void reset() 将流重置为最新的标记。

long skip(long n) 跳过字符。

例子 读取键盘的输入并输出

public class BufferedReaderTest {

public static void main(String[] args) {

BufferedReader br=null;

br=new BufferedReader(new InputStreamReader(System.in ));

String s=null;

try {

s=br.readLine();

} catch (IOException e) {

e.printStackTrace();

}

System.out.println(s);

}

}

CharArrayReader子类

CharArrayReader 是字符数组输入流。它和ByteArrayInputStream类似,只不过ByteArrayInputStream是字节数组输入流,而CharArray是字符数组输入流。CharArrayReader 是用于读取字符数组,它继承于Reader。操作的数据是以字符为单位。

构造函数:

CharArrayReader(char buf[]) 指定字符缓冲区,初始化下一次读取位置pos和有效字符个数count。

CharArrayReader(char buf[], int offset, int length) 基于方法指定的字符数组buf,把数组起点offset作为读取的起点pos,数组长度-offset和length的较小值。

常用方法:

public int read() throws IOException 读取一个字符。

public int read(char b[], int off, int len) throws IOException 将CharReader保存在字符缓冲区中的字符数据读取到方法指定的字符数组,数组填充起点为offset,填入的字符个数由len和b.length-offset的最小值决定。

public long skip(long n) throws IOException 跳过指定个数字符,如果方法指定跳过字符数参数n小于0则不做任何处理。

public boolean ready() throws IOException 判断当前字符流是否已经准备好被读取,主要是基于缓冲数组buf和当前读取位置pos和可读字节数count判断。

public boolean markSupported() 判断CharReader是否支持mark()操作,返回true支持。

void mark(int readAheadLimit) throws IOException 标记当前字符输入流的读取位置,后续调用reset方法会将流重新定位到该位置进行读取,方法指定参数readAheadLimit本。

意是指定标记之后继续读取多少字符,流仍然保留该标记的字符读取限制,但因为CharReader数据源即内部缓冲区会保留所有字符数据,所以该参数可以忽略。

public void reset() throws IOException 重置读取位置到上一次标记位置或者如果之前未曾标记则重置到流开头位置读取。

public void close() 关闭流,实际就是把内部缓冲区数组置空。

例子 方法演示:

public class CharArrayReaderTest {

private static final int LEN = 5;

// 对应英文字母“abcdefghijklmnopqrstuvwxyz”

private static final char[] ArrayLetters = new char[] {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};

public static void main(String[] args) {

tesCharArrayReader() ;

}

/**

* CharArrayReader的API测试函数

*/

private static void tesCharArrayReader() {

try {

// 创建CharArrayReader字符流,内容是ArrayLetters数组

CharArrayReader car = new CharArrayReader(ArrayLetters);

// 从字符数组中读取5个字符

for (int i=; i<LEN; i++) {

// 若能继续读取下一个字符,则读取下一个字符

if (car.ready() == true) {

// 读取“字符流的下一个字符”

char tmp = (char)car.read();

System.out.printf("%d : %c\n", i, tmp);

}

}

// 若“该字符流”不支持标记功能,则直接退出

if (!car.markSupported()) {

System.out.println("make not supported!");

return ;

}

// 标记“字符流中下一个被读取的位置”。即--标记“f”,因为因为前面已经读取了5个字符,所以下一个被读取的位置是第6个字符”

// (01), CharArrayReader类的mark(0)函数中的“参数0”是没有实际意义的。

// (02), mark()与reset()是配套的,reset()会将“字符流中下一个被读取的位置”重置为“mark()中所保存的位置”

car.mark(0);

// 跳过5个字符。跳过5个字符后,字符流中下一个被读取的值应该是“k”。

car.skip(5);

// 从字符流中读取个数据。即读取“klmno”

char[] buf = new char[LEN];

car.read(buf, 0, LEN);

System.out.printf("buf=%s\n", String.valueOf(buf));

// 重置“字符流”:即,将“字符流中下一个被读取的位置”重置到“mark()所标记的位置”,即f。

car.reset();

// 从“重置后的字符流”中读取5个字符到buf中。即读取“fghij”

car.read(buf, 0, LEN);

System.out.printf("buf=%s\n", String.valueOf(buf));

} catch (IOException e) {

e.printStackTrace();

}

}

}

FilterReader 子类

FilterReader是实现自定义过滤输入字符流的基类,本事是一个抽象类、为所有装饰类提供一个标准、只是简单重写了父类Reader的所有方法、要求子类必须重写核心方法、和提供具有自己特色的方法、这里没有像字节流那样有很多的子类来实现不同的功能、可能是因为字符流本来就是字节流的一种装饰、所以在这里没有必要再对其进行装饰、只是提供一个扩展的接口而已。

构造函数:

protected FilterReader(Reader in) 使用传入的底层字符输入流创建FilterReader

常用方法:

void close(); 关闭此流。

boolean markSupport() 检测是否支持mark。

void mark() 标记此流。

void reset() 重置最后一次mark的位置。

int read() 读取一个字符。

int read(char[] b, int off, int len) 将字符读取到字符数组b中。

boolean ready(); 检测此流是否可以读取。

long skip(long n) 跳过底层输入流中的n个字符。

例子用法:

public class FilterReaderTest {

public static void main(String[] args) throws Exception {

Reader r_stm = null;

FilterReader fr_stm = null;

try {

r_stm = new StringReader("Java World!!!!");

fr_stm = new FilterReader(r_stm) {};

for (int val = 0; val <= 6; ++val) {

char ch = (char) fr_stm.read();

System.out.println("ch:" + ch + " ");

long skip = fr_stm.skip(1);

System.out.println("fr_stm.skip(1):" + skip);

}

} catch (Exception ex) {

System.out.println(ex.toString());

} finally {

if (fr_stm != null) {

fr_stm.close();

}

}

}

}

InputStreamReader 子类

  • InputStreamReader继承自Reader,是字节流到字符流的桥接器。
  • 读取字节流通过指定的字符集解码为字符流,可以通过名称指定字符集,也可以通过显示指定,或者不指定则为平台默认的字符集。
  • 每次调用InputStreamReader的read()方法,都会从底层字节流读取一个或多个字节。
  • 为了能够有效地将字节转换为字符,可以预先从底层读比满足当前读取操作所需的字节更多的字节;为了提高效率,可以把一个InputStreamReader包装在一个BufferedReader。

构造函数:

public InputStreamReader(InputStream in) 用默认字符集创建一个InputStreamReader,参数为一个InputStream输入流。

public InputStreamReader(InputStream in, String charsetName) 用名称指定字符集创建一个InputStreamReader,参数为一个输入流和字符集名称。

public InputStreamReader(InputStream in, Charset cs) 用给定的字符集创建一个InputStreamReader,参数为输入流和字符集对象。

public InputStreamReader(InputStream in, CharsetDecoder dec) 用字符集解码器创建一个InputStreamReader,参数为输入流和字符集解码器。

常用方法:

public String getEncoding() ; 返回这个流所采用的编码名称。

public int read() throws IOException 读取一个字符。

public int read(char cbuf[], int offset, int length) throws IOException 读取最多length个字符到cbuf中,从offset开始存储,返回读取的字符数。

public boolean ready() throws IOException 返回流是否准备好被读取,准备好是当他输入buffer不为空,或者字节流中字节是可以读取的。

public void close() throws IOException 关闭InputStreamReader。

例子 从文件读取输出:

public class InputStreamReaderTest {

public static void main(String[] args) throws IOException {

InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\gbk.txt"),"GBK");

int len = 0;

while((len = isr.read())!=-1){

System.out.println((char)len);

}

isr.close();

}

}

PipedReader子类

PipedReader是字符管道输入流,它继承于Writer,用于读取对应绑定的管道字符输出流写入其内置字符缓存数组buffer中的字符、借此来实现线程之间的通信PipedReader中有两个方法供PipedWriter调用、receive(char c)、receive(char[] b, int off, intlen)使得PipedWriter可以将字符或者字符数组写入PipedReader的buffer中。

构造函数:

PipedReader(PipedWriter src) 使用默认的buf的大小和传入的PipedWriter构造PipedReader

PipedReader(PipedWriter src, int pipeSize) 使用指定的buf的大小和传入的pw构造PipedReader。

PipedReader() 使用默认大小构造PipedReader。

PipedReader(int pipeSize) 使用指定大小构造PipedReader。

常用方法:

void close() 清空buf中数据、关闭此流。

void connect(PipedWriter src) 调用与此流绑定的PipedWriter的connect方法、将此流与对应的PipedWriter绑定。

synchronized boolean ready() 查看是否可读。

synchronized int read() 从buf中读取一个字符、以整数形式返回。

synchronized int read(char cbuf[], int off, int len) 将buf中读取一部分字符到cbuf。

synchronized void receive(int c) PipedWriter调用此流的此方法、向PipedReader的buf以整数形式中写入一个字符。

synchronized void receive(char c[], int off, int len) 将c中一部分字符写入到buf中。

synchronized void receivedLast() 提醒所有等待的线程、已经接收到了最后一个字符。

例子 消息发送接收:

PipedSender(发送者对象,PipedWriter)

public class PipedSender implements Runnable{

private static char[] chs = new char[]

{'a','b','c','d','e','f','g','h','i','j','k','l','m',

'n','o','p','q','r','s','t','u','v','w','x','y','z'};

PipedWriter wr = new PipedWriter();

public PipedWriter getPipedWriter() {

return wr;

}

public void run() {

sendOne(); // 写入较短数据

sendMove();// 写入较长数据

}

/**

* 写入较短数据

*/

private void sendOne() {

try {

wr.write("this is a PipedSender");

}catch(Exception e) {

e.printStackTrace();

}finally {

try {

if (wr != null)

wr.close();

}catch(Exception e) {

e.printStackTrace();

}

}

}

/**

* 写入较长数据

*/

private void sendMove() {

try {

StringBuffer sb = new StringBuffer();

for (int i = 0; i < 100; i++) {

sb.append("0123456789");

}

sb.append(new String(chs));

String str = sb.toString();

wr.write(chs);

wr.write(str);

}catch(Exception e) {

e.printStackTrace();

}finally {

try {

if (wr != null)

wr.close();

}catch(Exception e) {

e.printStackTrace();

}

}

}

}

PipedReceiver(接收者对象,PipedReader)

public class PipedReceiver implements Runnable {

PipedReader re = new PipedReader();

public PipedReader getPipedReader() {

return re;

}

@Override

public void run() {

readOne(); // 读取一次

readMove(); // 全部读取

}

/**

* 读取一次数据

*/

private void readOne() {

char[] buff = new char[2048];

int len = 0;

try {

len = re.read(buff);

System.out.println("readOne : " + new String(buff,0,len));

} catch (IOException e) {

e.printStackTrace();

}finally {

try {

if (re != null)

re.close();

}catch(Exception e) {

e.printStackTrace();

}

}

}

/**

* 全部读取

*/

private void readMove() {

char[] buff = new char[1024];

int len = 0;

try {

while(true) {

len = re.read(buff);

if(len == -1) break;

System.out.println("readMove : " + new String(buff,0,len));

}

} catch (IOException e) {

e.printStackTrace();

}finally {

try {

if (re != null)

re.close();

}catch(Exception e) {

e.printStackTrace();

}

}

}

}

PipedDemo(主线程main类)

public class PipedDemo {

public static void main(String[] args) {

PipedSender se = new PipedSender();

PipedReceiver re = new PipedReceiver();

PipedWriter out = se.getPipedWriter();

PipedReader in = re.getPipedReader();

try {

in.connect(out);// 将输入流与输出流建立连接

// 开启线程

new Thread(se).start();

new Thread(re).start();

}catch(Exception e) {

e.printStackTrace();

}

}

}