`
足至迹留
  • 浏览: 486238 次
  • 性别: Icon_minigender_1
  • 来自: OnePiece
社区版块
存档分类
最新评论

<6> Reader && Writer

阅读更多
[续...]

支持国际文本的编程语言必须能识别原始字节读写和字符读写的差别,因为在国际化的系统里,原始字节和字符已经不是同一件事了。能读字符的类必须能解析各种字符编码(不仅是ASCII)并能把他们翻译成语言的本地字符集(native character set)。能写字符的类必须能把语言的本地字符集翻译成各种编码,并能输出它们。java提供的这种能力的类就是ReaderWriter(第一篇里提到过,他们底层仍然是基于字节的,只是做了编码的转换)。

java.io.Writer类模仿了java.io.OutputStream,java.io.Reader类模仿了java.io.InputStream类。他们里面包含的方法签名都很类似。Filtered input and output Streams are chained to other streams in their constructors, 类似地,filtered readers and writers are chained to other readers and writers in their constructors。InputStream和OutputStream是基于字节的输入输出类的抽象父类,Reader和Writer是基于字符的输入输出类的抽象父类。

然而,bytes在不同系统里是一个统一的概念,字符却不是,相同的字符用不同的字符集编码结果可能就不同。Reader和Writer类的具体子类可以把字符在java的内部Unicode字符集编码与不同的字符集编码之间转换。

6.1 The java.io.Writer Class
Writer类是抽象的,就像OutputStream一样。但就像OutputStream一样,很多时候我们不用关心具体的子类是什么,使用Writer类型获取字符输出类就行。
使用Writer就像使用OutputStream一样,只是它输出的不是bytes,而是chars。下面的write方法输出char数组从offset开始偏移length长度的一个子数组。
public abstract void write(char[] text, int offset, int length) throws IOException

就像OutputStream, Writer也可以使用缓存,因为它们底层的output stream是可以缓存的(writer的底层实现仍然是基于stream的),强制缓存里的字符输出可以调用flush()方法:
public abstract void flush() throws IOException

close()方法可以关闭writer和释放与之关联的所有资源:
public abstract void close() throws IOException

这个方法会先flush了writer,然后关闭底层的实现output stream。

6.2 The OutputStreamWriter Class
java.io.Writer是抽象类,它大部分子类是OutputStreamWriter类型:
public class OutputStreamWriter extends Writer

OutputStreamWriter的构造方法connects a character writer to an underlying output stream。
public OutputStreamWriter(OutputStream out) 
public OutputStreamWriter(OutputStream out, String encoding) throws 
UnsupportedEncodingException

第一个构造方法默认使用平台默认的encoding从underlying stream中输出。第二个构造方法可以指定一个encoding。
默认的编码可以通过String defaultEncoding = System.getProperty("file.encoding");获取。

write()方法可以根据构造方法中指定的编码把字符转成bytes,然后把这些字节输出到关联的output stream里。
public void write(int c) throws IOException 
public void write(char[] text, int offset, int length) throws IOException 
public void write(String s, int offset, int length) throws IOException

getEncoding()方法能获得Writer实际使用的字符编码:
public String getEncoding()


flush()和close()方法可以flush和close the underlying output stream.
public void flush() throws IOException 
public void close() throws IOException


6.3 The java.io.Reader Class
使用reader几乎和使用inputStream一样,只是reader读的是字符,而InputStream读的是bytes. 基本的read()方法从underlying input stream读取指定数量的字符到字符数组char[]指定的偏移处。
public abstract int read(char[] buffer, int offset, int length) throws IOException

read()方法返回实际读取到的字符的个数,如果遇到数据的结束标识则返回-1.

调用public int read(char[] buffer) throws IOException相当于read(buffer, 0, buffer.length)。

public int read() throws IOException方法返回一个单独的字符。

上面提到的三个read()方法都会阻塞,直到一些输入流可用,或遇到I/O错误,或遇到stream的结束。

6.4 The InputStreamReader Class
Reader最基本的子类是InputStreamReader:
public class InputStreamReader extends Reader


它的构造方法connects a character reader to an underlying input stream.
public InputStreamReader(InputStream in) 
public InputStreamReader(InputStream in, String encoding) 
throws UnsupportedEncodingException


第一个构造方法使用平台默认的编码(系统属性file.encoding的值),第二个构造方法使用指定的编码。
read()方法从underlying input stream读取字节,然后根据编码把字节转成字符。
public int read() throws IOException
public int read(char c[], int off, int length) throws IOException
getEncoding()方法返回reader使用的编码名称。

6.5 Character Array Readers and Writers
java.io.ByteArrayInputStream和java.io.ByteArrayOutputStream类可以使用stream的方法来读写字节数组;与之对应java.io.CharArrayReader和java.io.CharArrayWriter类使用Reader和Writer方法来读写char数组。因为char数组是java内部的Unicode字符,所以我们不用考虑不同编码之间的转换,如果想读取非Unicode编码的文本数组,就需要把ByteArrayInputStream关联到InputStreamReader上来操作。类似地,把文本以非Unicode编码形式来输出成一个字节数组需要把OutputStreamWriter关联到ByteArrayOutputStream上。

6.5.1 The CharArrayWriter Class
CharArrayWriter维护chars的一个数组,保存在内部的protected字段里:
protected char[] buf


无参构造方法创建一个默认32个字符缓存的CharArrayWriter对象,也可以自己指定缓存大小:
public CharArrayWriter() 
public CharArrayWriter(int initialSize)


writer()方法会把字符写到buffer里,如果缓存没有充分的空间保存则把缓存空间扩展1倍:
public void write(int c) 
public void write(char[] text, int offset, int length) 
public void write(String s, int offset, int length)


CharArrayWriter的flush()方法什么都不做,因为这个类的操作都是在jvm的内部,不需要输出到外部文件或网络,所以不需要flush操作

close()方法调用后就不能再往缓存里写数据了,否则会抛出IOException.
但是close之后,还可以以其他方式来读取缓存的数据。writeTo()方法能把缓存的数据复制到其他的Writer对象里:
public void writeTo(Writer out) throws IOException
toCharArray()方法返回缓存的一份拷贝:
public char[] toCharArray()

如:
CharArrayWriter caw = new CharArrayWriter(65536); 
for (int i = 0; i < 65536; i++) 
{ 
    caw.write(i); 
} 
caw.close(); 
char[] unicode = caw.toCharArray();


6.5.2 The CharArrayReader Class
CharArrayReader使用chars数组作为读取文本的数据源。这个类是少有的几个不需要底层InputStream作为数据源的reader,而是用char数组作为数据源
public CharArrayReader(char[] text) 
public CharArrayReader(char[] text, int offset, int length)


CharArrayReader内部一个protected字段buf[]保存了数据源text array的引用,所以可能会有并发问题

protected char buf[]
protected int pos
protected int count
protected int markedPos
read()方法从buf[]里读取文本,同时更新读取的位置pos字段。
public int read() throws IOException
public int read(char[] buffer, int offset, int length) throws IOException

最后,close()方法会把buf设置为null。此后再去读数据会抛出IOException。

6.6 String Readers and Writers
java.io.StringReader和java.io.StringWriter类允许使用Reader和Writer方法来读写字符串。就像char数组一样,java的string也是由纯Unicode字符组成的。

6.6.1 String Writers
StringWriter类维护一个内部的java.lang.StringBuffer对象,要输出的字符都追加在StringBuffer后面,同时很方便的转成String。
public class StringWriter extends Writer
public StringWriter()
protected StringWriter(int initialSize) //java 1.1
public StringWriter(int initialSize) // Java 2


StringWriter类有通常所见的一系列write()方法,所有这些方法都是把要输出的数据追加到StringBuffer后面:
public void write(int c) 
public void write(char[] text, int offset, int length) 
public void write(String s) 
public void write(String s, int offset, int length)


StringWriter类有flush()和close()方法,但两个方法实现都是空的,因为此类的所有操作都是在类的内部进行,所以不需要flush和close.
public void flush()
public void close()

所以即使调用了close()方法,仍然可以继续调用write()方法。有两个方法可以获取StringWriter的内部缓存,toString()和getBuffer()。
public String toString()
public StringBuffer getBuffer()

String是不可变对象,改变toString()返回的字符串对StringWriter类没有影响,但是改变getBuffer()返回的对象就会影响StringWriter的状态。

6.6.2 String Readers
StringReader从string里获取字符。这对你想顺序处理字符串里的每个字符非常有用,这个类是已废弃的java1.0的类StringBufferInputStream的替代:
public class StringReader extends Reader
因为String对象是不可变的,所以StringReader类不会改变数据源String对象,也不会存在多线程问题,read()方法会尽可能多的从string里读取请求的数据:
public int read() throws IOException
public int read(char[] buffer, int offset, int length) throws IOException

最后,close()方法会把内部的string数据设置为null。之后如果再去读数据则会抛出IOException.

6.7 Print Writers
java.io.PrintWriter是java.io.Writer类的子类,拥有我们熟悉的如PrintStream(比如System.out一样)拥有的print()和println()方法,这两个方法的区别是后者操作完毕后会添加一个换行符。这是特意让PrintWriter和PrintStream相似的。java1.0版本中PrintStream是用来输出基于文本(text-oriented)的内容,但是它不能处理多字节字符集; java1.1之后,Streams就专门用来基于字节(byte-oriented)的内容和数字类型的输出,如果需要输出文本,需要使用Writers。

PrintStream和PrintWriter的主要区别就是PrintWriter能处理多字节编码集,PrintStream未完全国际化,不能以平台无关的方式处理换行动作,这些问题再PrintWriter中得以解决。Sun本来想废弃PrintStream,由PrintWriter代替,但是这样会破坏太多已存在的代码(因此,java1.1时Sun废弃了PrintStream,但java1.2又取消废弃了)。
PrintWriter有四个构造方法:
public PrintWriter(Writer out) 
public PrintWriter(Writer out, boolean autoFlush) 
public PrintWriter(OutputStream out) 
public PrintWriter(OutputStream out, boolean autoFlush)

PrintWriter既可以把文本输出到output Stream也可以输出到其他的writer。如果autoFlush设置为true,则PrintWriter每次调用println()都会调用flush()。

PrintWriter类实现了java.io.Writer的抽象方法write():
public void write(int c)
public void write(char[] text)
public void write(String s)
public void write(String s, int offset, int length)
public void flush()
public void close()
这些方法使用上跟其他Writer类几乎一样,有一点区别就是PrintWriter类的所有方法都不会抛出IOException。如果内部发生IOException,则方法会内部捕获异常,并设置error flag, 使用checkError()方法可以获取这个标志的值:
public boolean checkError()

PrintWriter类的主要优点就是它重载了9种print()和10种println()方法。任何java对象,变量或文本都可以通过这两个方法输出。println()方法可以识别平台独立的换行符,根据autoFlush标志来刷新输出。print()方法不会这样,否则两者就一样了。
public void print(boolean b)
public void print(char c)
public void print(int i)
public void print(long l)
public void print(float f)
public void print(double d)
public void print(char[] text)
public void print(String s)
public void print(Object obj)

public void println()
public void println(boolean b)
public void println(char c)
public void println(int i)
public void println(long l)
public void println(float f)
public void println(double d)
public void println(char[] c)
public void println(String s)
public void println(Object o)

注意:网络编程中永远不要使用println()方法,无论是PrintWriter还是PrintStream。大多数网络协议像HTTP期望看到一个回车/换行对作为行分隔符。如果使用println()方法,你的程序可能可以在windows上运行,但是在其他平台上可能出错,而且很难诊断。

另外关于OutputStream和Writer还有一点注意
(1)如果使用带缓存(Buffered)的stream或writer,最后如果不flush或close掉,缓存里的数据不会输出到目的地。
(2)而如果使用不带缓存的OutputStream或Writer,同样不flush或close掉,则OutputStream会自动输出所有内容,而Writer仍然不会输出,需要flush或close。
当然,任何io操作都要养成用完关闭的习惯,而且是必须这样。
[完]
0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics