Java I/O中I/O流的典型使用方式详解
前言
尽管可以通过不同的方式组合IO流类,但我们可能也就只用到其中的几种组合。下面的例子可以作为典型的IO用法的基本参考。在这些示例中,异常处理都被简化为将异常传递给控制台,但是这只有在小型示例和工具中才适用。在代码中,你需要考虑更加复杂的错误处理方式。
同样,本文会包括如下几个方面:
- 缓冲输入文件
- 从内存输入
- 格式化的内存输入
- 基本的文件输出
- 存储和恢复数据
- 读写随机访问文件
- 实用工具
- 总结
1.缓冲输入文件
如果想要打开一个文件用于字符输入,可以使用以String或File对象作为文件名的FileReader。为了提高速度,我们可以对那个文件进行缓冲,那么我们需要将所产生的引用传给一个BufferedReader构造器。通过使用其readLine()方法来逐行读取文件,当readLine()返回null时,就到了文件末尾。
publicclassBufferedInputFile{ publicstaticStringread(StringfileName)throwsException{ BufferedReaderbr=newBufferedReader(newFileReader(fileName)); StringBuilderstr=newStringBuilder(); Stringtemp=null; while((temp=br.readLine())!=null){ str.append(temp+"\n"); } br.close(); returnstr.toString(); } publicstaticvoidmain(String[]args){ try{ System.out.println(BufferedInputFile.read("pom.xml")); }catch(Exceptione){ e.printStackTrace(); } } }
文件的全部内容都累积在字符串str中,最后记得调用close()来关闭流。
2.从内存输入
在下面的示例中,从上面的BufferedInputFile.read()读入的String结果被用来创建一个StringReader。然后调用read()每次读取一个字符,并把它打印到控制台。
publicclassMemoryInput{ publicstaticvoidmain(String[]args){ try{ StringReadersr=newStringReader(BufferedInputFile.read("pom.xml")); intc; while((c=sr.read())!=-1){ System.out.print((char)c); } }catch(Exceptione){ } } }
需要注意的是read()是以int形式返回下一个字节,因此必须将类型强转为char才能显示正确结果。
3.格式化的内存输入
要读取格式化数据,可以使用DataInputStream,它是一个面向字节的I/O类,我们可以用InputStream以字节的形式读取任何数据。
publicclassFormattedMemoryInput{ publicstaticvoidmain(String[]args){ try{ DataInputStreamdi=newDataInputStream(newByteArrayInputStream(BufferedInputFile.read("pom.xml").getBytes())); while(di.available()!=0){ System.out.print((char)di.readByte()); } }catch(Exceptione){ e.printStackTrace(); } } }
这里需要注意必须为ByteArrayInputStream的构造函数提供字节数组,而ByteArrayInputStream传递给DataInputStream之后进行了一次“装饰”,可以进行格式化输入(比如直接读取int、double等类型),这里我们只是通过readByte读取单个字节,调用该方法时任何字节的值都是合法的结果,因此返回值是不能用来检测输入是否结束,这里我们使用available()方法查看还有多少可供存取的字符来判断是否结束。
4.基本的文件输出
FileWriter对象可以向文件写入数据,通常会用BufferedWriter将其包装起来用以缓冲输出以提高性能。在本例中为了提供格式化机制,将其装饰成PrintWriter:
publicclassBasicFileOutput{ staticStringfile="BasicFileOutput.out"; publicstaticvoidmain(String[]args){ try{ BufferedReaderin=newBufferedReader(newStringReader(BufferedInputFile.read("pom.xml"))); PrintWriterout=newPrintWriter(newBufferedWriter(newFileWriter(file))); Stringtemp; intcount=0; while((temp=in.readLine())!=null){ out.println(count+++temp); } in.close(); out.close(); System.out.println(BufferedInputFile.read(file)); }catch(Exceptione){ e.printStackTrace(); } } }
这里在读取BasicFileOutput.out的内容之前,先调用了out的close()将其关闭,一方面是因为流用完之后需要及时关闭以节省资源,另一方面这里用到了缓冲区,如果不为所有的输出文件调用close(),缓冲区的内容可能不会刷新清空,这样可能导致信息不完整。
另外JavaSE5在PrintWriter中添加了一个辅助构造器,可以很方便根据文件名直接构造一个PrintWriter而不用执行一系列的装饰工作:
PrintWriterout=newPrintWriter(file);
5.存储和恢复数据
PrintWriter可以对数据进行格式化,以便阅读。但是为了输出可供另一个“流”恢复的数据,我们需要用DataOutputStream写入数据,并用DataInputStream恢复数据。当然,这些流可以是任何形式,在下面的例子中使用的是一个文件。
publicclassStoringAndRecoveringData{ publicstaticvoidmain(String[]args){ try{ DataOutputStreamout=newDataOutputStream(newBufferedOutputStream(newFileOutputStream("data.txt"))); out.writeDouble(3.14159); out.writeUTF("Thatwaspi"); out.writeDouble(1.41413); out.writeUTF("Squarerootof2"); out.close(); DataInputStreamin=newDataInputStream(newBufferedInputStream(newFileInputStream("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(Exceptione){ e.printStackTrace(); } } }
使用DataOutputStream写入数据,Java可以保证我们可以使用DataInputStream准确地读取数据--无论读和写数据的平台多么不同。当我们使用DataOutputStream时,写字符串并且让DataInputStream能够恢复它的唯一可靠的做法就是使用UTF-8编码,在这个例子中是靠writeUTF()和readUTF()来实现的。
writeDouble()和readDouble()方法能够写入和恢复double类型的数据。对于其他类型的数据,也有类似的方法用于读写。但是为了保证所有的读写方法都能够正常工作,我们必须知道流中数据项所在的确切位置,因为极有可能将保存的double数据作为一个简单的字节序列、char或其他类型读入。
6.读写随机访问文件
使用RandomAccessFile,类似于组合使用了DataInputStream和DataOutputStream,可以同时对一个文件执行读写操作,同时可以利用seek()在文件中到处移动,非常方便,关于RandomAccessFile的详细用法,前面有专门写过<
但是在使用RandomAccessFile时,你需要知道文件的排版,这样才能正确地操作它,RandomAccessFile拥有读取基本类型和UTF-8字符串的各种具体方法。
publicclassUsingRandomAccessFile{ staticStringfile="rtest.dat"; staticvoiddisplay()throwsIOException{ RandomAccessFilerf=newRandomAccessFile(file,"r"); for(inti=0;i<7;i++){ System.out.println("Value"+i+":"+rf.readDouble()); } System.out.println(rf.readUTF()); rf.close(); } publicstaticvoidmain(String[]args)throwsIOException{ RandomAccessFilerf=newRandomAccessFile(file,"rw"); for(inti=0;i<7;i++){ rf.writeDouble(i*1.414); } rf.writeUTF("Theendofthefile"); rf.close(); display(); rf=newRandomAccessFile(file,"rw"); rf.seek(5*8); rf.writeDouble(47.0001); rf.close(); display(); } }
我们通过writeDouble()方法往文件中写入Double类型数据并通过readDouble()方法来读取,这就是我们需要直到排版的原因,如果读取的不是Double类型的数据有可能出现不是我们想要的结果。
7.实用工具
到这里我们学习了多种I/O流的典型用法,比如缓冲输入文件、从内存输入、基本的文件输出、存储和恢复数据、随机读写文件,这些都是JavaI/O流比较典型的用法。这里我们发现读取文件、修改、在写出是一个很常见的程序化的任务,但是JavaI/O类库的设计有一个问题,就是我们需要编写很多代码来实现这些操作,要记住如何打开文件是一件优点困难的事情。因此,下面是收集的一些帮助类,可以很容易为我们完成这些基本任务,记录在这里,方便以后查看。
这里收集了两个工具:
- 一个是TextFile,帮助我们读取和写入文件;
- 另一个是BinaryFile,帮助我们简化二进制文件的读取。
7.1读取文件
TextFile类包含的static方法可以像简单字符串那样读写文本文件,并且我们可以创建一个TextFile对象,它用一个ArrayList来保存文件的若干行,好处是在我们操纵文件内容时可以使用ArrayList的所有功能。
publicclassTextFileextendsArrayList{ //将文件读取到一行字符串中 publicstaticStringread(StringfileName){ StringBuildersb=newStringBuilder(); try{ BufferedReaderin=newBufferedReader(newFileReader(newFile(fileName).getAbsoluteFile())); try{ Strings;; while((s=in.readLine())!=null){ sb.append(s).append("\n"); } }finally{ in.close(); } }catch(IOExceptione){ thrownewRuntimeException(e); } returnsb.toString(); } //单次调用将一个字符串写入一个文件 publicstaticvoidwrite(StringfileName,Stringtext){ try{ PrintWriterout=newPrintWriter(newFile(fileName).getAbsoluteFile()); try{ out.print(text); }finally{ out.close(); } }catch(IOExceptione){ thrownewRuntimeException(e); } } //读取文件,并通过正则表达式将其分离,保存在List中 publicTextFile(StringfileName,Stringsplitter){ super(Arrays.asList(read(fileName).split(splitter))); //因为split()方法有时会在返回的数组第一个位置产生一个空字符串 if(get(0).equals("")) remove(0); } //常规的分行读取 publicTextFile(StringfileName){ this(fileName,"\n"); } //将该TextFile中的内容分行写入指定文件中 publicvoidwrite(StringfileName){ try{ PrintWriterout=newPrintWriter(newFile(fileName).getAbsoluteFile()); try{ for(Stringitem:this){ out.println(item); } }finally{ out.close(); } }catch(IOExceptione){ thrownewRuntimeException(e); } } //简单验证一下 publicstaticvoidmain(String[]args){ Stringfile=read("TextFile.java"); write("test.txt",file); TextFiletext=newTextFile("test.txt"); text.write("test2.txt"); TreeSet words=newTreeSet (newTextFile("TextFile.java","\\W+")); System.out.println(words.headSet("a")); } }
这里利用静态的read()方法将文件读取到一个字符串中,再用静态的write()方法将其写入到文件中。然后将新写入的文件作为构造参数构造一个TestFile对象,利用其List的特性,将其内容写入文件test2中。这个类的作用是帮我们读取文件,可以通过静态的read方法读取到一个字符串中,也可以通过构造器读取文件到一个TextFile对象中。
7.2读取二进制文件
publicclassBinaryFile{ publicstaticbyte[]read(FilebFile)throwsIOException{ BufferedInputStreambf=newBufferedInputStream(newFileInputStream()); try{ byte[]data=newbyte[bf.available()]; br.read(data); returndata; }finally{ bf.close(); } } publicstaticbyte[]read(StringbFile)throwsIOException{ returnread(newFile(bFile).getAbsoluteFile()); } }
8.总结
本文没有总结什么新的知识点,只是总结了一些JavaI/O的常见用法比如缓冲输入文件、从内存输入、基本的文件输出、存储和恢复数据、随机读写文件等,并且搜集了两个工具类用来帮助我们读写文件读取二进制文件,以提高些代码的效率。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。