欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java I/O中I/O流的典型使用方式详解

程序员文章站 2022-05-12 13:21:19
前言 尽管可以通过不同的方式组合io流类,但我们可能也就只用到其中的几种组合。下面的例子可以作为典型的io用法的基本参考。在这些示例中,异常处理都被简化为将异常传递给控制...

前言

尽管可以通过不同的方式组合io流类,但我们可能也就只用到其中的几种组合。下面的例子可以作为典型的io用法的基本参考。在这些示例中,异常处理都被简化为将异常传递给控制台,但是这只有在小型示例和工具中才适用。在代码中,你需要考虑更加复杂的错误处理方式。

同样,本文会包括如下几个方面:

  • 缓冲输入文件
  • 从内存输入
  • 格式化的内存输入
  • 基本的文件输出
  • 存储和恢复数据
  • 读写随机访问文件
  • 实用工具
  • 总结

1. 缓冲输入文件

如果想要打开一个文件用于字符输入,可以使用以string或file对象作为文件名的filereader。为了提高速度,我们可以对那个文件进行缓冲,那么我们需要将所产生的引用传给一个bufferedreader构造器。通过使用其readline()方法来逐行读取文件,当readline()返回null时,就到了文件末尾。

public class bufferedinputfile {
public static string read(string filename) throws exception {
bufferedreader br = new bufferedreader(new filereader(filename));
stringbuilder str = new stringbuilder();
string temp = null;
while((temp = br.readline()) != null) {
str.append(temp + "\n");
}
br.close();
return str.tostring();
}
public static void main(string[] args) {
try {
system.out.println(bufferedinputfile.read("pom.xml"));
}catch(exception e) {
e.printstacktrace();
}
}
}

文件的全部内容都累积在字符串str中,最后记得调用close()来关闭流。

2. 从内存输入

在下面的示例中,从上面的bufferedinputfile.read()读入的string结果被用来创建一个stringreader。然后调用read()每次读取一个字符,并把它打印到控制台。

public class memoryinput {
public static void main(string[] args) {
try {
stringreader sr = new stringreader(bufferedinputfile.read("pom.xml"));
int c;
while((c = sr.read()) != -1) {
system.out.print((char)c);
}
}catch(exception e) {
}
}
}

需要注意的是read()是以int形式返回下一个字节,因此必须将类型强转为char才能显示正确结果。

3. 格式化的内存输入

要读取格式化数据,可以使用datainputstream,它是一个面向字节的i/o类,我们可以用inputstream以字节的形式读取任何数据。

public class formattedmemoryinput {
public static void main(string[] args) {
try {
datainputstream di = new datainputstream(new bytearrayinputstream(bufferedinputfile.read("pom.xml").getbytes()));
while(di.available() != 0) {
system.out.print((char)di.readbyte());
}
}catch (exception e) {
e.printstacktrace();
}
}
}

这里需要注意必须为bytearrayinputstream的构造函数提供字节数组,而bytearrayinputstream传递给datainputstream之后进行了一次“装饰”,可以进行格式化输入(比如直接读取int、double等类型),这里我们只是通过readbyte读取单个字节,调用该方法时任何字节的值都是合法的结果,因此返回值是不能用来检测输入是否结束,这里我们使用available()方法查看还有多少可供存取的字符来判断是否结束。

4. 基本的文件输出

filewriter对象可以向文件写入数据,通常会用bufferedwriter将其包装起来用以缓冲输出以提高性能。在本例中为了提供格式化机制,将其装饰成printwriter:

public class basicfileoutput {
static string file = "basicfileoutput.out";
public static void main(string[] args) {
try {
bufferedreader in = new bufferedreader(new stringreader(bufferedinputfile.read("pom.xml")));
printwriter out = new printwriter(new bufferedwriter(new filewriter(file)));
string temp;
int count = 0;
while((temp = in.readline()) != null) {
out.println(count++ + temp);
}
in.close();
out.close();
system.out.println(bufferedinputfile.read(file));
}catch(exception e) {
e.printstacktrace();
} 
}
}

这里在读取basicfileoutput.out的内容之前,先调用了out的close()将其关闭,一方面是因为流用完之后需要及时关闭以节省资源,另一方面这里用到了缓冲区,如果不为所有的输出文件调用close(),缓冲区的内容可能不会刷新清空,这样可能导致信息不完整。

另外java se5在printwriter中添加了一个辅助构造器,可以很方便根据文件名直接构造一个printwriter而不用执行一系列的装饰工作:

printwriter out = new printwriter(file);

5. 存储和恢复数据

printwriter可以对数据进行格式化,以便阅读。但是为了输出可供另一个“流”恢复的数据,我们需要用dataoutputstream写入数据,并用datainputstream恢复数据。当然,这些流可以是任何形式,在下面的例子中使用的是一个文件。

public class storingandrecoveringdata {
public static void main(string[] args) {
try {
dataoutputstream out = new dataoutputstream(new bufferedoutputstream(new fileoutputstream("data.txt")));
out.writedouble(3.14159);
out.writeutf("that was pi");
out.writedouble(1.41413);
out.writeutf("square root of 2");
out.close();
datainputstream in = new datainputstream(new bufferedinputstream(new fileinputstream("data.txt")));
system.out.println(in.readdouble());
system.out.println(in.readutf());
system.out.println(in.readdouble());
system.out.println(in.readutf());
in.close();
}catch(exception e) {
e.printstacktrace();
}
}
}

使用dataoutputstream写入数据,java可以保证我们可以使用datainputstream准确地读取数据--无论读和写数据的平台多么不同。当我们使用dataoutputstream时,写字符串并且让datainputstream能够恢复它的唯一可靠的做法就是使用utf-8编码,在这个例子中是靠writeutf()和readutf()来实现的。

