java常用类解析六:IO系统文件读写工具类

几个文件读写的工具类:文本文件读写、二进制文件读写、对象读写。其中对象读写工具类有错误,在试图进行多个对象读取时,读第二个对象就抛出异常,这是为什么?此外怎样把一个存放对象的文件中所有的对象读出来?

这个问题已经解决,非常感谢Aguo的文章:自定义ObjectOutputStream,解决追加写入后,读取错误的问题 。在这篇文章中我找到了答案,同时对作者的源代码添加了一些注解。解决方案请看文章最后。

1、文本文件读写工具类

package mine.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * 此工具类用于文本文件的读写
 * 
 * @author Touch
 */
public class TextFile {
	// 读取指定路径文本文件
	public static String read(String filePath) {
		StringBuilder str = new StringBuilder();
		BufferedReader in = null;
		try {
			in = new BufferedReader(new FileReader(filePath));
			String s;
			try {
				while ((s = in.readLine()) != null)
					str.append(s + '\n');
			} finally {
				in.close();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return str.toString();
	}

	// 写入指定的文本文件,append为true表示追加,false表示重头开始写,
	//text是要写入的文本字符串,text为null时直接返回
	public static void write(String filePath, boolean append, String text) {
		if (text == null)
			return;
		try {
			BufferedWriter out = new BufferedWriter(new FileWriter(filePath,
					append));
			try {
				out.write(text);
			} finally {
				out.close();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

 

package mine.util;

public class TestTextFile {
	public static void main(String[] args) {
		TextFile.write("file/textfile.txt", false,
				"这是一个文本文件的读写测试\nTouch\n刘海房\n男\n");
		TextFile.write("file/textfile.txt", true, "武汉工业学院\n软件工程");
		System.out.println(TextFile.read("file/textfile.txt"));
	}
}

 

2、二进制文件读写工具类

package mine.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 此工具类用于二进制文件的读写
 * 
 * @author Touch
 */
public class BinaryFile {
	// 把二进制文件读入字节数组,如果没有内容,字节数组为null
	public static byte[] read(String filePath) {
		byte[] data = null;
		try {
			BufferedInputStream in = new BufferedInputStream(
					new FileInputStream(filePath));
			try {
				data = new byte[in.available()];
				in.read(data);
			} finally {
				in.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return data;
	}

	// 把字节数组为写入二进制文件,数组为null时直接返回
	public static void write(String filePath, byte[] data) {
		if (data == null)
			return;
		try {
			BufferedOutputStream out = new BufferedOutputStream(
					new FileOutputStream(filePath));
			try {
				out.write(data);
			} finally {
				out.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


 

package mine.util;

import java.util.Arrays;

public class TestBinaryFile {
	public static void main(String[] args) {
		BinaryFile.write("file/binaryfile.dat", new byte[] { 1, 2, 3, 4, 5, 6,
				7, 8, 9, 10, 'a', 'b', 'c' });
		byte[] data = BinaryFile.read("file/binaryfile.dat");
		System.out.println(Arrays.toString(data));
	}
}


 

3、对象读写工具类(有问题,在读取多个对象时有问题,怎样把一个对象文件中的所有对象读出来?)

package mine.util;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * 此类用于对象的读写
 * 
 * @author Touch
 */
public class ObjectFile {
	// 把一个对象写入文件,isAppend为true表示追加方式写,false表示重新写
	public static void write(String filePath, Object o, boolean isAppend) {
		if (o == null)
			return;
		try {
			ObjectOutputStream out = new ObjectOutputStream(
					new FileOutputStream(filePath, isAppend));
			try {
				out.writeObject(o);
			} finally {
				out.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 把一个对象数组写入文件,isAppend为true表示追加方式写,false表示重新写
	public static void write(String filePath, Object[] objects, boolean isAppend) {
		if (objects == null)
			return;
		try {
			ObjectOutputStream out = new ObjectOutputStream(
					new FileOutputStream(filePath, isAppend));
			try {
				for (Object o : objects)
					out.writeObject(o);
			} finally {
				out.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 读取对象,返回一个对象
	public static Object read(String filePath) {
		Object o = null;
		try {
			ObjectInputStream in = new ObjectInputStream(new FileInputStream(
					filePath));
			try {
				o = in.readObject();
			} finally {
				in.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return o;
	}

	// 读取对象,返回一个对象数组,count表示要读的对象的个数
	public static Object[] read(String filePath, int count) {
		Object[] objects = new Object[count];
		try {
			ObjectInputStream in = new ObjectInputStream(new FileInputStream(
					filePath));
			try {
				for (int i = 0; i < count; i++) {
					//第二次调用in.readObject()就抛出异常,这是为什么?
					objects[i] = in.readObject();
				}
			} finally {
				in.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return objects;
	}
}


 

package mine.util;

import java.io.Serializable;

public class TestObjectFile {
	public static void main(String[] args) {
		ObjectFile.write("file/object1", new Person(), false);
		ObjectFile.write("file/object1", new Person(), true);
		ObjectFile.write("file/object1", new Person[] { new Person("Touch", 1),
				new Person("Rainbow", 0), new Person() }, true);
		for (Object o : ObjectFile.read("file/object1", 5))
			((Person) o).display();
	}
}

class Person implements Serializable {
	private String name = "刘海房";
	private int sex = 0;

	Person(String name, int sex) {
		this.name = name;
		this.sex = sex;
	}

	Person() {
	}

	void display() {
		System.out.println("my name is :" + name);
		String s = (sex == 0) ? "男" : "女";
		System.out.println("性别:" + s);
	}
}


4、对象读写工具类(解决了3中的问题,能够写入及读取多个对象)

          3中到底问题出在哪呢?先来看一段ObjectOutputStream构造方法的源代码,此源代码来自jdk1.6版。

    public ObjectOutputStream(OutputStream out) throws IOException {
	verifySubclass();
	bout = new BlockDataOutputStream(out);
	handles = new HandleTable(10, (float) 3.00);
	subs = new ReplaceTable(10, (float) 3.00);
	enableOverride = false;
	writeStreamHeader();
	bout.setBlockDataMode(true);
        if (extendedDebugInfo) {
	    debugInfoStack = new DebugTraceInfoStack();
	} else {
	    debugInfoStack = null;
        }   
    }


这段代码中我们只需要关注writeStreamHeader();方法,这个方法在源代码中的解释是

 /**
     * The writeStreamHeader method is provided so subclasses can append or
     * prepend their own header to the stream.  It writes the magic number and
     * version to the stream.
     *
     * @throws	IOException if I/O errors occur while writing to the underlying
     * 		stream
     */

也就是说我们打开(new)一个ObjectOutputStream的时候,这个ObjectOutputStream流中就已经被写入了一些信息,这些信息会写入到我们的文件中。在第一次写入文件时,这些头部信息时需要的,因为ObjectInputStream读的时候会帮我们过滤掉。但是当我们追加写入一个文件时,这些头部信息也会写入文件中,读取的时候只会把文件第一次出现的头部信息过滤掉,并不会把文件中间的头部信息也过滤掉,这就是问题的根源所在。

      怎么解决呢?正如lichong_87提到的

      一、可以在每次写入的时候把文件中所有对象读出来,然后重新写入,这种方法效率比较低。

      二、如果不是第一次写入文件,在写入时去掉头部信息,怎么去掉呢?头部信息是在writeStreamHeader();方法中写入的,所以我们可以通过继承ObjectOutputStream来覆盖这个方法,如果不是第一次写入文件,这个方法什么也不做。

     下面是第二种解决方案的源代码及测试

package mine.util.io;

import java.io.File;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

/**
 * 此类继承ObjectOutputStream,重写writeStreamHeader()方法,以实现追加写入时去掉头部信息
 */
public class MyObjectOutputStream extends ObjectOutputStream {
	private static File f;

	// writeStreamHeader()方法是在ObjectOutputStream的构造方法里调用的
	// 由于覆盖后的writeStreamHeader()方法用到了f。如果直接用此构造方法创建
	// 一个MyObjectOutputStream对象,那么writeStreamHeader()中的f是空指针
	// 因为f还没有初始化。所以这里采用单态模式
	private MyObjectOutputStream(OutputStream out, File f) throws IOException,
			SecurityException {
		super(out);
	}

	// 返回一个MyObjectOutputStream对象,这里保证了new MyObjectOutputStream(out, f)
	// 之前f已经指向一个File对象
	public static MyObjectOutputStream newInstance(File file, OutputStream out)
			throws IOException {
		f = file;// 本方法最重要的地方:构建文件对象,两个引用指向同一个文件对象
		return new MyObjectOutputStream(out, f);
	}

	@Override
	protected void writeStreamHeader() throws IOException {
		// 文件不存在或文件为空,此时是第一次写入文件,所以要把头部信息写入。
		if (!f.exists() || (f.exists() && f.length() == 0)) {
			super.writeStreamHeader();
		} else {
			// 不需要做任何事情
		}
	}
}


 

package mine.util.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

/**
 * 此类用于对象的读写
 * 
 * @author Touch
 */
public class ObjectFile {
	// 把一个对象写入文件,isAppend为true表示追加方式写,false表示重新写
	public static void write(String filePath, Object o, boolean isAppend) {
		if (o == null)
			return;
		try {
			File f = new File(filePath);
			MyObjectOutputStream out = MyObjectOutputStream.newInstance(f,
					new FileOutputStream(f, isAppend));
			try {
				out.writeObject(o);
			} finally {
				out.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 把一个对象数组写入文件,isAppend为true表示追加方式写,false表示重新写
	public static void write(String filePath, Object[] objects, boolean isAppend) {
		if (objects == null)
			return;
		try {
			File f = new File(filePath);
			MyObjectOutputStream out = MyObjectOutputStream.newInstance(f,
					new FileOutputStream(f, isAppend));
			try {
				for (Object o : objects)
					out.writeObject(o);
			} finally {
				out.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 读取对象,返回一个对象
	public static Object read(String filePath) {
		Object o = null;
		try {
			ObjectInputStream in = new ObjectInputStream(new FileInputStream(
					filePath));
			try {
				o = in.readObject();
			} finally {
				in.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return o;
	}

	// 读取对象,返回一个对象数组,count表示要读的对象的个数
	public static Object[] read(String filePath, int count) {
		Object[] objects = new Object[count];
		try {
			ObjectInputStream in = new ObjectInputStream(new FileInputStream(
					filePath));
			try {
				for (int i = 0; i < count; i++) {
					
					objects[i] = in.readObject();
				}
			} finally {
				in.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return objects;
	}
}


 

package mine.util.io;

import java.io.Serializable;

public class TestObjectFile {
	public static void main(String[] args) {
		ObjectFile.write("file/t.dat", new Person(), false);
		ObjectFile.write("file/t.dat", new Person(), true);
		ObjectFile.write("file/t.dat", new Person[] { new Person("Touch", 1),
				new Person("Rainbow", 0), new Person() }, true);
		for (Object o : ObjectFile.read("file/t.dat", 5))
			((Person) o).display();
	}
}

class Person implements Serializable {
	private static final long serialVersionUID = 1L;
	private String name = "刘海房";
	private int sex = 0;

	Person(String name, int sex) {
		this.name = name;
		this.sex = sex;
	}

	Person() {
	}

	void display() {
		System.out.println("my name is :" + name);
		String s = (sex == 0) ? "男" : "女";
		System.out.println("性别:" + s);
	}
}


运行结果:

my name is :刘海房
性别:男
my name is :刘海房
性别:男
my name is :Touch
性别:女
my name is :Rainbow
性别:男
my name is :刘海房
性别:男

 

 

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
package com.hexiang.utils; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; /** * 本是专门解析XML文件的,主要用于为系统读取自己的配置文件时提供最方便的解析操作 * @author HX * */ public class XmlManager { /** * 得到某节点下某个属性的值 * @param element 要获取属性的节点 * @param attributeName 要取值的属性名称 * @return 要获取的属性的值 * @author HX_2010-01-12 */ public static String getAttribute( Element element, String attributeName ) { return element.getAttribute( attributeName ); } /** * 获取指定节点下的文本 * @param element 要获取文本的节点 * @return 指定节点下的文本 * @author HX_2010-01-12 */ public static String getText( Element element ) { return element.getFirstChild().getNodeValue(); } /** * 解析某个xml文件,并在内存中创建DOM树 * @param xmlFile 要解析的XML文件 * @return 解析某个配置文件后的Document * @throws Exception xml文件不存在 */ public static Document parse( String xmlFile ) throws Exception { // 绑定XML文件,建造DOM树 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document domTree = db.parse( xmlFile ); return domTree; } /** * 获得某节点下的某个子节点(指定子节点名称,和某个属性的值) * 即获取parentElement下名字叫childName,并且属性attributeName的值为attributeValue的子结点 * @param parentElement 要获取子节点的那个父节点 * @param childName 要获取的子节点名称 * @param attributeName 要指定的属性名称 * @param attributeValue 要指定的属性的值 * @return 符合条件的子节点 * @throws Exception 子结点不存在或有多个符合条件的子节点 * @author HX_2008-12-01 */ public static Element getChildElement( Element parentElement, String childName, String attributeName, String attributeValue ) throws Exception { NodeList list = parentElement.getElementsByTagName( childName ); int count = 0; Element curElement = null; for ( int i = 0 ; i < list.getLength() ; i ++ ) { Element child = ( Element )list.item( i ); String value = child.getAttribute( attributeName ); if ( true == value.equals( attributeValue ) ) { curElement =
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值