[重学Java基础][Java IO流][Part.7] 字符串输入输出流
StringReader
概述
StringReader 字符输入流 和CharArrayReader类似 都是节点流 内存流
和CharArrayReader不同的地方是直接读入字符串而不是字符数组
用法也是类似的 都是用于接口适配 如果你需要调用的接口采用了Reader为参数
而你的数据源来自内存而不是其他外部数据源 ,那么就可以使用StringReader
源码分析
成员变量
和CharArrayReader相似
字符串内容体
private String str;
流内容长度
private int length;
索引游标 下一个流读入位置
private int next = 0;
在流中被标记的位置
private int mark = 0;
成员方法
构造方法 很简单 传入一个String参数s 初始化成员变量即可
public StringReader(String s) {
this.str = s;
this.length = s.length();
}
读取字符方法 如果下一个读入位置已经超过了流内容长度 则返回-1表示已读取完毕
否则返回对应索引位置的字符对应的整型数值 并移动索引游标
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
if (next >= length)
return -1;
return str.charAt(next++);
}
}
读入字符 并写入到字符数组方法
入参为指定的要存储的内容的字符数组char cbuf[],数组存储起始的位置int off和终止位置int len
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)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
如果索引游标超过数组长度 读取完毕 返回-1
if (next >= length)
return -1;
整形值int n,设置为length-next和len之间的较小值,因为一般情况下使用len即可,
如果len长度超过了数据的总长度,那么就要使用length-next的值 以避免读入越界
int n = Math.min(length - next, len);
使用String类的getChars方法,将指定str从next到next+n位置的数据
复制到传入的字符数组cbuf中,起始位置从off开始
str.getChars(next, next + n, cbuf, off);
移动索引游标
next += n;
return n;
}
}
跳过方法 指定跳过long ns个字符内容
public long skip(long ns) throws IOException {
synchronized (lock) {
ensureOpen();
如果游标已经到尾部 则说明流已经读完 不能再跳过了
if (next >= length)
return 0;
n为真正的跳过的字符大小 一般是ns的大小 但如果ns
超过了数据的总长度,那么就要使用length-next的值 以避免越界
long n = Math.min(length - next, ns);
这里是处理ns为负数的情况 如果ns为负数 则n一定为ns
Math.max(-next, n)计算保证了如果ns为负数且其绝对值大于next时
倒回next大小,回到流起点 避免越界
即只有当读取位置大于回退的数值大小时才可以回腿,所以最多之能回退到数据的起点位置
n = Math.max(-next, n);
next += n;
return n;
}
}
标记与重置
标记方法 在指定位置做标记,与reset方法连用,可以让流回退到标记位置
public void mark(int readAheadLimit) throws IOException {
if (readAheadLimit < 0){
throw new IllegalArgumentException("Read-ahead limit < 0");
}
synchronized (lock) {
ensureOpen();
mark = next;
}
}
重置方法 移动索引游标到标记位置
public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
next = mark;
}
}
关闭方法 可以看到 StringReader是内存流 不存在数据源 所以关闭就是置为空值
public void close() {
synchronized (lock) {
str = null;
}
}
示例
读入
String str = "使用StringReader";
StringReader reader = new StringReader(str);
int i=0;
reader.mark(0);
while((i=reader.read())!=-1)
{
System.out.print((char)i);
}
reader.reset();
System.out.println();
reader.skip(2);
while((i=reader.read())!=-1)
{
System.out.print((char)i);
}
输出结果
使用StringReader
StringReader
读入到字符数组
String str = "使用StringReader";
StringReader reader = new StringReader(str);
char[] chars = new char[1024];
int len = 0;
while ( (len = reader.read(chars)) != -1 ) {
}
System.out.println( new String(chars, 0, chars.length-1) );
输出结果
使用StringReader
StringWriter
概述
StringReader 字符输入流 和CharArrayReader类似 都是节点流 内存流
和CharArrayReader不同的地方是直接读入字符串而不是字符数组
用法也是类似的 都是用于接口适配 如果你需要调用的接口采用了Reader为参数
而你的数据源来自内存而不是其他外部数据源 ,那么就可以使用StringReader
源码分析
成员变量
和CharArrayReader相似
字符串内容体
private String str;
流内容长度
private int length;
索引游标 下一个流读入位置
private int next = 0;
在流中被标记的位置
private int mark = 0;
成员方法
构造方法 很简单 传入一个String参数s 初始化成员变量即可
public StringReader(String s) {
this.str = s;
this.length = s.length();
}
读取字符方法 如果下一个读入位置已经超过了流内容长度 则返回-1表示已读取完毕
否则返回对应索引位置的字符对应的整型数值 并移动索引游标
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
if (next >= length)
return -1;
return str.charAt(next++);
}
}
读入字符 并写入到字符数组方法
入参为指定的要存储的内容的字符数组char cbuf[],数组存储起始的位置int off和终止位置int len
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)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
如果索引游标超过数组长度 读取完毕 返回-1
if (next >= length)
return -1;
整形值int n,设置为length-next和len之间的较小值,因为一般情况下使用len即可,
如果len长度超过了数据的总长度,那么就要使用length-next的值 以避免读入越界
int n = Math.min(length - next, len);
使用String类的getChars方法,将指定str从next到next+n位置的数据
复制到传入的字符数组cbuf中,起始位置从off开始
str.getChars(next, next + n, cbuf, off);
移动索引游标
next += n;
return n;
}
}
跳过方法 指定跳过long ns个字符内容
public long skip(long ns) throws IOException {
synchronized (lock) {
ensureOpen();
如果游标已经到尾部 则说明流已经读完 不能再跳过了
if (next >= length)
return 0;
n为真正的跳过的字符大小 一般是ns的大小 但如果ns
超过了数据的总长度,那么就要使用length-next的值 以避免越界
long n = Math.min(length - next, ns);
这里是处理ns为负数的情况 如果ns为负数 则n一定为ns
Math.max(-next, n)计算保证了如果ns为负数且其绝对值大于next时
倒回next大小,回到流起点 避免越界
即只有当读取位置大于回退的数值大小时才可以回腿,所以最多之能回退到数据的起点位置
n = Math.max(-next, n);
next += n;
return n;
}
}
标记与重置
标记方法 在指定位置做标记,与reset方法连用,可以让流回退到标记位置
public void mark(int readAheadLimit) throws IOException {
if (readAheadLimit < 0){
throw new IllegalArgumentException("Read-ahead limit < 0");
}
synchronized (lock) {
ensureOpen();
mark = next;
}
}
重置方法 移动索引游标到标记位置
public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
next = mark;
}
}
关闭方法 可以看到 StringReader是内存流 不存在数据源 所以关闭就是置为空值
public void close() {
synchronized (lock) {
str = null;
}
}
示例
读入
String str = "使用StringReader";
StringReader reader = new StringReader(str);
int i=0;
reader.mark(0);
while((i=reader.read())!=-1)
{
System.out.print((char)i);
}
reader.reset();
System.out.println();
reader.skip(2);
while((i=reader.read())!=-1)
{
System.out.print((char)i);
}
输出结果
使用StringReader
StringReader
读入到字符数组
String str = "使用StringReader";
StringReader reader = new StringReader(str);
char[] chars = new char[1024];
int len = 0;
while ( (len = reader.read(chars)) != -1 ) {
}
System.out.println( new String(chars, 0, chars.length-1) );
输出结果
使用StringReader
StringWriter
概述
StringWriter字符输入流 和CharArrayWriter类似 都是节点流 内存流
和CharArrayWriter不同的地方是直接写入字符串而不是字符数组
用法也是类似的 都是用于接口适配 如果你需要调用的接口采用了Reader为参数
而你的数据源来自内存而不是其他外部数据源 ,那么就可以使用StringWriter
源码分析
成员变量
仅有一个StringBuffer,因为流的操作有数据改变的方法,所以简单的String类型并不能满足
private StringBuffer buf;
成员方法
无参构造
public StringWriter() {
buf = new StringBuffer();
lock = buf;
}
带整型参数的构造函数,参数值值设定了内置buf初始化时的容量大小
并设置了StringBuffer对象为writer的锁对象
public StringWriter(int initialSize) {
if (initialSize < 0) {
throw new IllegalArgumentException("Negative buffer size");
}
buf = new StringBuffer(initialSize);
lock = buf;
}
写方法
写入一个字符
public void write(int c) {
buf.append((char) c);
}
写入字符数组char cbuf[] 起始位置off,写入长度len
public void write(char cbuf[], int off, int len) {
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
buf.append(cbuf, off, len);
}
写入字符串 此类最常用方法
public void write(String str) {
buf.append(str);
}
写入字符串 可设定起始位置和写入长度
public void write(String str, int off, int len) {
buf.append(str, off, off + len);
}
写入字符序列 和写入字符串方法类似 只不过入参是字符序列
所以可以写入所有字符序列的实现类 如StringBuffer StringBuilder等
public StringWriter append(CharSequence csq) {
write(String.valueOf(csq));
return this;
}
追加写入方法 和写入大同小异 只不过返回类对象 可以链式调用
public StringWriter append(CharSequence csq) {
write(String.valueOf(csq));
return this;
}
public StringWriter append(CharSequence csq, int start, int end) {
if (csq == null) csq = "null";
return append(csq.subSequence(start, end));
}
public StringWriter append(char c) {
write(c);
return this;
}
获取字符串
public String toString() {
return buf.toString();
}
获取字符缓冲
public StringBuffer getBuffer() {
return buf;
}
刷新和关闭 都是空实现 因为此流为内存流 无数据汇
public void flush() {
}
public void close() throws IOException {
}
示例
StringBuffer sb = new StringBuffer("使用StringWriter");
StringWriter stringWriter=new StringWriter(100);
stringWriter.append(sb);
System.out.println(stringWriter.toString());
stringWriter.write("追加写入");
System.out.println(stringWriter.toString());
输出结果
使用StringWriter
使用StringWriter追加写入