Java的IO系统(3) - 字符流类
字节流虽然方便也强大,但是不支持Unicode。字符流就是为了解决这个问题的,所以除了面对的对象不一样(一个是字节,一个是字符),他们的类和行为很相仿。
Reader 类
这是字符输入流的积累,也是一个抽象类。主要有以下方法:
由于和字节流类似,本篇相对简略一些
方法 | 描述 |
---|---|
abstract void close( ) | 关闭输入源。如果试图继续读取,将产生IOException异常 |
void mark(int mumChars) | 在输入流的当前位置放置标记,该标记在读入numChars个字符之前都一直有效 |
boolean markSupported( ) | 如果这个流支持mark或reset方法,就返回true |
int read() | 返回一个表示调用输入流中下一个可用字符的整数。如果到达文件末尾,就返回-1 |
int read(char buffer[ ]) | 尝试读取buffer.length个字符到buffer中,并且返回成功读取的实际字符数。如果到达文件末尾,则返回-1 |
int read(CharBuffer buffer) | 尝试读取字符到buffer中,并且返回成功读取的实际字符数。如果到达文件末尾,则返回-1 |
abstract int read(char buffer[ ],int offset,int numChars) | 尝试读取numChars个字符到buffer中,从buffer[offset]开始保存读取的字符,返回成功读取的字符数。如果到达文件末尾,就返回-1 |
boolean ready( ) | 如果下一个输入请求不等待,就返回true;否则返回false |
void reset( ) | 将输入指针重新设置为前面设置的标记位置 |
long skip(long numChars) | 略过numChars个输入字符,返回实际略过的字符数 |
Writer 类
主要有以下方法:
方法 | 描述 |
---|---|
Writer append(char ch) | 将ch追加到调用输出流的末尾,返回对调用流的引用 |
Writer append(CharSequence chars) | 将chars追加到调用输出流的末尾,返回对调用流的引用 |
Writer append(CharSequence chars,int begin, int end) | 将chars中从begin到end- 1之间的字符追加到调用输出流的末尾,返回对调用流的引用 |
abstract void close( ) | 关闭输出流。如果试图继续向其中写入内容,将产生IOException异常 |
abstract void flush( ) | 完成输出状态,从而清空所有缓冲区 |
void write(int ch) | 向调用输出流写入单个字符。注意参数是int类型,从而可以直接使用表达式调用write方法,而不必将之强制转换回char类型。但是只会写入低阶的16位 |
void write(char buffer[]) | 将整个字符数组写入调用输出流中 |
abstract void write(char buffer[],int offset,int numChars) | 将buffer数组中从buffer[offset]开始的numChars个字符写入调用输出流中 |
void write(String str) | 将str写入调用输出流中 |
void write(String str, int offset,int numChars) | 将字符串str中从offset开始的numChars个字符写入调用输出流中 |
FileReader
这是FileInputStream的字符流对应类。做为对比,我们把代码放一起:
int size;
try (FileInputStream f = new FileInputStream("/Users/sheldon/IdeaProjects/QLExpress/README.md")) {
System.out.println("文件总字节数" + (size = f.available()));
int n = size / 400;
System.out.println("分400份,头" + n + "个字节:");
for (int i = 0; i < n; i++) {
System.out.print((char) f.read());
}
System.out.println("\n剩余字节数: " + f.available());
System.out.println("接下来" + n + "个字符:");
byte b[] = new byte[n];
if (f.read(b) != n) {
System.err.println("不足" + n + "个字符。");
}
System.out.println(new String(b, 0, n));
System.out.println("\n剩余字节:" + (size = f.available()));
System.out.println("跳过总的一半");
f.skip(size / 2);
System.out.println("剩余字节 " + f.available());
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("-------");
try (FileReader f = new FileReader("/Users/sheldon/IdeaProjects/QLExpress/README.md")){
size = 0;
while (size < 60) {
System.out.print((char)f.read());
size++;
}
}
输出内容:
文件总字节数21885
分400份,头54个字节:
# QLExpressåºæ¬è¯æ³
[;
FileOutputStream fos2 = new FileOutputStream("t2.txt");) {
byte[] bytes = s.getBytes();
int l = bytes.length;
fos1.write(bytes);
fos2.write(bytes, l - 10, 10);
}
try (FileWriter fw = new FileWriter("t3.txt");) {
fw.write(s.toCharArray());
}
能够在t3.txt中看到完整正确的字符串。
CharArrayReader、CharArrayWriter
分别是ByteArrayInputStream和ByteArrayOutputStream的对应类。
BufferedReader
这是BufferedInputStream的对应类。我们按行处理文件的readLine()方法就来自该类。从Java8开始,该类增加了一个用于按行生成迭代流的方法lines(),返回值类型是java.util.stream.Stream。
BufferedWriter
无他。
PushbackReader 类
先看对比代码:
String s = "if (a==1) {a = 2;}";
try (ByteArrayInputStream is = new ByteArrayInputStream(s.getBytes());
PushbackInputStream pis = new PushbackInputStream(is);) {
byte c;
while ((c = (byte) pis.read()) != -1 ) {
if (c == '=') {
c = (byte) pis.read();
if (c == '=') {
System.out.print(" 是 ");
} else {
System.out.print(" 赋值为 ");
pis.unread(c);
}
} else {
System.out.print((char)c);
}
}
}
System.out.println();
System.out.println("---------");
try (PushbackReader pr = new PushbackReader(new CharArrayReader(s.toCharArray()))){
int i;
while ((i = pr.read()) != -1 ) {
char c = (char) i;
if (c == '=') {
c = (char) pr.read();
if (c == '=') {
System.out.print(" 正是 ");
} else {
System.out.print(" 写值为 ");
pr.unread(c);
}
} else {
System.out.print((char)c);
}
}
}
System.out.println();
System.out.println(s.getBytes().length);
System.out.println(s.toCharArray().length);
输出:
if (a 是 1) {a 赋值为 2;}
---------
if (a 是 1) {a 赋值为 2;}
18
18
可见结果一样,而且字节数和字符数也一样。如果有汉字呢?
String s = "if (a==你) {a = 我;}";
try (ByteArrayInputStream is = new ByteArrayInputStream(s.getBytes());
PushbackInputStream pis = new PushbackInputStream(is);) {
byte c;
while ((c = (byte) pis.read()) != -1 ) {
if (c == '=') {
c = (byte) pis.read();
if (c == '=') {
System.out.print(" 是 ");
} else {
System.out.print(" 赋值为 ");
pis.unread(c);
}
} else {
System.out.print((char)c);
}
}
}
System.out.println();
System.out.println("---------");
try (PushbackReader pr = new PushbackReader(new CharArrayReader(s.toCharArray()))){
int i;
while ((i = pr.read()) != -1 ) {
char c = (char) i;
if (c == '=') {
c = (char) pr.read();
if (c == '=') {
System.out.print(" 是 ");
} else {
System.out.print(" 赋值为 ");
pr.unread(c);
}
} else {
System.out.print((char)c);
}
}
}
System.out.println();
System.out.println(s.getBytes().length);
System.out.println(s.toCharArray().length);
输出:
if (a 是 ¦ᄑᅠ) {a 赋值为 ₩ネム;}
---------
if (a 是 你) {a 赋值为 我;}
22
18
原因是Java把latin1字符和utf16区别对待。一个latin1字符无论是字节还是字符都是1,而utf16的字节是2,字符是1。
PrintWriter
无他
Console类
Console类是Java6加入到java.io包的,用于操作控制台:读取控制台、写到控制台。该类的大部分功能都可以通过System.in和System.out获得,但也有自己独特的地方,比如不显示输入的密码。
Console没有公开的构造方法,只能通过System.console()获取实例。没有可用的控制台会返回null。 主要方法如下:
方法 | 描述 |
---|---|
void flush( ) | 将缓冲的输出物理地写到控制台 |
Console format(String fmtString,Object..args) | 使用fmtString指定的格式将args 写到控制台 |
Console printf(String fmtString,Object..args) | 使用fmtString指定的格式将args 写到控制台 |
Reader reader( ) | 返回对连接到控制台的Reader对象的引用 |
String readLine( ) | 读取并返回从键盘输入的字符串。当用户按下回车键时,输入结束。如果已经到达控制台输入流的末尾,就返回null;如果失败,就抛出IOError异常 |
String readLine(String fmtString,Object…args) | 按照fmtString和args指定的格式显示提示性字符串,然后读取并返回从键盘输入的字符串。当用户按下回车键时,输入结束。如果已经到达控制台输入流的末尾,就返回null;如果失败,就抛出IOError异常 |
char[ ] readPassword() | 读取从键盘输入的字符串。当用户按下回车键时,输入结束。输入的字符串并不显示。如果已经到达控制台输入流的末尾,就返回null;如果失败,就抛出IOError异常 |
char[ ] readPassword(String fmtString,Object..args) | 按照fmtString和args指定的格式显示提示性字符串,然后读取从键盘输入的字符串。当用户按下回车键时,输入结束。输入的字符串并不显示。如果已经到达控制台输入流的末尾,就返回null;如果失败,就抛出IOError异常 |
PrintWriter writer( ) | 返回对连接到控制台的Writer对象的引用 |
