17.6 使用流式输入/输出
下面的例子演示了几个Java的输入/输出字符流类和方法。该程序执行标准wc(字数统计)命令。程序有两个模式:如果没有语句提供的文件名存在,程序对标准输入流进行操作。如果一个或多个文件名被指定,程序对每一个文件进行操作。
// A word counting utility.
import java.io.*;
class WordCount {
public static int words = 0;
public static int lines = 0;
public static int chars = 0;
public static void wc(InputStreamReader isr)
throws IOException {
int c = 0;
boolean lastWhite = true;
String whiteSpace = " \t\n\r";
while ((c = isr.read()) != -1) {
// Count characters
chars++;
// Count lines
if (c == '\n') {
lines++;
}
// Count words by detecting the start of a word
int index = whiteSpace.indexOf(c);
if(index == -1) {
if(lastWhite == true) {
++words;
}
lastWhite = false;
}
else {
lastWhite = true;
}
}
if(chars != 0) {
++lines;
}
}
public static void main(String args[]) {
FileReader fr;
try {
if (args.length == 0) { // We're working with stdin
wc(new InputStreamReader(System.in));
}
else { // We're working with a list of files
for (int i = 0; i < args.length; i++) {
fr = new FileReader(args[i]);
wc(fr);
}
}
}
catch (IOException e) {
return;
}
System.out.println(lines + " " + words + " " + chars);
}
}
wc( )方法对任何输入流进行操作并且计算字符数,行数和字数。它在lastNotWhite里追踪字数的奇偶和空格。当在没有参数的情况下执行时,WordCount以System.in为源流生成一个InputStreamReader对象。该流然后被传递到实际计数的 wc( )方法。当在有一个或多个参数的情况下执行时,WordCount 假设这些文件名存在并给每一个文件创建FileReader,传递保存结果的FileReader对象给wc( ) 方法。两种情况下,在退出之前都打印结果。
17.6.1 用StreamTokenizer (流标记)来改善wc( )
在输入流中一个更好的寻找模式的方法使用Java的另一个输入/输出类:StreamTokenizer。与第16章的StringTokenizer相似,StreamTokenizer把InputStream拆解到被字符组界定的标记(token)中。它有下面的构造函数:
StreamTokenizer(Reader inStream)
这里,inStream必须具有Reader的某种形式。StreamTokenizer定义了几个方法。该例中,我们仅用到少数几个。为重置分隔符的默认设置,我们使用 resetSyntax( )方法。分隔符的默认设置与标记表征的Java程序完美和谐,而且是为该例专用的。我们规定我们的标记,即“字”,是两边都有空格的明显字符组成的连续的字符串。我们用eolIsSignificant()来保证换行符作为标记被传递,所以我们可以和计算字数一样计算行数。它的通常形式如下:
void eolIsSignificant(boolean eolFlag)
如果eolFlag为true,行结束符作为标记返回;若为false,行结束符被忽略。wordChars( )方法用来指定可以用于字的字符范围。它的通常形式如下:
void wordChars(int start, int end)
这里,start和end指定了有效字符的范围。程序中,从33到255范围内的字符都是有效字符。
空格符由 whitespaceChars( )说明。它的一般形式如下:
void whitespaceChars(int start, int end)
这里,start和end指定了有效空格符的范围。下一个标记通过调用nextToken( )从输入流获得,它返回标记的类型。StreamTokenizer定义个四个int型常量:TT_EOF,TT_EOL,TT_NUMBER和TT_WORD。有三个实例变量。nval是一个公开的double 型变量,用来保存可识别的字数的值。sval是一个public String 型变量,用来保存可识别的的字的值。ttype是一个int型变量,说明刚刚被nextToken( )方法读取的标记的类型。如果标记是一个字,ttype等于TT_WORD。如果标记为一个数,ttype等于TT_NUMBER。如果标记是单一字符,ttype包含该字符的值。如果遇到一个行结束情况,ttype等于TT_EOL(这假定了参数为true调用eolIsSignificant())。如果遇到流的结尾,ttype 等于TT_EOF。用StreamTokenizer 修改过的字数计算程序显示如下:
// Enhanced word count program that uses a StreamTokenizer
import java.io.*;
class WordCount {
public static int words=0;
public static int lines=0;
public static int chars=0;
public static void wc(Reader r) throws IOException {
StreamTokenizer tok = new StreamTokenizer(r);
tok.resetSyntax();
tok.wordChars(33, 255);
tok.whitespaceChars(0, ' ');
tok.eolIsSignificant(true);
while (tok.nextToken() != tok.TT_EOF) {
switch (tok.ttype) {
case tok.TT_EOL:
lines++;
chars++;
break;
case tok.TT_WORD:
words++;
default: // FALLSTHROUGH
chars += tok.sval.length();
break;
}
}
}
public static void main(String args[]) {
if (args.length == 0) { // We're working with stdin
try {
wc(new InputStreamReader(System.in));
System.out.println(lines + " " + words + " " + chars);
} catch (IOException e) {};
} else { // We're working with a list of files
int twords = 0, tchars = 0, tlines = 0;
for (int i=0; i
try {
words = chars = lines = 0;
wc(new FileReader(args[i]));
twords += words;
tchars += chars;
tlines += lines;
System.out.println(args[i] + ": " +
lines + " " + words + " " + chars);
} catch (IOException e) {
System.out.println(args[i] + ": error.");
}
}
System.out.println("total: " +
tlines + " " + twords + " " + tchars);
}
}