writedouble()和readdouble()方法能够写入和恢复double类型的数据。对于其他类型的数据,也有类似的方法用于读写。但是为了保证所有的读写方法都能够正常工作,我们必须知道流中数据项所在的确切位置,因为极有可能将保存的double数据作为一个简单的字节序列、char或其他类型读入。

6. 读写随机访问文件

使用randomaccessfile,类似于组合使用了datainputstream和dataoutputstream,可以同时对一个文件执行读写操作,同时可以利用seek()在文件中到处移动,非常方便,关于randomaccessfile的详细用法,前面有专门写过<<java i/o系统:file和randomaccessfile>>。

但是在使用randomaccessfile时,你需要知道文件的排版,这样才能正确地操作它,randomaccessfile拥有读取基本类型和utf-8字符串的各种具体方法。

public class usingrandomaccessfile{
static string file = "rtest.dat";
static void display() throws ioexception{
randomaccessfile rf = new randomaccessfile(file,"r");
for(int i = 0; i < 7; i++){
system.out.println("value " + i + ": " + rf.readdouble());
}
system.out.println(rf.readutf());
rf.close();
}
public static void main(string[] args) throws ioexception{
randomaccessfile rf = new randomaccessfile(file,"rw");
for(int i = 0; i < 7; i++){
rf.writedouble(i*1.414);
}
rf.writeutf("the end of the file");
rf.close();
display();
rf = new randomaccessfile(file,"rw");
rf.seek(5*8);
rf.writedouble(47.0001);
rf.close();
display();
}
}

我们通过writedouble()方法往文件中写入double类型数据并通过readdouble()方法来读取,这就是我们需要直到排版的原因,如果读取的不是double类型的数据有可能出现不是我们想要的结果。

7. 实用工具

到这里我们学习了多种i/o流的典型用法,比如缓冲输入文件、从内存输入、基本的文件输出、存储和恢复数据、随机读写文件,这些都是java i/o流比较典型的用法。这里我们发现读取文件、修改、在写出是一个很常见的程序化的任务,但是java i/o类库的设计有一个问题,就是我们需要编写很多代码来实现这些操作,要记住如何打开文件是一件优点困难的事情。因此,下面是收集的一些帮助类,可以很容易为我们完成这些基本任务,记录在这里,方便以后查看。

这里收集了两个工具:

  • 一个是textfile,帮助我们读取和写入文件;
  • 另一个是binaryfile,帮助我们简化二进制文件的读取。

7.1 读取文件

textfile类包含的static方法可以像简单字符串那样读写文本文件,并且我们可以创建一个textfile对象,它用一个arraylist来保存文件的若干行,好处是在我们操纵文件内容时可以使用arraylist的所有功能。

public class textfile extends arraylist<string>{
// 将文件读取到一行字符串中
public static string read(string filename){
stringbuilder sb = new stringbuilder();
try{
bufferedreader in = new bufferedreader(new filereader(new file(filename).getabsolutefile()));
try{
string s;;
while((s = in.readline()) != null){
sb.append(s).append("\n");
}
}finally{
in.close();
}
}catch (ioexception e){
throw new runtimeexception(e);
}
return sb.tostring();
}
// 单次调用将一个字符串写入一个文件
public static void write(string filename,string text){
try{
printwriter out = new printwriter(new file(filename).getabsolutefile());
try{
out.print(text);
}finally{
out.close();
}
}catch(ioexception e){
throw new runtimeexception(e);
}
}
// 读取文件,并通过正则表达式将其分离,保存在list中
public textfile(string filename,string splitter){
super(arrays.aslist(read(filename).split(splitter)));
// 因为split()方法有时会在返回的数组第一个位置产生一个空字符串
if(get(0).equals(""))
remove(0);
}
// 常规的分行读取
public textfile(string filename){
this(filename,"\n");
}
// 将该textfile中的内容分行写入指定文件中
public void write(string filename){
try{
printwriter out = new printwriter(new file(filename).getabsolutefile());
try{
for(string item : this){
out.println(item);
}
}finally{
out.close();
}
}catch(ioexception e){
throw new runtimeexception(e);
}
}
// 简单验证一下
public static void main(string[] args){
string file = read("textfile.java");
write("test.txt",file);
textfile text = new textfile("test.txt");
text.write("test2.txt");
treeset<string> words = new treeset<string>(new textfile("textfile.java","\\w+"));
system.out.println(words.headset("a"));
}
}

这里利用静态的read()方法将文件读取到一个字符串中,再用静态的write()方法将其写入到文件中。然后将新写入的文件作为构造参数构造一个testfile对象,利用其list的特性,将其内容写入文件test2中。这个类的作用是帮我们读取文件,可以通过静态的read方法读取到一个字符串中,也可以通过构造器读取文件到一个textfile对象中。

7.2 读取二进制文件

public class binaryfile{
public static byte[] read(file bfile)throws ioexception{
bufferedinputstream bf = new bufferedinputstream(new fileinputstream());
try{
byte[] data = new byte[bf.available()];
br.read(data);
return data;
}finally{
bf.close();
}
}
public static byte[] read(string bfile)throws ioexception{
return read(new file(bfile).getabsolutefile());
}
}

8. 总结

本文没有总结什么新的知识点,只是总结了一些java i/o的常见用法比如缓冲输入文件、从内存输入、基本的文件输出、存储和恢复数据、随机读写文件等,并且搜集了两个工具类用来帮助我们读写文件读取二进制文件,以提高些代码的效率。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。