您的位置:首页 > Web前端

Java 日看一类(4)之IO包中的BufferedReader类

2018-02-13 14:32 302 查看
所引入的包和继承的类:import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;public class BufferedReader extends Reader后面讲到会贴链接

类的使用注释:/**
* Reads text from a character-input stream, buffering characters so as to
* provide for the efficient reading of characters, arrays, and lines.
*
* <p> The buffer size may be specified, or the default size may be used. The
* default is large enough for most purposes.
*
* <p> In general, each read request made of a Reader causes a corresponding
* read request to be made of the underlying character or byte stream. It is
* therefore advisable to wrap a BufferedReader around any Reader whose read()
* operations may be costly, such as FileReaders and InputStreamReaders. For
* example,
*
* <pre>
* BufferedReader in
* = new BufferedReader(new FileReader("foo.in"));
* </pre>
*
* will buffer the input from the specified file. Without buffering, each
* invocation of read() or readLine() could cause bytes to be read from the
* file, converted into characters, and then returned, which can be very
* inefficient.
*
* <p> Programs that use DataInputStreams for textual input can be localized by
* replacing each DataInputStream with an appropriate BufferedReader.
*
* @see FileReader
* @see InputStreamReader
* @see java.nio.file.Files#newBufferedReader
*
* @author Mark Reinhold
* @since JDK1.1
*/
大意如下:
从一个输入的字符流中读取文本,为字符、数组、一行文本的高效读取提供字符缓冲功能。
缓冲区的大小可能是特殊设定值,可能是使用默认的大小。默认的大小已经足够解决大部分问题。
一般情况下,每一个read请求由一个Reader类发起,使底层进行对字节流或byte流执行相应的读取请求。
对一些read()函数开销较大的的Reader类,(例如:FileReaders和InputStreamReaders)使用BufferedReader类进行封装是很明智的。
引例:
BufferedReader in = new BufferedReader(new FileReader("foo.in"));如果没由进行缓冲,对read()和readline()调用会直接从文件中读取一次byte数据,转换成字符格式并返回,这是十分不明智的做法。
程序对文本数据使用DataInputStream时,在合适的情况下可以局部替换DataInputStream为BufferedReader

 BufferedReader类含有十一个成员变量:
内部包含的Reader对象:private Reader in;缓冲区数组:private char cb[];缓冲区数组的标识符:private int nChars, nextChar;无效数据标识:private static final int INVALIDATED = -2;“未标记”标示:private static final int UNMARKED = -1;被标记字符的位置:private int markedChar = UNMARKED;预读取大小限制(每次更新缓冲需保存的字符数量限制):private int readAheadLimit = 0;
/* Valid only when markedChar > 0 */
是否跳过下个换行符:private boolean skipLF = false;是否跳过所有的换行符:private boolean markedSkipLF = false;默认缓冲区大小:private static int defaultCharBufferSize = 8192;默认预计行长度:private static int defaultExpectedLineLength = 80;

