第 7章 Java的输入 /输出流
第 7章 Java的输入 /输出流
7.1 Java语言 I/O的类层次
7.2 Java中文件的操作
7.3 特殊的 I/O处理流
第 7章 Java的输入 /输出流
7.1 Java语言 I/O的类层次
Java输入 /输出流封装在包 java.io中, 其常用的层次
结构如图 7.1,7.2所示 。
除了图中给出的类外,实际使用中,我们还会碰
到 File类,它用来描述某个文件的信息。输入 /输出类
中还有几个比较重要的接口,例如,DataInput,Data
Output,File NameFilter。后面我们会作详细的介绍。
第 7章 Java的输入 /输出流
图 7.1
O b j e c t R a n d o m A c c e s s F i l e
I n p u t S t r e a m
O u t p u t S t r e a m
S e q u e n c e I n p u t S t r e a m
S t r i n g B u f f e r I n p u t S t r e a m
F i l t e r I n p u t S t r e a m
B y t e A r r a y I n p u t S t r e a m
P i p e d I n p u t S t r e a m
F i l e I n p u t S t r e a m
D a t a I n p u t S t r e a m
P u s h b a c k I n p u t S t r e a m
L i n e N u m b e r I n p u t S t r e a m
B u f f e r e d I n p u t S t r e a m
F i l t e r O u t p u t S t r e a m
B y t e A r r a y O u t p u t S t r e a m
P i p e d O u t p u t S t r e a m
F i l e O u t p u t S t r e a m
P r i n t S t r e a m
B u f f e r e d O u t p u t S t r e a m
D a t a O u t p u t S t r e a m
第 7章 Java的输入 /输出流
图 7.2
F i l t e r R e a d e r
P i p e d R e a d e r
S t r i n g R e a d e r
I n p u t S t r e a m R e a d e r
C h a r A r r a y R e a d e r
B u f f e r e d R e a d e r
P u s h b a c k R e a d e r
F i l e R e a d e r
L i n e N u m b e r R e a d e r
R e a d e r
F i l t e r W r i t e r
P i p e d W r i t e r
S t r i n g W r i t e r
O u t p u t S t r e a m W r i t e r
C h a r A r r a y W r i t e r
B u f f e r e d W r i t e r
F i l e W r i t e r
W r i t e r
F i l t e r W r i t e r
第 7章 Java的输入 /输出流
Java的流方法从结构上可以分为三大类:字节流、
数据流和打印流。字节输入 /输出流操作对象为字节
(byte);数据流的数据比较多,包含所有基本类型的二
进制数据;打印流是机器能够识别的字符形式 (包括
ASCII码和 Unicode码 )。
第 7章 Java的输入 /输出流
7.2 Java中文件的操作
编写程序免不了要经常与文件打交道,文件的输
入 /输出需要用到图 7.1中所列出的类 FileInputStream、
FileOutputStream及 RandomAccessFile类。前两个类针
对于顺序文件的存取,后一个类用于随机文件的读取。
后面我们会有详细的介绍。
第 7章 Java的输入 /输出流
7.2.1 文件与目录的描述类 —— File
File类并不用来进行文件的读 /写操作, 它用来描
述文件对象的属性, 既可以表示文件, 也可以表示目
录 。 使用它提供的方法, 我们可以得到所指对象的描
述信息, 包括名称, 存在否, 读 /写权限, 路径等等 。
需要注意的是, 当我们在 Windows环境使用路径时,
其分隔符不能是单一的, \” 符号, 因为与 C/C++相同,
符号, \” 已经被转意了 。 例如:
第 7章 Java的输入 /输出流
c:\jbuilder3\java\bin
路径是非法的, 系统不会识别, 正确的应该为
c:\\jbilder3\\java\\bin
下面我们通过表 7.1给出 File类的一些方法及说明。
第 7章 Java的输入 /输出流
表 7.1 File类的方法及变量
方法及变量名 功能描述
public File(File dir,String name) 由指定的目录, 名字创建该对象
public File(String path,String name) 由指定的路径, 名字创建该对象
public File(String path) 由指定路径创建该对象
public staticfinal String pathSeparator 返回系统的路径分隔符
public boolean canRead() 如果文件可读为真
public boolean canWrite() 如果文件可写为真
public boolean delete() 删除文件
public boolean equals(Object obj) 文件比较, 如是同一个文件则为真
public boolean exists() 指定的文件存在返回真
第 7章 Java的输入 /输出流
public String getAbsolutePath() 得到文件的绝对路径
public String getName() 得到文件名
public String getParent() 得到父目录的名字
public String getPath() 返回路径
public int hashCode() 返回一个哈希码
public nativeboolean isAbsolute() 如果是绝对路径返回真
public boolean isDirectory() 如果是目录则返回真
public boolean isFile() 如果是文件则返回真
public long lastModified() 返回最近一次修改时间
public long length() 返回文件长度
第 7章 Java的输入 /输出流
public String[] list(FilenameFilter filter) 返回指定格式的目录中的文件名
public String[] list() 返回当前目录中的所有文件名
public boolean mkdir() 创建目录, 成功返回真
public boolean mkdirs() 创建路径中所有目录, 成功则返回真
public static final char pathSeparatorChar 返回路径分隔符
public boolean renameTo(Filedest) 文件更名, 成功返回真
public static final char separatorChar 返回文件分隔符
public String toString() 返回对象的字符串表示
第 7章 Java的输入 /输出流
下面我们给出几个 File类的应用实例 。 通过例题的
使用, 希望读者对 File类有更清楚的认识 。
例 7.1
import java.io.*;
public class MyClass1 {
public static void main(String args[]){
Filef=new
File("c:\\jbuilder3\\myprojects\\untitled5\\MyClass1.java");
if(!f.exists())
第 7章 Java的输入 /输出流
Transcript.println("File MyClass1.java doesn't exist!");
else {
Transcript.println("This file can read "+f.canRead());
Transcript.println("last modified "+f.lastModified());
Transcript.println("Parent is "+f.getParent());
Transcript.println("File length is "+f.length());
} }
public MyClass1() { }
}
第 7章 Java的输入 /输出流
该程序的运行结果如图 7.3所示 。
此程序中, 我们为了说明路径分隔符的使用方法,
实例化文件对象的时候给出了全路径, 其实由于我们
已经在 Java系统中设置好了源路径, 只要给出文件名
就行了 。
表 7.1中, list方法用于列出一个目录中所有的文件
或与某个模式相匹配的文件 。 下面我们给出两个例子
来讨论带参数或不带参数的 list方法的使用 。
第 7章 Java的输入 /输出流
图 7.3
第 7章 Java的输入 /输出流
例 7.2
import java.io.*;
public class MyClass2 {
public static void main(String args[]) {
File f1=new File("c:\\jbuilder3\\myprojects");
if(!f1.isDirectory())
Transcript.println("Error,"+f1+"isn't a directory!");
else{ String dirList[]=f1.list();
for(int i=0;i<dirList.length;i++)
第 7章 Java的输入 /输出流
Transcript.println(dirList[i]); }
} }
public MyClass2() { }
}
在该例中,我们并没有在调用 File类的 list方法中传
递参数,这样,处在目录 c:\\jbuilder3\\myprojects下的
所有文件及目录将均被输出,结果如图 7.4所示。
第 7章 Java的输入 /输出流
图 7.3
第 7章 Java的输入 /输出流
图 7.4
第 7章 Java的输入 /输出流
程序设计中, 往往会碰到根据某一匹配模式来查
找目录下的文件的问题, 这时就需使用 File类带参数的
list方法, 即
public String[] list(FilenameFilter filter)
其中,参数 FilenameFilter是一个接口,只有一个方
法的定义:
boolean accept(File dir,String name);
对于指定目录下的文件, 我们可以调用该方法确
定某个文件是否包含于其中 。 比如上例中, 我们查找
以, un” 打头的文件或目录, 程序如下:
第 7章 Java的输入 /输出流
例 7.3
import java.io.*;
public class MyClass3 implements FilenameFilter {
File f;
public static void main(String args[]){
File f1=new File("c:\\jbuilder3\\myprojects");
if(!f1.isDirectory())
Transcript.println("Error,"+f1+"isn't a directory!");
else{ MyClass3 m=new MyClass3(f1);
第 7章 Java的输入 /输出流
String dirList[]=f1.list(m);
for(int i=0;i<dirList.length;i++)
Transcript.println(dirList[i]);
} }
public MyClass3(File f) {
this.f=f; }
public boolean accept(File dir,String name){
return name.startsWith("un");
} }
第 7章 Java的输入 /输出流
7.2.2 文件 I/O处理
1,Byte流 (字节流 )文件的读取
该类的结构如图 7.1所示,我们主要用其中的
FileOutputStream和 FileInputStream类,它们的父类为
InputStream和 OutputStream。主要的方法有:
● InputStream
int read()
int read(byte buf[])
int read(byte buf[],int offset,int length)
close()
第 7章 Java的输入 /输出流
● OutputStream
int write(int c)
int write(byte buf[])
int write(byte buf[],int offset,int length)
close()
下面给出一个应用实例 7.4。
第 7章 Java的输入 /输出流
图 7.5
第 7章 Java的输入 /输出流
例 7.4
import java.io.*;
public class FileCopy {
public static void main(String args[]) throws IOException{
FileInputStream f1;
FileOutputStream f2;
f1=new FileInputStream("FileCopy.java");
f2=new FileOutputStream("acopy_of_java_file");
第 7章 Java的输入 /输出流
int temp;
while((temp=f1.read())!=-1)
f2.write(temp);
f1.close(); f1.close(); }
public FileCopy() { }
}
在该例中, 我们利用字节流将本程序拷贝至另一
个文件 acopy_of_java_file中, 如果指定的文件不存在,
则创建一个新文件, 否则原文件的内容会被新写入的
内容覆盖 。 当程序运行后, 将生成一个与原程序相同
的副本 。
第 7章 Java的输入 /输出流
2,Character流 (字符流 )文件的读取
该类如图 7.2所示, 输入 /输出类的父类为 Reader、
Writer,其基本的方法有:
● Reader
int read()
int read(char buf[])
int read(char buf[],int offset,int length)
close()
第 7章 Java的输入 /输出流
● Writer
int write(int c)
int write(char buf[])
int write(char buf[],int offset,int length)
close()
读者可与字节流进行比较, 注意二者方法的区别 。
下面我们用字符流来改写例 7.4:
第 7章 Java的输入 /输出流
例 7.5
import java.io.*;
public class FileCopy {
public static void main(String args[]) throws IOException{
FileReader f1;
FileWriter f2;
f1=new FileReader("FileCopy.java");
f2=new FileWriter("acopy_of_java_file");
第 7章 Java的输入 /输出流
int temp;
while((temp=f1.read())!=-1)
f2.write(temp);
f1.close(); f2.close(); }
public FileCopy() { }
}
第 7章 Java的输入 /输出流
例 7.6
import java.io.*;
public class ReadFile {
public static void main (String [ ] args)
throws IOException {
FileReader fr = new FileReader("ReadFile.java");
BufferedReader br = new BufferedReader(fr);
String line = br.readLine( );
while (line != null) {
第 7章 Java的输入 /输出流
Transcript.println(line);
line = br.readLine( ); }
br.close( );
}}
本程序中,我们通过类 BufferedReader对文件实
现按行读取,达到一行一行输出的目的,结果如图
7.6所示。
第 7章 Java的输入 /输出流
图 7.6
第 7章 Java的输入 /输出流
3,二进制数据流的文件读取
如果要读取与机器无关的基本数据类型的数据, 如
整型或浮点型的二进制数, 就要用到二进制数据文件流
DataInputStream,DataOutputStream。 实际使用中, 类
DataInputStream和 DataOutputStream必须和一个输入类
(InputStream)或输出类 (OutputStream)联接起来, 不能直
接用文件名或文件对象 (File)对其直接初始化, 例如:
第 7章 Java的输入 /输出流
例 7.7
import java.io.*;
public class DatastreamDemo {
public static void main(String args[])
throws IOException {
FileOutputStream f2=new FileOutputStream("data");
DataOutputStream dfo=new DataOutputStream(f2);
dfo.writeBoolean(true);
第 7章 Java的输入 /输出流
dfo.writeInt(100);
dfo.writeFloat(200.2f);
f2.close(); dfo.close();
FileInputStream f1=new FileInputStream("data");
DataInputStream dfi= new DataInputStream(f1);
boolean b=dfi.readBoolean();
int i=dfi.readInt();
float f=dfi.readFloat();
f1.close(); dfi.close();
Transcript.println("The value is,");
第 7章 Java的输入 /输出流
Transcript.println(" "+b);
Transcript.println(" "+i);
Transcript.println(" "+f); }
public DatastreamDemo() { }
}
该例中, 我们首先利用类 DataOutputStream生成一
个二进制文件 data,并对它写入三个不同类型的数据:
布尔型, 整型, 浮点型 。 然后利用 DataInputStream读入
刚刚输入的数据并显示出来, 结果如图 7.7所示 。 可以看
出, 输出结果与我们的输入是一一对应的 。
第 7章 Java的输入 /输出流
图 7.7
第 7章 Java的输入 /输出流
4,随机访问文件的读取
对于 InputStream/OutputStream,Reader/Writer类来
说, 它们都是顺序访问流, 只能进行顺序读写 。 而所
谓随机读写, 是指读写完上一个字节后, 不只能读写
其后继的字节, 还可以读写文件中任意的字节, 就好
象文件中有一个随意移动的指针一样 。
Java语言提供了类 RandomAccessFile来进行随机文
件的读取 。 在生成一个 RandomAccessFile对象时, 不仅
要说明文件对象或文件名, 同时还需指明访问模式,
即, 只读方式, (r)或, 读写方式, (rw),这类似于
C/C++中的 fopen() 函数 。
第 7章 Java的输入 /输出流
RandomAccessFile类的功能类似于
DataOutputStream类和 DataInputStream类的功能合并,
即实现了在一个流中进行读、写两种功能。其常用的
方法如表 7.2所示。
第 7章 Java的输入 /输出流
表 7.2 RandomAccessFile类的常用方法
方法名 功能描述
long getFilePointer() 返回当前文件指针
long length() 返回文件长度
boolean readBoolean() 读入一个布尔值
int readInt() 读入一个整数
string readLine() 读入一行字符串
void seek(long pos) 文件指针移到指定位置
int skipBytes(int n) 文件指针向前移 n个字节
void write(byte b[]) 把数组内容写入文件
void writeBoolean(boolean v) 写入一个布尔值
void writeInt(int v) 写入一个整数
第 7章 Java的输入 /输出流
例 7.8的功能与例 7.7一样,只不过是用
RandomAccessFile来实现的。
例 7.8
import java.io.*;
public class RandomDemo {
public static void main(String args[])
throws IOException {
RandomAccessFilefa=new
RandomAccessFile("data","rw");
第 7章 Java的输入 /输出流
fa.writeBoolean(true);
fa.writeInt(100);
fa.writeFloat(200.2f);
fa.seek(0);
boolean b=fa.readBoolean();
int i=fa.readInt();
float f=fa.readFloat();
Transcript.println("The value read from a random file is, ");
Transcript.println(" "+b);
Transcript.println(" "+i);
Transcript.println(" "+f);
第 7章 Java的输入 /输出流
fa.close(); }
public RandomDemo() { }
}
程序运行结果如图 7.8所示 。
第 7章 Java的输入 /输出流
图 7.8
第 7章 Java的输入 /输出流
7.3 特殊的 I/O处理流
除了前面我们介绍的一些常用的输入 /输出流之外,
java.io包中提供了很多特殊的输入 /输出流, 我们可描
述如表 7.3所示 。
第 7章 Java的输入 /输出流
类型说明 流 类 功能描述
管道流
PipedReader
PipedWriter
PipedInputStream
PipedOutputStream
用于在不同的线程或进程之间进行信息传递
内存流
CharArrayReader
CharArrayWriter
ByteArrayInputStream
ByteArrayOutputStrea
m
用于在内存中进行读 /写, 往往借助于数组, 通过
read()或 write()方法从数组中读 /写数据
StringReader
StringWriter
StringBufferInputStrea
m
从存在于内存的一个字符串中读 /写数据
输入流的连接 SequenceInputStream 将多个 InputStream连接起来顺序进行输入
过滤流
FilterReader
FilterWriter
FilterInputStream
FilterOutputStream
特定的抽象类, 须扩展
解析流 StreamTokenizer 用于对字符串的解析
表 7.3 java.io包中的特殊输入 /输出流
第 7章 Java的输入 /输出流
表 7.3 java.io包中的特殊输入 /输出流
第 7章 Java的输入 /输出流
7.3.1 管道流
管道流 (PipedStream)可用来把一个程序、线程或代
码段的输出直接连接到另一个程序、线程或代码段的
输入。 Java中管道的类有 PipedReader,PipedWriter、
PipedInputStream及 PipedOutputStream。使用过程中,
管道输入流作为一个通信管道的接收端进行数据的读
取,管道输出流作为发送端进行数据的传送。下面我
们先看一个实例:
第 7章 Java的输入 /输出流
例 7.9
import java.io.*;
public class PipedDemo {
public static void main(String args[])
throws IOException{
PipedWriter pw= new PipedWriter();
PipedReader pr= new PipedReader(pw);
new Read(pw,"ReadFile.txt").start();
new Write(pr,"WriteFile.txt").start(); }
第 7章 Java的输入 /输出流
public PipedDemo() { }
}
import java.io.*;
public class Write extends Thread{
PipedReader pr;
File f;
Write(PipedReader pr,String f){
this.pr=pr;
this.f=new File(f); }
public void run() {
try { FileWriter fw=new FileWriter(f);
第 7章 Java的输入 /输出流
int data;
while((data=pr.read())!=-1)
fw.write(data);
fw.close(); }catch(IOException e){}
}
public Write() { }
}
import java.io.*;
public class Read extends Thread{
PipedWriter pw;
File f;
第 7章 Java的输入 /输出流
Read(PipedWriter pw,String f){
this.pw=pw; this.f=new File(f); }
public void run() {
try {FileReader fr=new FileReader(f);
int data;
while((data=fr.read())!=-1)
pw.write(data);
pw.close(); }catch(IOException e){}
}
第 7章 Java的输入 /输出流
在本例中,我们通过定义两个线程进行管道
PipedReader,PipedWriter的连接。线程的概念读者可
参考后面章节的内容,这里只要把其理解为可以同时
工作的两段程序就可以了。程序运行后,WriteFile.txt
的内容就从文件 ReadFile.txt中完全拷贝过来。在管道
流的初始化过程中,可以给出对应的管道输入 /输出流
作为参数进行连接,如本例:
PipedReader pr= new PipedReader(PipedWriter pw);
第 7章 Java的输入 /输出流
或 PipedWriter pw=new
PipedWriter(PipedReader pr);
在 Java中,管道输入 /输出流还可以通过方法
connect()进行连接,如把本例中的程序改为
PipedWriter pw= new PipedWriter();
PipedReader pr= new PipedReader();
pr.connect(pw);
效果是一样的。读者也可用管道流
PipedInputStream,PipedOutputStream来实现上例,这
里留给读者自己练习。
第 7章 Java的输入 /输出流
7.3.2 内存的 I/O流
表 7.3给出了 Java支持内存读 /写的类, 总结起来有
以下两类 。
(1) 对应字节内存读 /写的有 ByteArrayInputStream、
ByteArrayOutputStream及 String
BufferInputStream。它们可以从字节数组中读取数
据或向字节数组中写入数据。例如:
例 7.10
import java.io,*;
public class MemoryDemo {
第 7章 Java的输入 /输出流
public static void main(String args[])throws IOException{
byte b[]={111,100,74,98,80,69};
byte temp[]=new byte[10];
ByteArrayInputStream bi= new ByteArrayInputStream(b);
ByteArrayOutputStreambo=new ByteArrayOutputStream();
StringBufferInputStream bs=
new StringBufferInputStream("A demo for memory input!");
int x;
while((x=bi.read())!=-1)
第 7章 Java的输入 /输出流
bo.write(x);
Transcript.println("The result of ByteArrayOutputStream is:
"+bo);
bs.read(temp,0,4);
Transcript.print("The result of StringBufferOutputStream is:
");
for(int i=0;i<=3;i++)
Transcript.print(" "+temp[i]);
Transcript.println(); Transcript.print(" "+bs);
}} 运行结果如图 7.9所示 。
第 7章 Java的输入 /输出流
图 7.9
第 7章 Java的输入 /输出流
对于 ByteArrayOutputStream来说,我们先从
ByteArrayInputStream类中把字节流读入 bo的缓冲区中,
然后直接进行输出,该对象调用自己的 toString()进行
输出格式转换。
(2) 对应字符内存读 /写的有 CharArrayReader、
CharArrayWriter,StringReader及 StringWriter。它们可
以从字符数组中读取数据或向字符数组中写入数据。
例如:
例 7.11
import java.io.*;
public class MemoryDemo {
第 7章 Java的输入 /输出流
public static void main(String args[])throws IOException{
char b[]={'a','t','e','s','t','d','e','m','o'};
char temp[]=new char[10];
CharArrayReader br= new CharArrayReader(b);
CharArrayWriter bw=new CharArrayWriter();
StringReader bsr= new StringReader("test demo!");
StringWriter bsw= new StringWriter();
int x;
while((x=br.read())!=-1)
bw.write(x);
第 7章 Java的输入 /输出流
Transcript.println("The result of CharArrayReader is:
"+bw);
bsr.read(temp,0,4);
Transcript.print("The result of StringReader is,");
for(int i=0;i<=3;i++)
Transcript.print(" "+temp[i]); Transcript.println();
bsw.write("hello,everybody!");
Transcript.print("The result of StringWriter is,");
Transcript.println(" "+bsw); }
} 程序运行结果如图 7.10所示 。
第 7章 Java的输入 /输出流
图 7.10
第 7章 Java的输入 /输出流
我们可以发现以上两个程序的基本架构无大的区
别, 只是对不同内存流, 构造方法有所不同 。 上述的
几种输出内存流, 它们在初始化的时候, 缺省的缓冲
区的大小均为 32个字节 。 当然, 实际操作过程中, 缓
冲区的大小会随数据的写入自动增加 。 不同的类会有
多种不同的方法, 方法的使用建立在对各种流的熟悉
程度之上, 读者应尽可能的学习 Java联机文档的内容,
获取更多的知识 。
第 7章 Java的输入 /输出流
7.3.3 多个输入流的连接
如果我们在对文件的操作中需要读取多个文件的话,
可使用 Java所提供的 SequenceInputStream类 。 该类把多
个文件连接起来, 形成多个连接的顺序输入流, 该流
依次打开每个输入流, 读取数据, 然后关闭该流, 并
自动切换到下一个输入流 。 它的两种构造方法如下:
第 7章 Java的输入 /输出流
SequenceInputStream(Enumeration e);
SequenceInputStream(InputStream s1,InputStream s2);
其中, Enumeration类为一接口, 处在 Java中的 util包中,
它包含两个方法:
public Object nextElement()
public boolean hasMoreElements()
方法 nextElement()取得集合中的下一个对象;方法
hasMoreElements()测试集合中的元素是否已经全部取
完,并返回一个布尔值。下面是一个利用顺序流对指
定的两个文件进行合并操作的例子。
第 7章 Java的输入 /输出流
例 7.12
import java.io.*;
import java.util.*;
public class ConcaDemo {
public static void main(String args[]) throws IOException{
String s[]=new String[2];
s[0]="PipedDemo.java";
s[1]="MemoryDemo.java";
Files flist=new Files(s);
第 7章 Java的输入 /输出流
SequenceInputStream si=new SequenceInputStream(flist);
FileOutputStreamfo=new
FileOutputStream("concaFile.txt");
int c;
while((c=si.read())!=-1)
fo.write(c);
si.close(); fo.close(); }
}
import java.io.*;
import java.util.*;
第 7章 Java的输入 /输出流
public class Files implements Enumeration{
private String[] listofFiles;
private int current=0;
public Files(String[] listofFiles) {
this.listofFiles=listofFiles; }
public boolean hasMoreElements(){
if(current<listofFiles.length)
return true;
else return false; }
public Object nextElement(){
第 7章 Java的输入 /输出流
try {
if(!hasMoreElements())
return null;
else { current++;
return new FileInputStream(listofFiles[current-1]); }
}catch(Exception e){return null;}
}}
当我们在 JBuilder中运行该程序后,文件
concaFile.txt就合并了 PipedDemo.java及
MemoryDemo.java中的内容。
第 7章 Java的输入 /输出流
7.3.4 过滤流
过滤流在读 /写数据的同时可以对数据进行处理。
Java提供了多个具有过滤功能的输入 /输出类,字节类
过滤流的父类均为 FilterInputStream、
FilterOutputStream。这两个类为抽象类,包含的子类有:
DataInputStream/DataOutputStream
BufferedInputStream/BufferedOutputStream
LineNumberInputStream
PushbackInputStream
PrintStream
第 7章 Java的输入 /输出流
FilterReader/FilterWriter
过滤流是建立在其他流之上的,也就是说,当使
用一个过滤流时,必须首先把过滤流连接到某个输入 /
输出流上。比如,可以通过输入过滤流的方法 read()从
下层流中读取数据,通过输出过滤流的 write()方法把数
据写入下层流。
第 7章 Java的输入 /输出流
不同过滤流实现不同的过滤功能, 例如:数据的
暂存, 数据的统计, 数据的转换等等 。 读者对上述的
一些类也不必死记, 使用的时候, 查看相关的联机帮
助文档便可获得详细的内容 。 下面我们分两部分来对
过滤流加以介绍:首先介绍如何使用 Java系统提供的
过滤流类, 之后, 介绍如何定义自己的过滤流 。
第 7章 Java的输入 /输出流
1,使用过滤流
类 BufferedInputStream/BufferedOutputStream 实现
了带缓冲的过滤流 。 进行网络编程时, 当需要在不同
线程之间传送数据时, 使用缓冲流会得到较高的性能 。
数据的输入 /输出均是首先读入 /写出到缓冲区中, 对
BufferedOutputStream来说, 当数据缓冲区满时, 就可
以写入所连接的输出流, 它所提供的 flush()也可强制缓
冲区的内容在任何时候全部写入输出流 。
第 7章 Java的输入 /输出流
过滤流 DataInputStream,DataOutputStream实现了
接口 DataInput,DataOutput。 它们可以从所连接的输入
流中读取与机器无关的基本数据类型, 也可以向所连
接的输出流写入基本类型数据 。 这一点我们可以从例
7.7看出 。
类 PrintStream所提供的方法 print(),println()可以输
出各种类型数据, 只是后一种方法在输入结束时自动
换行 。 我们可以利用生成该类的实例进行不同数据的
输出 。
下面我们给出应用过滤流的具体实例:
第 7章 Java的输入 /输出流
例 7.13
import java.io.*;
public class FilterIODemo {
public static void main(String args[])
throws IOException {
DataInputStream di=new DataInputStream(new
BufferedInputStream(System.in));
PrintStream po= new PrintStream(new
BufferedOutputStream(System.out));
String s;
第 7章 Java的输入 /输出流
s=di.readLine();
while(!s.equals("see you!"))
{ po.println("Result,"+s);
po.flush();
s=di.readLine(); }
di.close(); po.close(); }
public FilterIODemo() { }
}
第 7章 Java的输入 /输出流
本例中,System.in,System.out是系统的标准输入 /
输出类,这里我们指的是控制台,即键盘和屏幕。该
段程序运行时,用户可不断输入一些字符,输入的字
符会立即显示在屏幕上,直到某一行输入的字符中有
,see you!”时,程序结束运行。结果如图 7.11所示。
第 7章 Java的输入 /输出流
图 7.11
第 7章 Java的输入 /输出流
例 7.14
import java.io.*;
public class PushbackDemo {
public static void main(String args[])
throws IOException{
PushbackInputStream pis=new
PushbackInputStream(System.in);
int data;
data=pis.read();
第 7章 Java的输入 /输出流
while(!Character.isLetter((char)data))
data=pis.read();
pis.unread(data);
while(true){
data=pis.read();
if (Character.isLetter((char)data))
Transcript.print(" "+(char)data); }
}
public PushbackDemo() { }
}
第 7章 Java的输入 /输出流
程序中语句:
while(!Character.isLetter((char)data))
data=pis.read();
pis.unread(data);
的作用是把开头的白字符 (white character)过滤掉,
并且回吐给输入流过滤掉的第一个字符 。
第 7章 Java的输入 /输出流
2,创建自己的过滤流
程序的编写过程中,往往需要实现自己的过滤流
以在读 /写操作时可以对数据进行特定的处理,这时就
必须自己定义继承于 FilterInputStream、
FilterOutputStream的过滤流。如果有必要,需重载方法
read(),write()或其他一些方法,同时应该确保输入 /输
出流一起工作。请看下例。
第 7章 Java的输入 /输出流
例 7.15
import java.io.*;
public class Main {
public static void main(String args[])
throws IOException{
InputStream fin=new FileInputStream("ReadFile.txt");
OutputStreamstdout=new
FileOutputStream(FileDescriptor.out);
第 7章 Java的输入 /输出流
MyInputStream nis=new MyInputStream(fin);
MyOutputStream nos=new MyOutputStream(stdout);
while(true)
nos.write(nis.read()); }
public Main() { }
}
import java.io.*;
public class MyInputStream extends FilterInputStream {
public int read() throws IOException{
第 7章 Java的输入 /输出流
int data=in.read();
StringBuffer sb=new StringBuffer();
while(data!=-1&&!Character.isDigit((char)data))
data=in.read();
while(data!=-1&&Character.isDigit((char)data))
{ sb.append((char)data);
data=in.read(); }
return Integer.parseInt(sb.toString()); }
public MyInputStream(InputStream in) {
super(in); }
第 7章 Java的输入 /输出流
}
import java.io.*;
public class MyOutputStream extends FilterOutputStream {
public void write(int b) throws IOException{
System.out.println("Now in OutputStream is,");
String data=Integer.toString(b);
byte char_array[]=new byte[data.length()];
data.getBytes(0,data.length(),char_array,0);
out.write(char_array); }
public MyOutputStream(OutputStream out) {
super(out); }
}
第 7章 Java的输入 /输出流
本例中,定义了两个类,MyInputStream和
MyOutputStream,它们均从过滤流 FilterInputStream、
FilterOutputStream继承而来,同时重写了父类中的 read()、
write()方法。在类 MyInputStream的 read()方法中,可以读
出文件中所有的数字表达式。因为我们并没有对所有的
例外进行捕获,所以,类 Main中的语句, while(true)”
虽是无限循环,但最终却能由例外
NumberFormatException中断。在类 MyOutputStream中,
由于没有相关的输出字符方法,如 DataInputStream中的
writeChar()方法,因而我们首先把数字转换为一个字节
数组,然后进行输出。假如文件 ReadFile.txt的内容为
第 7章 Java的输入 /输出流
Number,12 45 89 100
Score,65 89 22 88
则最后的输出结果为
System Output,Now in OutputStream is:
System Output,12
System Output,Now in OutputStream is:
System Output,45
System Output,Now in OutputStream is:
第 7章 Java的输入 /输出流
System Output,89
System Output,Now in OutputStream is:
System Output,100
System Output,Now in OutputStream is:
System Output,65
System Output,Now in OutputStream is:
System Output,89
System Output,Now in OutputStream is:
System Output,22
System Output,Now in OutputStream is:
System Output,88
第 7章 Java的输入 /输出流
7.3.5 解析流
类 StreamTokenizer可以通过其对应的语法表把输入
流解析为记号流 。 它通过将输入流中读取的字符与语
法表的属性相对照来加以判断空格, 数字, 字母, 串
引用符 (,"” 或, '” ) 及注释字符等 。 类
StreamTokenizer中的成员变量 ttype用来记录当前所获
得的记号类型, 对应的关系见表 7.4。
第 7章 Java的输入 /输出流
表 7.4 成员变量 ttype属性
属 性 功 能 描 述
TT_WORD 记号为单词, 值保存在 sval中
TT_NUMBER 记号为数字, 值保存在 nval中
TT_EOL 表示到达行尾
TT_EOF 表示输入流的结束
第 7章 Java的输入 /输出流
表 7.4中,sval,nval均为类 StreamTokenizer中的成
员变量,保存解析出的记号的值。类中提供的方法
nextToken()从输入流中读入下一个标记,该方法返回
记号的类型,如表 7.4示,同时把记号的值保存在相应
的变量中。关于其他的用法,读者可查阅相关的文档。
下面我们利用类 StreamTokenizer来改写例 7.15。
第 7章 Java的输入 /输出流
例 7.16
import java.io.*;
public class TokenDemo {
public static void main(String args[])
throws IOException {
int sum=0;
InputStream in=new FileInputStream("ReadFile.txt");
StreamTokenizer sin=new StreamTokenizer(in);
sin.eolIsSignificant(true);
while(sin.nextToken()!=sin.TT_EOF){
第 7章 Java的输入 /输出流
if(sin.ttype==sin.TT_WORD)
{ Transcript.print(sin.sval+","); }
else if(sin.ttype==sin.TT_NUMBER)
{ Transcript.print(sin.nval+","); sum+=sin.nval; }
else if(sin.ttype==sin.TT_EOL)
Transcript.println("Total,"+sum); }
}
public TokenDemo() { }
}
如果文件 ReadFile.txt的内容与上例相同的话,则输出结
果如图 7.12所示。
第 7章 Java的输入 /输出流
图 7.12
第 7章 Java的输入 /输出流
7.3.6 Java命令行参数的使用
熟悉 C/C++的读者应该对命令行参数并不陌生, 与
它们相似, Java中的命令行参数是在命令行中执行某
个 Java程序时, 直接将一些参数传送给程序, 然后通
过特殊的处理, 在程序中获取这些参数的值并直接运
用到程序的执行过程中 。 当然, 我们的 Java调式环境
为 JBuilder3.0,此时可通过运行 (run)菜单的 Parameters
选项进行参数设置, 同样也能达到参数传送的目的,
效果是一样的 。
第 7章 Java的输入 /输出流
与 C/C++稍微不同的是, 在 Java中, 命令行参数的
个数可从 args.length中直接获取, 传入的第一个参数就
是第一个命令行参数, 而不是程序名本身 。 下面我们
给出一个简单的例子对命令行参数加以说明 。 在例 7.17
中, 通过参数传递的方式输入任意个整数值, 然后比
较其大小, 根据排序后结果进行输出 。
第 7章 Java的输入 /输出流
例 7.17
public class ParaComDemo {
public static void main(String args[]) {
int num[]=new int[args.length];
for(int i=0;i<num.length;i++)
num[i]=Integer.parseInt(args[i]);
Transcript.println("The array before sorted is,");
for(int i=0;i<num.length;i++)
Transcript.print(" "+num[i]); Transcript.println();
第 7章 Java的输入 /输出流
for(int i=0;i<num.length-1;i++)
for(int j=i+1;j<num.length;j++)
if(num[i]<num[j])
{ int temp=num[i]; num[i]=num[j]; num[j]=temp; }
Transcript.println("The array after sorted is,");
for(int i=0;i<num.length;i++)
Transcript.print(" "+num[i]); }
public ParaComDemo() { }
}
第 7章 Java的输入 /输出流
如果我们在参数的设置中, 取 23 45 99 45 -11 939
100 -233,则程序的运行结果如图 7.13所示 。
图 7.13