Java 文件与IO操作大全

               在应用系统中,不管多大的系统或者是项目,总结起来都可以看成是:输入--->处理--->输出,所以说输入输出是非常重要的,除此之外我们还经常会遇到文件的上传下载,备份等等常用的功能,这些都需要使用文件和IO操作,而Java.io围绕File InputStream OutputStream Write  Reader 等五个重要的对象和Serializable这个接口提供了强大的输入输出处理的功能。

              1,首先看File类,File类可以直接用来操作文件,包括了对文件的新建和删除等常用的操作。

<span style="white-space:pre">		</span>// 常用的构造方法
		File file1=new File("weijun");             //直接使用路径创建文件
		File file2=new File(file1,"file2");        // 指定创建文件的父文件       file2为File1 的子文件
		File file3=new File("weijun","file3");     //用路径指定父文件                    file3 是当前路径中"weijun"目录下的子文件、、
		// File中的常量,文件分割符和路径分割符
		System.out.println("fileSeparator:"+File.separator);   //fileSeparator:\
		System.out.println("FilePathSeparator:"+File.pathSeparator);  // FilePathSeparator:;
		// File 类中的主要方法
		file1.delete();                           // 删除文件
		file1.exists();                           //判断文件是够存在
		file1.isDirectory();                      // 判断当前文件是否是目录
		file1.isFile();                           //判断是否是普通文件
		file1.mkdir();                            //如果当前目录不存在则创建一个目录
		file1.getAbsolutePath();                  //得到文件的绝对路劲        
		file1.getPath();                          //得到文件袋饿相对路劲
		file1.getParentFile();                    //得到文件的父文件
		file1.getParent();                        //得到父文件名
		file1.listFiles();                        //得到文件中所有文件列表,返回所有文件
		file1.list();                             //返回所有子文件名
		file1.listFiles(new FileFilter() {        //根据文件来筛选返回文件,需要一个FileFilter接口,实现该接口的accept函数
			
			public boolean accept(File pathname) { //当调用listFiles方法时,会逐一的取当前文件的子文件,来匹配accept方法,如果为true则加入返回结果中
				
				return false;
			}
		});                                       //根据文件名来筛选返回文件
		file1.listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return false;
			}
		});
	}
使用File类可以很方便的对文件进行操作,但是 如果要对文件的内容进行操作,则可以使用随机读取类RandomAccessFile,可以随机的读取一个文件中指定位置的数据,用一个例子来说明:

import java.io.File;
import java.io.RandomAccessFile;

public class RandomAccessFileTest {
	public static void main (String[] args) {
		try{
		File f=new File("test.txt");  // 要操作的文件
		RandomAccessFile rdf=new RandomAccessFile(f,"rw"); // 对文件test 可读可写
		// 之所以随机读取类能够对指定的文件内容进行操作,相当于其内部设置了文件的指针指向文件的当前位置,所以在我们实现随机读取时最好固定读取内容的长度一定
		String  name="xiaozhang ";        // 10字节
		rdf.writeBytes(name);             // 写入第一个人的姓名
		name="xiaoli    ";                //10字节
		rdf.writeBytes(name);             // 写入第二个人的姓名
		name="xiaowang  ";                //10字节
		rdf.writeBytes(name);             // 写入第三个人的姓名  
		// 现在要安顺寻查找第二个人  第一个人  第三个人
		byte[] b=new byte[10];
		rdf.seek(0);     //从文件起始处开始读写
		rdf.skipBytes(10);         //跳过是10字节,从第二个人开始
		for(int i=0;i<b.length;i++)
			b[i]=rdf.readByte();   //逐个字节的读取
		name=new String(b);        // 将字节数组转化成字符串
		System.out.println("第二个人是:"+name);
		rdf.seek(0);              //跳到文件起始位置,准备读取第一个人的姓名
		b=new byte[10];           // 重新初始化字节缓冲区
		for(int i=0;i<b.length;i++)
			b[i]=rdf.readByte();   //逐个字节的读取
		name=new String(b);        // 将字节数组转化成字符串
		System.out.println("第一个人是:"+name);
		rdf.skipBytes(10);         //当第一个人的信息取完后指针应该指向第二个人的信息的开始,所以跳过后就到了第三个人信息的开始处
		b=new byte[10];           // 重新初始化字节缓冲区
		for(int i=0;i<b.length;i++)
			b[i]=rdf.readByte();   //逐个字节的读取
		name=new String(b);        // 将字节数组转化成字符串
		System.out.println("第三个人是:"+name);
		rdf.close();  //关闭随机读写文件
		}
		catch(Exception e){
			e.printStackTrace();
			System.out.println(e.getMessage());
		}
		/*得到的结果:
		 *  第二个人是:xiaoli    
			第一个人是:xiaozhang 
			第三个人是:xiaowang  
		 */
		
	}

}