含有十五个方法:
构造函数(自定义缓冲区大小):(传入一个Reader类和int值)public BufferedReader(Reader in, int sz) {
super(in);
if (sz <= 0)

18810
throw new IllegalArgumentException("Buffer size <= 0");
this.in = in;
cb = new char[sz];
nextChar = nChars = 0;
}构造函数(默认缓冲区大小):public BufferedReader(Reader in) {
this(in, defaultCharBufferSize);
}判定流的有效性:private void ensureOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
}缓冲区装填:private void fill() throws IOException {
int dst;//更新缓冲区开始位置的标识
if (markedChar <= UNMARKED) {//判定是否存在标记
/* No mark */
dst = 0;//不存在标记,将标识移至缓冲区头部
} else {
/* Marked */
int delta = nextChar - markedChar;//下个未读取字符到上一个标记位置的距离
if (delta >= readAheadLimit) {//需要保存的字符量预读取限制
/* Gone past read-ahead limit: Invalidate mark */
markedChar = INVALIDATED;//无效标记
readAheadLimit = 0;//限制重置
dst = 0;//指向缓冲头
} else {
if (readAheadLimit <= cb.length) {//预读取限制小于缓冲区大小
/* Shuffle in the current buffer */
System.arraycopy(cb, markedChar, cb, 0, delta);//将标记至当前读取位置的数据挪至缓冲区头部
markedChar = 0;//标记位置为0
dst = delta;//更新位置为下个要读取字节
} else {
/* Reallocate buffer to accommodate read-ahead limit */
char ncb[] = new char[readAheadLimit];//创建新缓冲区,大小为预读取限制大小
System.arraycopy(cb, markedChar, ncb, 0, delta);//将原缓冲区中的数据转移
cb = ncb;
markedChar = 0;
dst = delta;
}
nextChar = nChars = delta;//修改读取指针的指向和有效字符数
}
}

int n;
do {
n = in.read(cb, dst, cb.length - dst);//填满缓冲区
} while (n == 0);//当实际读取字节数为0时继续读取
if (n > 0) {
nChars = dst + n;//读取成功修改有效字节数
nextChar = dst;//修改读取指针指向
}
}从缓冲区中读取数据:public int read() throws IOException {
synchronized (lock) {//线程枷锁
ensureOpen();
for (;;) {
if (nextChar >= nChars) {//读取指针超出有效字符数
fill();//更新缓冲区
if (nextChar >= nChars)//仍超出有效字符数
return -1;//程序报错返回
}
if (skipLF) {//设定为跳过下个换行符
skipLF = false;//标记重置
if (cb[nextChar] == '\n') {//下个字符为换行符
nextChar++;
continue;//直接跳过并继续循环
}
}
return cb[nextChar++];//返回读取值
}
}
}缓冲区大批量读取(内部私有):private int read1(char[] cbuf, int off, int len) throws IOException {
if (nextChar >= nChars) {//读取指针超出目前有效字符数
/* If the requested length is at least as large as the buffer, and
if there is no mark/reset activity, and if line feeds are not
being skipped, do not bother to copy the characters into the
local buffer. In this way buffered streams will cascade
harmlessly. */
if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {//需读取长度大于缓冲区大小且不存在标记且不需要跳过下个换行符
return in.read(cbuf, off, len);//直接从Reader中读取,不经过缓冲区
}
fill();
}
if (nextChar >= nChars) return -1;//刷新后仍超出则程序报错
if (skipLF) {
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;
if (nextChar >= nChars)
fill();
if (nextChar >= nChars)
return -1;
}
}
int n = Math.min(len, nChars - nextChar);//取需读取长度和缓冲区剩余有效长度的较小值
System.arraycopy(cb, nextChar, cbuf, off, n);
nextChar += n;//更新读取指针
return n;//返回有效读取长度
}缓冲区批量读取(公有):public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {// 线程枷锁
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {//读取指针小于0或需读取长度大于传入数组长度或需读取长度小于0或读取指针预需读取长度相加大于数组长度或相加小于0
throw new IndexOutOfBoundsException();//数组越界
} else if (len == 0) {//读取长度为0
return 0;
}

int n = read1(cbuf, off, len);//调用上个函数,得到实际读取长度
if (n <= 0) return n;//实际读取长度小于等于0,直接返回(返回0或报错)
while ((n < len) && in.ready()) {//当n小于需读取长度且Reader流准备好时
int n1 = read1(cbuf, off + n, len - n);//直接从Reader流中读取剩余所需的数据
if (n1 <= 0) break;
n += n1;
}
return n;
}
}行文本读取(当行文本过大时会创建一个字符串缓冲区单独储存):String readLine(boolean ignoreLF) throws IOException {
StringBuffer s = null;//开个字符串缓冲区
int startChar;//字符指针

synchronized (lock) {
ensureOpen();
boolean omitLF = ignoreLF || skipLF;//确认这一行读取是否需要读取换行符

bufferLoop:
for (;;) {

if (nextChar >= nChars)
fill();
if (nextChar >= nChars) { /* EOF */
if (s != null && s.length() > 0)//字符串缓冲区不为空且长度大于0
return s.toString();//返回字符串缓冲区中的字符串
else
return null;
}
boolean eol = false;//句尾标识符
char c = 0;
int i;

/* Skip a leftover '\n', if necessary */
if (omitLF && (cb[nextChar] == '\n'))
nextChar++;
skipLF = false;
omitLF = false;

charLoop:
for (i = nextChar; i < nChars; i++) {//遍历缓冲区中剩余数据
c = cb[i];
if ((c == '\n') || (c == '\r')) {//数据为换行或回车
eol = true;
break charLoop;//跳出字符循环
}
}

startChar = nextChar;
nextChar = i;

if (eol) {//句子结束
String str;
if (s == null) {//字符串缓冲区为空则直接从缓冲区中读取
str = new String(cb, startChar, i - startChar);
} else {
s.append(cb, startChar, i - startChar); //字符串缓冲区从缓冲区中读取数据
str = s.toString();
}
nextChar++;
if (c == '\r') {//若charloop因回车中断,跳过下一个换行符
skipLF = true;
}
return str;
}

if (s == null)
s = new StringBuffer(defaultExpectedLineLength);//创建默认大小缓冲区
s.append(cb, startChar, i - startChar);//读取数据进缓冲区
}
}
}默认不跳过换行读取:public String readLine() throws IOException {
return readLine(false);
}跳过后续的字符(在缓冲区中操作):public long skip(long n) throws IOException {
if (n < 0L) {
throw new IllegalArgumentException("skip value is negative");
}
synchronized (lock) {
ensureOpen();
long r = n;
while (r > 0) {
if (nextChar >= nChars)
fill();
if (nextChar >= nChars) /* EOF */
break;
if (skipLF) {
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;
}
}
long d = nChars - nextChar;//缓冲区剩余有效长度
if (r <= d) {
nextChar += r;
r = 0;
break;
}
else {
r -= d;
nextChar = nChars;
}
}
return n - r;//返回实际跳过长度
}
}检测当前状态是否能读取:public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();

