尽管可以通过不同的方式组合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的常见用法比如缓冲输入文件、从内存输入、基本的文件输出、存储和恢复数据、随机读写文件等,并且搜集了两个工具类用来帮助我们读写文件读取二进制文件,以提高些代码的效率。

Java I/O系统学习系列三:I/O流的典型使用方式的更多相关文章

  1. Java I/O系统学习系列二:输入和输出

    编程语言的I/O类库中常使用流这个抽象概念,它代表任何有能力产出数据的数据源对象或者是有能力接收数据的接收端对象.“流”屏蔽了实际的I/O设备中处理数据的细节. 在这个系列的第一篇文章:<< ...

  2. Java I/O系统学习系列一:File和RandomAccessFile

    I/O系统即输入/输出系统,对于一门程序语言来说,创建一个好的输入/输出系统并非易事.因为不仅存在各种I/O源端和想要与之通信的接收端(文件.控制台.网络链接等),而且还需要支持多种不同方式的通信(顺 ...

  3. Java I/O系统学习系列五:Java序列化机制

    在Java的世界里,创建好对象之后,只要需要,对象是可以长驻内存,但是在程序终止时,所有对象还是会被销毁.这其实很合理,但是即使合理也不一定能满足所有场景,仍然存在着一些情况,需要能够在程序不运行的情 ...

  4. (Struts2学习系列三)Struts2动态方法调用:通配符方式

    更改src/struts2.xml的代码: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE ...

  5. MyBatis学习系列三——结合Spring

    目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring MyBatis在项目中应用一般都要结合Spring,这一章主要把MyBat ...

  6. Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream

    Java IO流学习总结三:缓冲流-BufferedInputStream.BufferedOutputStream 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/ ...

  7. scrapy爬虫学习系列三:scrapy部署到scrapyhub上

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...

  8. DocX开源WORD操作组件的学习系列三

    DocX学习系列 DocX开源WORD操作组件的学习系列一 : http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_sharp_001_docx1.htm ...

  9. RabbitMQ学习系列三-C#代码接收处理消息

    RabbitMQ学习系列三:.net 环境下 C#代码订阅 RabbitMQ 消息并处理 http://www.80iter.com/blog/1438251320680361 http://www. ...

随机推荐

  1. STM32F429驱动SDRAM

    1 SDRAM控制原理 1.1 SDRAM信号线 1.2 SDRAM地址线 SDRAM包含有“A”以及“BA”两类地址线: A:行(Row)与列(Column)共用的地址线 BA:独立的用于指定SDR ...

  2. (八)OpenStack---M版---双节点搭建---Cinder安装和配置

    ↓↓↓↓↓↓↓↓视频已上线B站↓↓↓↓↓↓↓↓ >>>>>>传送门 1.创建数据库并授权 2.获得admin凭证执行管理员命令并创建服务证书 3.创建块存储设备AP ...

  3. Unity检视面板的继承方法研究 (一)

    对于检视面板 Inspector 的面板继承方式对项目来说是很有必要的, 比如一个基类, 写了一个很好看的检视面板[CustomEditor(typeof(XXX))], 可是所有子类的面板无法直接继 ...

  4. WordCount-JAVA版

    WordCountMapper import java.io.IOException; import org.apache.hadoop.io.IntWritable; import org.apac ...

  5. 4.Go-结构体、结构体指针和方法

    4.1.结构体 结构体:将一个或多个变量组合到一起形成新的类型,这个类型就是结构体,结构体是值类型 定义结构体和赋值 //Learn_Go/main.go package main import &q ...

  6. 记一次排错经历,requests和fake_useragent

    在部署tornado项目上线时, 首次重启服务后第一次请求必然会报错, 后续的就能正常访问, 长报错urllib.error.URLError,如图排查多次依然发现不了问题 报的最多的依然是上图中的错 ...

  7. luogu P1084疫情控制 二分

    链接 loj luogu太水不要去了. 思路 二分. 每个军队在一定的时间内越往上越好. 注意一个军队可以跨过1去帮别的. 把能到1脚下的点都存下来特判. 有一种情况是这个子树内只有一个军队,但这个军 ...

  8. KMP——从入门到不会打题

    KMP——从入门到不会打题 前言 如果你不了解哈希,建议先观看本蒟蒻的另一篇博客,对哈希有一定的理解   哈希大法吼 KMP算法,别名烤馍片或者看毛片,由烤馍片男子天团三位神犇同时发现的一种强大的单模 ...

  9. Linux性能优化实战学习笔记:第三十二讲

    一.上节总结 专栏更新至今,四大基础模块的第三个模块——文件系统和磁盘 I/O 篇,我们就已经学完了.很开心你还没有掉队,仍然在积极学习思考和实践操作,并且热情地留言与讨论. 今天是性能优化的第四期. ...

  10. Linux性能优化实战学习笔记:第四十六讲

    一.上节回顾 不知不觉,我们已经学完了整个专栏的四大基础模块,即 CPU.内存.文件系统和磁盘 I/O.以及网络的性能分析和优化.相信你已经掌握了这些基础模块的基本分析.定位思路,并熟悉了相关的优化方 ...