2,字节输入输出流: InputStream和OutputStream  这两个抽象类是所有字节输入输出流类的父类,字节流是最基本的流,文件的操作,网络数据的传输都依赖于字节流。

InputStream 常用方法:

int  read()          读出下一个字节,当不在有内容时返回-1;

int read(byte[] b);将读出的内容放入字节数组中,放回实际读取的字节数,如果没有读取内容放回-1

int read(byte[] b,int off,int len );将读出的内容放入字节数组中,从数组的off开始 放入len个字节,返回实际读取的长度,如果没有读取内容返回-1

void close();关闭文件流

OutoutStream常用的方法:

void write(int b);讲一个字节数据写入数据流

void write(byte[] b);x写入一个字节数组

void write(bute[] b,int off,int len)  将字节数组从off 开始的len个字节写入到数据流

void close() 关闭流

void flush()刷新缓冲区 




2.1首先最常用的就是文件的字节输入输出流FileInputStream  和 FileOutputStream,使用文件的字节流能够很方便的对文件内容(内容非纯中文)进行读写。

文件读写通常包含四个操作:  使用File类找到一个文件;用文件创建一个流;执行读写的操作;关闭文件流  

文件流操作实例,复制一个文件:

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

public class FileCopyTest {
	// 将源路劲的文件复制到目的路劲的文件
	public	boolean copyFile(File src ,File dist){  // 第一步找到目标文件
			boolean isCopy =true;
			FileInputStream in=null;
			FileOutputStream out=null;
			try {
				in=new FileInputStream(src);  // 第二步 创建读写流
				out=new FileOutputStream(dist);
				byte[] buff=new byte[1024];
				int length=0;  // 每次复制的字节数       //  第三步开始文件读写
				while((length=in.read(buff, 0, 1024))>0){
					out.write(buff, 0, length);
				}
			} catch (Exception e) {
				isCopy=false;              //  文件复制失败
				e.printStackTrace();
			}
			finally{
				
				try {
					in.close();      //第四步关闭流
					out.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			return isCopy;
			
		}
	public static void main(String[] args) {
		File src=new File("test.txt");  // 必须是存在的文件
		File dist=new File("testCopy.txt");  // 如果不存在会自动创建
		if(new FileCopyTest().copyFile(src, dist))
			System.out.println("文件复制成功");
	}

}
2.2使用对象输入输出流ObjectOutputStream和ObjectInputStream可以实现对象的输入输出,但是前提是对象必需实现 序列化接口,所谓序列化就是把一个对象变为二进制流的一种方法,通过对象序列化可以方便的实现对象的传输和存储。

如果一个对象想被序列化,则对象需要实现接口java.io.Serializable接口。这个接口的定义:

public  interface Serializable{}  此接口中并没有定义任何方法,所以这个接口只是一个标示接口,标示一个实现了该接口的类具备了被序列化的能力。
使用对象流操作对象的读写:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ObjectStreamTest {
	private File test;  // 用来保持对象的文件
	public ObjectStreamTest(File test) {
		super();
		this.test = test;
	}
	public ObjectStreamTest() {
		super();
	}
	public static void main(String[] args)throws Exception {
		Student stu=new Student("xiaoxiao",24,new SimpleDateFormat("yyyy-MM-dd").parse("1990-06-16"));
		ObjectStreamTest obTest=new ObjectStreamTest(new File("objectStreamTest.txt"));
		obTest.writeObject(stu);
		Student redObj=(Student)obTest.readObject();
		System.out.println("取得学生的信息:");
		System.out.println("姓名:"+redObj.getStuName());
		System.out.println("年龄:"+redObj.getStuAge());
		System.out.println("出身日期:"+new SimpleDateFormat("yyyy-MM-dd").format(redObj.getStuBirth()));
	}
	public  void writeObject(Object obj) throws Exception{  // 向文件中写入对象
		ObjectOutputStream objOut=new ObjectOutputStream(new FileOutputStream(test));
		objOut.writeObject(obj);  // 写入对象
		objOut.close();
		
	}
	public  Object readObject()  throws Exception{  // 从文件中读取对象
		Object result=null;
		ObjectInputStream objIn=new ObjectInputStream(new FileInputStream(test));
		result=objIn.readObject();  // 从流中读取对象
		objIn.close();
		return result;
	}
}

class Student implements java.io.Serializable{  // 学生类实现序列化接口
	private String stuName;
	private int stuAge;
	private Date stuBirth;
	public String getStuName() {
		return stuName;
	}
	public int getStuAge() {
		return stuAge;
	}
	public Date getStuBirth() {
		return stuBirth;
	}
	public void setStuName(String stuName) {
		this.stuName = stuName;
	}
	public void setStuAge(int stuAge) {
		this.stuAge = stuAge;
	}
	public void setStuBirth(Date stuBirth) {
		this.stuBirth = stuBirth;
	}
	public Student(String stuName, int stuAge, Date stuBirth) {
		super();
		this.stuName = stuName;
		this.stuAge = stuAge;
		this.stuBirth = stuBirth;
	}
	public Student() {
		super();
		// TODO Auto-generated constructor stub
	}
	
}
2.3     文件流和对象流输入输出的位置都是针对文件,java流也提供了直接在内存中操作的流:ByteArrayInputStream,ByteArrayOutputStream。

内存流用的并不多,一般只在生成一些临时文件的时候才会用到,而如果这些临时信息放在文件中的话,则程序运行完之后还必需要删除文件,所以这种情况直接使用内存流比较合适。     看下面的例子:使用内存流完成字符串的大小写转换(使用这两个流的习惯跟平时要反过来,这里要特别说明,因为我们运行的程序也是放在内存中):

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class ByteArrayStreamTest {
	public static void main (String[] args) throws Exception{
		String msg="hello word";
		// 定义一个内存流,并将内存中的字节数组读入到内存流中
		ByteArrayInputStream bis=new ByteArrayInputStream(msg.getBytes());
		// 定义内存输出流,用来读取数据,这里可以看出内存流的使用上跟平时文件操作是相反的
		ByteArrayOutputStream bos=new ByteArrayOutputStream();
		int len; // 表示每次读出的字节数
		while((len=(bis.read()))!=-1){ // 每次从流中读取一个字节,放回字节的内容(0--255)
			char ch=(char)len;  // 转成字符
			bos.write(Character.toUpperCase(ch)); // 将字符从小写转到大写,并从流中写会到内存中
		}
		msg=bos.toString(); // 将内存中的内容转化成字符串赋值给msg
		System.out.println("字符串转变大写后为:"+msg);
	}
}

2.4  java中还提供了管道流来实现两个线程之间的通信(在多线程的操作中会有总结),要注意的是:如果要使用管道输出,则必需要把输出连接到输入上如图:

PipeOutputStream类上使用  pubic void connect(PipeInputStream snk)Throws IOException  连接管道流

2.5 java 还提供了与平台无关的数据操作流DataOutputStream和DataInputStream,可以很方便的对一定格式的数据进行处理。

使用DataOutputStream 和 DataInputStream 对一定格式的数据进行处理:

package edu.hue.jk.io;

import java.io.DataInputStream;

class Student implements java.io.Serializable{  // 学生类实现序列化接口
	private String stuName;
	private int stuAge;
	private Date stuBirth;
	public String getStuName() {
		return stuName;
	}
	public int getStuAge() {
		return stuAge;
	}
	public Date getStuBirth() {
		return stuBirth;
	}
	public void setStuName(String stuName) {
		this.stuName = stuName;
	}
	public void setStuAge(int stuAge) {
		this.stuAge = stuAge;
	}
	public void setStuBirth(Date stuBirth) {
		this.stuBirth = stuBirth;
	}
	public Student(String stuName, int stuAge, Date stuBirth) {
		super();
		this.stuName = stuName;
		this.stuAge = stuAge;
		this.stuBirth = stuBirth;
	}
	public Student() {
		super();
	}
	
}
public class DataStreamTest {
	public static void main(String[] args) throws Exception{
		// 定义数据输出流,输出到指定的文件中
		DataOutputStream dos=new DataOutputStream(new FileOutputStream(new File("DateTest.txt")));
		// 构造学生数组
		Student stu1=new Student("student1", 22, new SimpleDateFormat("yyyy-MM-dd").parse("1992-06-16"));
		Student stu2=new Student("student2", 22, new SimpleDateFormat("yyyy-MM-dd").parse("1992-06-16"));
		List<Student> students=new LinkedList<Student>();
		students.add(stu1);
		students.add(stu2);
		// 向文件中写入学生的数据
		for(Student s:students){
			dos.writeChars(s.getStuName());  // 写入字符串
			dos.writeChar('\t');//加入分割符
			dos.writeInt(s.getStuAge());  // 写入整数
			dos.writeChar('\t');
			dos.writeChars(new SimpleDateFormat("yyyy-MM-dd").format(s.getStuBirth()));
			dos.writeChar('\n');    //插入换行符
		}
		// 定义数据输入流,从文件中得到学生的信息
		DataInputStream dis=new DataInputStream(new FileInputStream(new File("DateTest.txt")));
		for(int i=0;i<2;i++){
			char c;
			int len=0;
			char[] byteName=new char[100];  // 用来将字节数组转换成字符串使用
			while((c=dis.readChar())!='\t'){  // 读出姓名
				byteName[len++]=c;
			}
			String name=new String(byteName,0,len);
			//dis.readChar();  // 读出分割字符
			int age=dis.readInt(); // 读出年纪
			dis.readChar();  // 读出分割字符
			len=0;
			byteName=new char[100];  // 用来将字节数组转换成字符串使用
			while((c=dis.readChar())!='\n'){  // 读出出生年月
				byteName[len++]=c;
			}
			String birth=new String(byteName,0,len);
			//dis.readChar();  // 读出回车字符
			System.out.println("学生姓名:"+name+"年龄:"+age+"出生年月:"+birth);
		}
		dos.close();
		dis.close();
	}

}

数据输出:

学生姓名:student1年龄:22出生年月:1992-06-16
学生姓名:student2年龄:22出生年月:1992-06-16

3,字符流Reader和Writer     java在使用字符流的操作和字节流基本上一样只是代码不同而已,关于字符流与字节流的区别和如何使用,我觉得百度上的这个回答总结的很清楚(下面关于字符流和字节流的区别引用自百度上别人的回答):

	字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节, 操作字节和字节数组。所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是 音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点. 
     	所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列. 
      	字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串; 2. 字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以。
除此之外java在字节流和字符流对文件的操作上也是有些不同的,字节流直接针对文件进行处理,而字符流在操作时会使用到缓冲区,再通过缓冲区去操作文件,至于什么事缓冲区,我的理解其实缓冲区就是一段内存,因为对文件的读写比直接操作内存中的数据的速度要慢的多,所以在这中间就加入的缓冲区(相当于cache的作用)解决文件读写和内存直接读写的速度不匹配的问题。
在使用字符流操作时,千万不要忘了输出缓冲的内容  writer.flush()
使用字符流的操作(也是复制文件内容):
import java.io.File;
public class CopyFile_Reader {
	public static void main(String[] args) {
		//源文件对象
		File file1 = new File("DateTest.txt");
		//目标文件
		File file2 = new File("copyTest.txt");
		FileReader reader = null ;
		FileWriter writer = null ; 
		try {
			reader = new FileReader(file1);  // 处理步奏跟字节流差不多
			writer = new FileWriter(file2);
			int length = 0;
			char[] c = new char[1024];
			while((length=reader.read(c, 0, 1024))!=-1){  
				writer.write(c, 0, length);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				reader.close();
				writer.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

3.1 很多时候由于需要,我们要将字节流转化成字符流(例如从键盘得到中文信息),java提供了两个转换流InputStreamReader和OutputStreamReader用来将字节流转换成对应的字符流。实例:从键盘输入一句话(中文)保存到文件

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;


public class StreamReaderAndWriterTest {
	//  从键盘中得到中文信息,写入到文件中
	public static void main(String[] args) throws Exception{
		InputStreamReader isr=new InputStreamReader(System.in); //将从键盘上的得到的字节流转化成字符流
		BufferedReader br=new BufferedReader(isr);  // 使用缓存来读取
		String msg=br.readLine();   // 读取一行信息  
		FileOutputStream fos=new FileOutputStream("streamReaderAndWriterTest.txt");
		OutputStreamWriter osw=new OutputStreamWriter(fos);  // 转化成字符输出流
		BufferedWriter bw=new BufferedWriter(osw);
		bw.write(msg);  // 写入得到的信息
		bw.close();     // 关闭的时候会输出缓冲区的信息
		isr.close();
	}

}
上面总结了很多关于流的操作和文件的操作,下面总结了一个综合的例子,<span style="color:#990000;">用来处理文件或者文件夹的备份,采用深度优先搜索的方式来备份文件夹</span>:
<pre class="java" name="code">import java.io.File;
// 要不要加入多线程来专门处理每个文件的复制
//  用来备份一个文件或则是文件夹的类
public class FileCopy {
	private File srcFile;       // 源文件
	private File distFile;      //目标文件
	
	public FileCopy() {
		super();
	}
	public FileCopy(File srcFile, File distFile) {
		super();
		this.srcFile = srcFile;
		this.distFile = distFile;
	}
	public boolean copy(){  // 使用深度优先搜索来处理文件备份,谁说算法在应用中用不上???             
		boolean result=true;
		distFile=new File(distFile,srcFile.getName());
		if(srcFile.isFile()){   //如果源文件是普通文件  直接复制源文件
			try {
				InputStream in=new FileInputStream(srcFile);
				OutputStream out=new FileOutputStream(distFile);
				byte[] buff=new byte[1024];
				int length=0;
				while((length=in.read(buff, 0, 1024))>0){
					out.write(buff, 0, length);
				}
				in.close();
				out.close();
			} catch (Exception e) {
				e.printStackTrace();
				System.out.println(e.getMessage());
				result=false;
			}
			distFile=distFile.getParentFile();  // 如果当前复制的是文件,则目标文件要上移
		}
		else{  // 如果是文件夹
			if(distFile.mkdir())  // 创建文件夹
				System.out.println("文件夹"+distFile.getName()+"创建成功");
			else
				System.out.println(distFile.getPath()+"创建失败");
			File[] fileList=srcFile.listFiles();
			for(File f:fileList){  // 开始复制文件夹中的子文件
				srcFile=f;
				copy();
			}
			distFile=distFile.getParentFile();  // 如果一个文件复制完成之后,目标文件的位置也要上移
		}
		return result;
	}
}



                
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值