/*
* If newline needs to be skipped and the next char to be read
* is a newline character, then just skip it right away.
*/
if (skipLF) {
/* Note that in.ready() will return true if and only if the next
* read on the stream will not block.
*/
if (nextChar >= nChars && in.ready()) {
fill();
}
if (nextChar < nChars) {
if (cb[nextChar] == '\n')
nextChar++;
skipLF = false;
}
}
return (nextChar < nChars) || in.ready();
}
}是否支持标记操作:public boolean markSupported() {
return true;
}设置标记:public void mark(int readAheadLimit) throws IOException {
if (readAheadLimit < 0) {//最大预读小于0
throw new IllegalArgumentException("Read-ahead limit < 0");
}
synchronized (lock) {
ensureOpen();
this.readAheadLimit = readAheadLimit;//线程同步限制
markedChar = nextChar;//标记当前位置
markedSkipLF = skipLF;//标记换行符跳过状态
}
}状态返回至标记位置:public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
if (markedChar < 0)
throw new IOException((markedChar == INVALIDATED)
? "Mark invalid"
: "Stream not marked");
nextChar = markedChar;
skipLF = markedSkipLF;
}
}关闭流,清空缓存:public void close() throws IOException {
synchronized (lock) {
if (in == null)
return;
try {
in.close();
} finally {
in = null;
cb = null;
}
}
}返回字符串流:public Stream<String> lines() {
Iterator<String> iter = new Iterator<String>() {//字符串迭代器
String nextLine = null;

@Override
public boolean hasNext() { //检测是否还可读
if (nextLine != null) {
return true;
} else {
try {
nextLine = readLine();
return (nextLine != null);
} catch (IOException e) {
throw new UncheckedIOException(e);//未检查IO异常
}
}
}

@Override
public String next() {//读取下一行
if (nextLine != null || hasNext()) {
String line = nextLine;
nextLine = null;
return line;
} else {
throw new NoSuchElementException();
}
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
iter, Spliterator.ORDERED | Spliterator.NONNULL), false);//返回一个有定制操作迭代器的String流
}

Buffered型的IO类设计思路大同小异,希望读者可以举一反三
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: