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;
	}
}



發佈了47 篇原創文章 · 獲贊 4 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章