1/0流的典型使用方式
1.缓冲输入文件
package com.java.io; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; public class BufferedInputFile { public static String read(String pathname) throws IOException { BufferedReader in = new BufferedReader(new FileReader(new File(pathname))); String s; StringBuilder sb = new StringBuilder(); while((s = in.readLine())!=null){ sb.append(s + " ") ; } in.close(); return sb.toString(); } public static void main(String[] args) throws IOException { System.out.print(read("src/com/java/io/BufferedInputFile.java")); } }
console将当前的BufferedInputFile.java文件原封不动的打印出来;
2 从内存输入
package com.java.io; import java.io.IOException; import java.io.StringReader; public class MemoryInput { public static void main(String[] args) throws IOException{ StringReader in = new StringReader(BufferedInputFile.read("src/com/java/io/MemoryInput.java")); int c; //read是以int形式返回下一字节,因此必须类型转换为char才能正确打印。 //read()一个字符一个字符的读的。 Reads a single character. while((c=in.read()) != -1){ System.out.print((char) c); } } }
3 格式化的内存输入
要读取格式化数据 可以使用DataInputStream 它是一个面向字节的I/O类(不是面向字符的)。
因此我们必须使用InputSteam类而不是Reader类,当然我们可以用InputStream以字节的形式读取任何数据(例如一个文件)
不过,在这里使用的是字符串。
package com.java.io; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; public class FormattedMemoryInput { public static void main(String[] args) throws IOException{ try { DataInputStream in = new DataInputStream( new ByteArrayInputStream( BufferedInputFile.read("src/com/java/io/FormattedMemoryInput.java").getBytes())); while(true){ byte b = in.readByte(); System.out.print((char) b); } } catch (EOFException e) { System.err.println("End of stream") ; } } }
使用available方法查看还有多少可供读取的字符:
package com.java.io; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.FileInputStream; import java.io.IOException; public class TestEOF { public static void main(String[] args) throws IOException{ DataInputStream in = new DataInputStream( new BufferedInputStream( new FileInputStream("src/com/java/io/TestEOF.java"))); while(in.available() != 0){ System.out.print((char)in.readByte()); } } }
注意. available的工作方式会随着所读取的媒介类型的不同而有所不同,学面意思就是"在没有阻塞的情况下所能读取的字节数“。
对于文件,这意味着整个文件,但是对于不问类型的流,可能就不是这样的,因此要谨慎使用。
4 基本的文件输出:
FileWriter对象可以向文件写入数据,首先,创建一个与指定文件连接的FileWriter。
实际上,我们通常会用BufferedWriter将其包装起来用以缓冲输出〈尝式移除此包装来感受对性能的影响--缓冲往往能显著的增加IO操作
的性能)。在本例中,为了提供格式化机制,它被装饰成PrintWriter,按照这种方式创建的数据文件可作为普通文本文件读取。
package com.java.io; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.StringReader; /** * 一旦读完输入数据流, readLine()会返回null。 * 我们可以看到要为out显式调用c1ose.如果我们不为所有的输出文件调用close() * 就会发现缓冲区内容不会被刷新清空 那么它们也就不完整。 */ public class BasicFileOutput { static String file = "BasicFileOutput.out"; public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader( new StringReader( BufferedInputFile.read("src/com/java/io/BasicFileOutput.java"))); PrintWriter out = new PrintWriter( new BufferedWriter(new FileWriter(file))); int lineCount = 1; String s; while((s = in.readLine()) != null ){ out.println(lineCount++ + ": " + s); } out.close(); System.out.println(BufferedInputFile.read(file)); } }
文本文件输出的快捷方式
Java SE5在PrintWriter 中添加了-个辅助构造器,使得你不必在每次希望创建文本文件并向其中写入时,都去执行所有的装饰工作。下面是用这种快捷方式重写的BasicFileOutput.java :
package com.java.io; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import java.io.StringReader; public class FileOutputShortcut { static String file = "FileOutputShortcut.out"; public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader( new StringReader(BufferedInputFile.read("src/com/java/io/FileOutputShortcut.java"))); PrintWriter out = new PrintWriter(file); int lineCount = 1; String s; while((s = in.readLine()) != null ){ out.println(lineCount++ + ": " + s); } out.close(); System.out.println(BufferedInputFile.read(file)); } }
5 存储和恢复数据
PrintWriter可以对数据进行格式化,以便人们的阅读。但是为了输出可供另一个"流"恢
复的数据,我们需要用DataOutputStream写入数据,并用DatalnputStream恢复数据.
package com.java.io; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class StoringAndRecoveringData { public static void main(String[] args) throws IOException { 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()); // Only readUTF() will recover the // Java-UTF String properly: System.out.println(in.readUTF()); System.out.println(in.readDouble()); System.out.println(in.readUTF()); } }
当我们使用DataOutputStream肘,写字符串并且让DataInputStream能够恢复它的唯一可靠的做法就是使用UTF-8编码
在这个示例中是用writeUTF和readUTF来实现的。
有了writeUTFO 和readUTFO ,我们就可以用DataOutputStream把字符串和其他数据类型相混合,我们知道字符串完全可以作为Unicode来存储,
并且可以很容易地使用DataInputStream来恢复它。
6 读写随机访问文件
使用RandomAccessFile , 类似于组合使用了DataInputStream和DataOutputStream (因为
它实现了相同的接曰: DataInput和DataOutput) 。另外我们可以看到,利用seek()可以在文件中
到处移动,并修改文件中的某个值。
package com.java.io; import java.io.IOException; import java.io.RandomAccessFile; public class UsingRandomAccessFile { static String file = "rtest.dat"; static void display() throws IOException { //我们可指定以"只读"( r ) 方式或"读写" ( rw ) 方式打开一个RandomAccessFile文件。 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"); //因为double总是8字节长,所以为了用Seek查找第5个双精度值,你只需用5*8来产生查找位置。 rf.seek(5*8); rf.writeDouble(47.0001); rf.close(); display(); } }
7 管道流
PipedlnputStream 、PipedOutputStream 、PipedReader及PipedWriter在本章只是简单地提到。但这并不表明它们没有什么用处,它们的价值只有在我们开始理解多钱程之后才会显现,因为管道流用于任务之间的通信。这些在第2l:'!1旨'会用一个示例进行讲述。
文件读写的实用工具
-个很常见的程序化任务就是读取文件到内存,修改,然后再写出。Java 110类库的问题之19361
一就是:它需要编写相当多的代码去执行这些常用操作一一设有任何基本的帮助功能可以为我们
做这一切。更糟糕的是,装饰器会使得要记住如何打开文件变成一件相当困难的事。因此,在
我们的类库中添加帮助类就显得相当有意义,这样就可以很容易地为我们完成这些基本任务。
下面的TextFile类在本书前面的示例中就已经被用来简化对文件的读写操作了。它包含的
static方法可以像简单字符串那样读写文本文件,并且我们可以创建一个TextFile对象,它用一
个ArrayList来保存文件的若干行(如此,当我们操纵文件内容时,就可以使用ArrayList的所
有功能)。
// Static functions for reading and writing text files as // a single string, and treating a file as an ArrayList. package com.java.io; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.TreeSet; public class TextFile extends ArrayList<String> { // Read a file as a single 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); sb.append(" "); } } finally { in.close(); } } catch(IOException e) { throw new RuntimeException(e); } return sb.toString(); } // Write a single file in one method call: 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); } } 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); } } // Read a file, split by any regular expression: public TextFile(String fileName, String splitter) { super(Arrays.asList(read(fileName).split(splitter))); // Regular expression split() often leaves an empty // String at the first position: if(get(0).equals("")){ remove(0); } } // Normally read by lines: public TextFile(String fileName) { this(fileName, " "); } public static void main(String[] args) { String file = read("src/com/java/io/TextFile.java"); write("test.txt", file); TextFile text = new TextFile("test.txt"); text.write("test2.txt"); // Break into unique sorted list of words: //TreeSet是有序的set //\W+以非单词分割,得到的都是单词list,TextFile本身就是一个ArrayList,组成了TextFile; //TreeSet的构造函数 TreeSet<集合> TreeSet<String> words = new TreeSet<String>(new TextFile("src/com/java/io/TextFile.java", "\W+")); // Display the capitalized words: //打印出TreeSet中小于'a'的,就是打印出大写字母的 System.out.println(words.headSet("a")); } }
输出:
[0, ArrayList, Arrays, Break, BufferedReader, Display, File, FileReader, IOException, List, Normally, PrintWriter, Read, Regular, RuntimeException, Static, String, StringBuilder, System, TextFile, TreeSet, W, Write]
注意
在任何打开文件的代码在finaUy子句中,作为防卫措施都添加了对文件的closeO调用,以保证文件将会被正确关闭。
因为这个类希望将读取和写入文件的过程简单化,因此所有的IOException都被转型为RuntimeException ,
因此用户不必使用try-catch语句块。但是,你可能需要创建另一种版本将IOException传递给调用者。
另一种解决读取文件问题的方法是使用在Java SE5中引入的java.util.scanner类.但是,这
只能用于读取文件,而不能用于写入文件,并且这个工具(你会注意到它不在java.io包中)主
要是设计用来创建编程语言的扫描器或"小语言"的。
读取二进制文件
这个工具与TextFile类似,因为它简化了读取二进制文件的过程:
package com.java.io; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class BinaryFile { public static byte[] read(File bFile) throws IOException{ BufferedInputStream bf = new BufferedInputStream( new FileInputStream(bFile)); try { byte[] data = new byte[bf.available()]; bf.read(data); return data; } finally { bf.close(); } } public static byte[] read(String bFile) throws IOException { return read(new File(bFile).getAbsoluteFile()); } }
其中一个重载方法接受File参数,第二个重毒草方法接受表示文件名的String参数。这两个方
法都返回产生的byte数组。availableO方法被用来产生恰当的数组尺寸,井且readO方法的特定
的重载版本填充了这个数组。
read() : 从输入流中读取数据的下一个字节,返回0到255范围内的int字节值。如果因为已经到达流末尾而没有可用的字节,则返回-1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。
read(byte[] b) : 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。
如果 b 的长度为 0,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b 中。
将读取的第一个字节存储在元素 b[0] 中,下一个存储在 b[1] 中,依次类推。读取的字节数最多等于b 的长度。设 k 为实际读取的字节数;这些字节将存储在 b[0] 到 b[k-1] 的元素中,不影响 b[k] 到b[b.length-1] 的元素。
由帮助文档中的解释可知,read()方法每次只能读取一个字节,所以也只能读取由ASCII码范围内的一些字符。这些字符主要用于显示现代英语和其他西欧语言。而对于汉字等unicode中的字符则不能正常读取。只能以乱码的形式显示。
对于read()方法的上述缺点,在read(byte[] b)中则得到了解决,就拿汉字来举例,一个汉字占有两个字节,则可以把参数数组b定义为大小为2的数组即可正常读取汉字了。当然b也可以定义为更大,比如如果b=new byte[4]的话,则每次可以读取两个汉字字符了,但是需要注意的是,如果此处定义b 的大小为3或7等奇数,则对于全是汉字的一篇文档则不能全部正常读写了。
--------------