Java的檔案讀寫操作
file(記憶體)----輸入流---->【程式】----輸出流---->file(記憶體)
當我們讀寫文字檔案的時候,採用Reader是非常方便的,比如FileReader,InputStreamReader和BufferedReader。其中最重要的類是InputStreamReader, 它是位元組轉換為字元的橋樑。你可以在構造器重指定編碼的方式,如果不指定的話將採用底層作業系統的預設編碼方式,例如GBK等。使用FileReader讀取檔案:
FileReader fr = new FileReader("ming.txt"); int ch = 0; while((ch = fr.read())!=-1 ) {
System.out.print((char)ch);
}
其中read()方法返回的是讀取得下個字元。當然你也可以使用read(char[] ch,int off,int length)這和處理二進位制檔案的時候類似。
事實上在FileReader中的方法都是從InputStreamReader中繼承過來的。read()方法是比較好費時間的,如果為了提高效率我們可以使用BufferedReader對Reader進行包裝,這樣可以提高讀取得速度,我們可以一行一行的讀取文字,使用readLine()方法。
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("ming.txt")));String data = null;while((data = br.readLine())!=null){System.out.println(data); }
瞭解了FileReader操作使用FileWriter寫檔案就簡單了,這裡不贅述。
Eg.我的綜合例項
testFile:
import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReader;public class testFile { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub // file(記憶體)----輸入流---->【程式】----輸出流---->file(記憶體) File file = new File("d:/temp", "addfile.txt"); try { file.createNewFile(); // 建立檔案 } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 向檔案寫入內容(輸出流) String str = "親愛的小南瓜!"; byte bt[] = new byte[1024]; bt = str.getBytes(); try { FileOutputStream in = new FileOutputStream(file); try { in.write(bt, 0, bt.length); in.close(); // boolean success=true; // System.out.println("寫入檔案成功"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { // 讀取檔案內容 (輸入流) FileInputStream out = new FileInputStream(file); InputStreamReader isr = new InputStreamReader(out); int ch = 0; while ((ch = isr.read()) != -1) { System.out.print((char) ch); } } catch (Exception e) { // TODO: handle exception } }}
java中多種方式讀檔案
//------------------參考資料---------------------------------////1、按位元組讀取檔案內容//2、按字元讀取檔案內容//3、按行讀取檔案內容//4、隨機讀取檔案內容import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.FileReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.RandomAccessFile;import java.io.Reader;public class ReadFromFile { /** * 以位元組為單位讀取檔案,常用於讀二進位制檔案,如圖片、聲音、影像等檔案。 * * @param fileName * 檔案的名 */ public static void readFileByBytes(String fileName) { File file = new File(fileName); InputStream in = null; try { System.out.println("以位元組為單位讀取檔案內容,一次讀一個位元組:"); // 一次讀一個位元組 in = new FileInputStream(file); int tempbyte; while ((tempbyte = in.read()) != -1) { System.out.write(tempbyte); } in.close(); } catch (IOException e) { e.printStackTrace(); return; } try { System.out.println("以位元組為單位讀取檔案內容,一次讀多個位元組:"); // 一次讀多個位元組 byte[] tempbytes = new byte[100]; int byteread = 0; in = new FileInputStream(fileName); ReadFromFile.showAvailableBytes(in); // 讀入多個位元組到位元組陣列中,byteread為一次讀入的位元組數 while ((byteread = in.read(tempbytes)) != -1) { System.out.write(tempbytes, 0, byteread); } } catch (Exception e1) { e1.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e1) { } } } } /** * 以字元為單位讀取檔案,常用於讀文字,數字等型別的檔案 * * @param fileName * 檔名 */ public static void readFileByChars(String fileName) { File file = new File(fileName); Reader reader = null; try { System.out.println("以字元為單位讀取檔案內容,一次讀一個位元組:"); // 一次讀一個字元 reader = new InputStreamReader(new FileInputStream(file)); int tempchar; while ((tempchar = reader.read()) != -1) { // 對於windows下,rn這兩個字元在一起時,表示一個換行。 // 但如果這兩個字元分開顯示時,會換兩次行。 // 因此,遮蔽掉r,或者遮蔽n。否則,將會多出很多空行。 if (((char) tempchar) != 'r') { System.out.print((char) tempchar); } } reader.close(); } catch (Exception e) { e.printStackTrace(); } try { System.out.println("以字元為單位讀取檔案內容,一次讀多個位元組:"); // 一次讀多個字元 char[] tempchars = new char[30]; int charread = 0; reader = new InputStreamReader(new FileInputStream(fileName)); // 讀入多個字元到字元陣列中,charread為一次讀取字元數 while ((charread = reader.read(tempchars)) != -1) { // 同樣遮蔽掉r不顯示 if ((charread == tempchars.length) && (tempchars[tempchars.length - 1] != 'r')) { System.out.print(tempchars); } else { for (int i = 0; i < charread; i++) { if (tempchars[i] == 'r') { continue; } else { System.out.print(tempchars[i]); } } } } } catch (Exception e1) { e1.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e1) { } } } } /** * 以行為單位讀取檔案,常用於讀面向行的格式化檔案 * * @param fileName * 檔名 */ public static void readFileByLines(String fileName) { File file = new File(fileName); BufferedReader reader = null; try { System.out.println("以行為單位讀取檔案內容,一次讀一整行:"); reader = new BufferedReader(new FileReader(file)); String tempString = null; int line = 1; // 一次讀入一行,直到讀入null為檔案結束 while ((tempString = reader.readLine()) != null) { // 顯示行號 System.out.println("line " + line + ": " + tempString); line++; } reader.close(); } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e1) { } } } } /** * 隨機讀取檔案內容 * * @param fileName * 檔名 */ public static void readFileByRandomAccess(String fileName) { RandomAccessFile randomFile = null; try { System.out.println("隨機讀取一段檔案內容:"); // 開啟一個隨機訪問檔案流,按只讀方式 randomFile = new RandomAccessFile(fileName, "r"); // 檔案長度,位元組數 long fileLength = randomFile.length(); // 讀檔案的起始位置 int beginIndex = (fileLength > 4) ? 4 : 0; // 將讀檔案的開始位置移到beginIndex位置。 randomFile.seek(beginIndex); byte[] bytes = new byte[10]; int byteread = 0; // 一次讀10個位元組,如果檔案內容不足10個位元組,則讀剩下的位元組。 // 將一次讀取的位元組數賦給byteread while ((byteread = randomFile.read(bytes)) != -1) { System.out.write(bytes, 0, byteread); } } catch (IOException e) { e.printStackTrace(); } finally { if (randomFile != null) { try { randomFile.close(); } catch (IOException e1) { } } } } /** * 顯示輸入流中還剩的位元組數 * * @param in */ private static void showAvailableBytes(InputStream in) { try { System.out.println("當前位元組輸入流中的位元組數為:" + in.available()); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { String fileName = "C:/temp/newTemp.txt"; ReadFromFile.readFileByBytes(fileName); ReadFromFile.readFileByChars(fileName); ReadFromFile.readFileByLines(fileName); ReadFromFile.readFileByRandomAccess(fileName); }}
//二、將內容追加到檔案尾部import java.io.FileWriter;import java.io.IOException;import java.io.RandomAccessFile;/** * 將內容追加到檔案尾部 */public class AppendToFile { /** * A方法追加檔案:使用RandomAccessFile * * @param fileName * 檔名 * @param content * 追加的內容 */ public static void appendMethodA(String fileName, String content) { try { // 開啟一個隨機訪問檔案流,按讀寫方式 RandomAccessFile randomFile = new RandomAccessFile(fileName, "rw"); // 檔案長度,位元組數 long fileLength = randomFile.length(); // 將寫檔案指標移到檔案尾。 randomFile.seek(fileLength); randomFile.writeBytes(content); randomFile.close(); } catch (IOException e) { e.printStackTrace(); } } /** * B方法追加檔案:使用FileWriter * * @param fileName * @param content */ public static void appendMethodB(String fileName, String content) { try { // 開啟一個寫檔案器,建構函式中的第二個引數true表示以追加形式寫檔案 FileWriter writer = new FileWriter(fileName, true); writer.write(content); writer.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { String fileName = "C:/temp/newTemp.txt"; String content = "new append!"; // 按方法A追加檔案 AppendToFile.appendMethodA(fileName, content); AppendToFile.appendMethodA(fileName, "append end. n"); // 顯示檔案內容 ReadFromFile.readFileByLines(fileName); // 按方法B追加檔案 AppendToFile.appendMethodB(fileName, content); AppendToFile.appendMethodB(fileName, "append end. n"); // 顯示檔案內容 ReadFromFile.readFileByLines(fileName); }}
1、判斷檔案是否存在,不存在建立檔案
File file=new File(path+filename); if(!file.exists()) { try { file.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
2、判斷資料夾是否存在,不存在建立資料夾
File file =new File(path+filename); //如果資料夾不存在則建立 if (!file .exists()) { file .mkdir(); }
import java.io.File; import java.io.FileOutputStream; import java.io.*; public class FileTest { public FileTest() { } public static void main(String[] args) { FileOutputStream out = null; FileOutputStream outSTr = null; BufferedOutputStream Buff=null; FileWriter fw = null; int count=1000;//寫檔案行數 try { out = new FileOutputStream(new File(“C:/add.txt”)); long begin = System.currentTimeMillis(); for (int i = 0; i < count; i++) { out.write(“測試java 檔案操作\r\n”.getBytes()); } out.close(); long end = System.currentTimeMillis(); System.out.println(“FileOutputStream執行耗時:” + (end - begin) + ” 豪秒”); outSTr = new FileOutputStream(new File(“C:/add0.txt”)); Buff=new BufferedOutputStream(outSTr); long begin0 = System.currentTimeMillis(); for (int i = 0; i < count; i++) { Buff.write(“測試java 檔案操作\r\n”.getBytes()); } Buff.flush(); Buff.close(); long end0 = System.currentTimeMillis(); System.out.println(“BufferedOutputStream執行耗時:” + (end0 - begin0) + ” 豪秒”); fw = new FileWriter(“C:/add2.txt”); long begin3 = System.currentTimeMillis(); for (int i = 0; i < count; i++) { fw.write(“測試java 檔案操作\r\n”); } fw.close(); long end3 = System.currentTimeMillis(); System.out.println(“FileWriter執行耗時:” + (end3 - begin3) + ” 豪秒”); } catch (Exception e) { e.printStackTrace(); } finally { try { fw.close(); Buff.close(); outSTr.close(); out.close(); } catch (Exception e) { e.printStackTrace(); } } } }
String name = "AAAA.txt";String lujing = "1"+"/"+"2";//定義路徑File a = new File(lujing,name);
a.getParentFile().mkdirs(); //這裡如果不加getParentFile(),建立的資料夾為"1/2/AAAA.txt/"
那麼,a的意義就是“1/2/AAAA.txt”。這裡a是File,但是File這個類在Java裡表示的不只是檔案,雖然File在英語裡是檔案的意思。Java裡,File至少可以表示檔案或資料夾(大概還有可以表示系統裝置什麼的,這裡不考慮,只考慮檔案和資料夾)。也就是說,在“1/2/AAAA.txt”真正出現在磁碟結構裡之前,它既可以表示這個檔案,也可以表示這個路徑的資料夾。那麼,如果沒有getParentFile(),直接執行a.mkdirs(),就是說,建立“1/2/AAAA.txt”代表的資料夾,也就是“1/2/AAAA.txt/”,在此之後,執行a.createNewFile(),試圖建立a檔案,然而以a為名的資料夾已經存在了,所以createNewFile()實際是執行失敗的。你可以用System.out.println(a.createNewFile())這樣來檢查是不是真正建立檔案成功。所以,這裡,你想要建立的是“1/2/AAAA.txt”這個檔案。在建立AAAA.txt之前,必須要1/2這個目錄存在。所以,要得到1/2,就要用a.getParentFile(),然後要建立它,也就是a.getParentFile().mkdirs()。在這之後,a作為檔案所需要的資料夾大概會存在了(有特殊情況會無法建立的,這裡不考慮),就執行a.createNewFile()建立a檔案。
Java RandomAccessFile的使用
Java的RandomAccessFile提供對檔案的讀寫功能,與普通的輸入輸出流不一樣的是RamdomAccessFile可以任意的訪問檔案的任何地方。這就是“Random”的意義所在。
RandomAccessFile的物件包含一個記錄指標,用於標識當前流的讀寫位置,這個位置可以向前移動,也可以向後移動。RandomAccessFile包含兩個方法來操作檔案記錄指標。
long getFilePoint():記錄檔案指標的當前位置。
void seek(long pos):將檔案記錄指標定位到pos位置。
RandomAccessFile包含InputStream的三個read方法,也包含OutputStream的三個write方法。同時RandomAccessFile還包含一系列的readXxx和writeXxx方法完成輸入輸出。
RandomAccessFile的構造方法如下
mode的值有四個
"r":以只讀文方式開啟指定檔案。如果你寫的話會有IOException。
"rw":以讀寫方式開啟指定檔案,不存在就建立新檔案。
"rws":不介紹了。
"rwd":也不介紹。
/** * 往檔案中依次寫入3名員工的資訊, * 每位員工有姓名和員工兩個欄位 然後按照 * 第二名,第一名,第三名的先後順序讀取員工資訊 */import java.io.File;import java.io.RandomAccessFile;public class RandomAccessFileTest { public static void main(String[] args) throws Exception { Employee e1 = new Employee(23, "張三"); Employee e2 = new Employee(24, "lisi"); Employee e3 = new Employee(25, "王五"); File file = new File("employee.txt"); if (!file.exists()) { file.createNewFile(); } // 一箇中文佔兩個位元組 一個英文字母佔一個位元組 // 整形 佔的位元組數目 跟cpu位長有關 32位的佔4個位元組 RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); randomAccessFile.writeChars(e1.getName()); randomAccessFile.writeInt(e1.getAge()); randomAccessFile.writeChars(e2.getName()); randomAccessFile.writeInt(e2.getAge()); randomAccessFile.writeChars(e3.getName()); randomAccessFile.writeInt(e3.getAge()); randomAccessFile.close(); RandomAccessFile raf2 = new RandomAccessFile(file, "r"); raf2.skipBytes(Employee.LEN * 2 + 4); String strName2 = ""; for (int i = 0; i < Employee.LEN; i++) { strName2 = strName2 + raf2.readChar(); } int age2 = raf2.readInt(); System.out.println("strName2 = " + strName2.trim()); System.out.println("age2 = " + age2); raf2.seek(0); String strName1 = ""; for (int i = 0; i < Employee.LEN; i++) { strName1 = strName1 + raf2.readChar(); } int age1 = raf2.readInt(); System.out.println("strName1 = " + strName1.trim()); System.out.println("age1 = " + age1); raf2.skipBytes(Employee.LEN * 2 + 4); String strName3 = ""; for (int i = 0; i < Employee.LEN; i++) { strName3 = strName3 + raf2.readChar(); } int age3 = raf2.readInt(); System.out.println("strName3 = " + strName3.trim()); System.out.println("age3 = " + age3); }}class Employee { // 年齡 public int age; // 姓名 public String name; // 姓名的長度 public static final int LEN = 8; public Employee(int age, String name) { this.age = age; // 對name字元長度的一個處理 if (name.length() > LEN) { name = name.substring(0, LEN); } else { while (name.length() < LEN) { name = name + "/u0000"; } } this.name = name; } public int getAge() { return age; } public String getName() { return name; }}
主體:
RandomAccessFile類。其I/O效能較之其它常用開發語言的同類效能差距甚遠,嚴重影響程式的執行效率。
開發人員迫切需要提高效率,下面分析RandomAccessFile等檔案類的原始碼,找出其中的癥結所在,並加以改進優化,建立一個"性/價比"俱佳的隨機檔案訪問類BufferedRandomAccessFile。
在改進之前先做一個基本測試:逐位元組COPY一個12兆的檔案(這裡牽涉到讀和寫)。
讀 | 寫 | 耗用時間(秒) |
RandomAccessFile | RandomAccessFile | 95.848 |
BufferedInputStream + DataInputStream | BufferedOutputStream + DataOutputStream | 2.935 |
我們可以看到兩者差距約32倍,RandomAccessFile也太慢了。先看看兩者關鍵部分的原始碼,對比分析,找出原因。
1.1.[RandomAccessFile]
Java程式碼- publicclass RandomAccessFile implements DataOutput, DataInput {
- publicfinalbyte readByte() throws IOException {
- int ch = this.read();
- if (ch < 0)
- thrownew EOFException();
- return (byte)(ch);
- }
- publicnativeint read() throws IOException;
- publicfinalvoid writeByte(int v) throws IOException {
- write(v);
- }
- publicnativevoid write(int b) throws IOException;
- }
可見,RandomAccessFile每讀/寫一個位元組就需對磁碟進行一次I/O操作。
1.2.[BufferedInputStream]
Java程式碼- publicclass BufferedInputStream extends FilterInputStream {
- privatestaticint defaultBufferSize = 2048;
- protectedbyte buf[]; // 建立讀快取區
- public BufferedInputStream(InputStream in, int size) {
- super(in);
- if (size <= 0) {
- thrownew IllegalArgumentException("Buffer size <= 0");
- }
- buf = newbyte[size];
- }
- publicsynchronizedint read() throws IOException {
- ensureOpen();
- if (pos >= count) {
- fill();
- if (pos >= count)
- return -1;
- }
- return buf[pos++] & 0xff; // 直接從BUF[]中讀取
- }
- privatevoid fill() throws IOException {
- if (markpos < 0)
- pos = 0; /* no mark: throw away the buffer */
- elseif (pos >= buf.length) /* no room left in buffer */
- if (markpos > 0) { /* can throw away early part of the buffer */
- int sz = pos - markpos;
- System.arraycopy(buf, markpos, buf, 0, sz);
- pos = sz;
- markpos = 0;
- } elseif (buf.length >= marklimit) {
- markpos = -1; /* buffer got too big, invalidate mark */
- pos = 0; /* drop buffer contents */
- } else { /* grow buffer */
- int nsz = pos * 2;
- if (nsz > marklimit)
- nsz = marklimit;
- byte nbuf[] = newbyte[nsz];
- System.arraycopy(buf, 0, nbuf, 0, pos);
- buf = nbuf;
- }
- count = pos;
- int n = in.read(buf, pos, buf.length - pos);
- if (n > 0)
- count = n + pos;
- }
- }
1.3.[BufferedOutputStream]
Java程式碼- publicclass BufferedOutputStream extends FilterOutputStream {
- protectedbyte buf[]; // 建立寫快取區
- public BufferedOutputStream(OutputStream out, int size) {
- super(out);
- if (size <= 0) {
- thrownew IllegalArgumentException("Buffer size <= 0");
- }
- buf = newbyte[size];
- }
- publicsynchronizedvoid write(int b) throws IOException {
- if (count >= buf.length) {
- flushBuffer();
- }
- buf[count++] = (byte)b; // 直接從BUF[]中讀取
- }
- privatevoid flushBuffer() throws IOException {
- if (count > 0) {
- out.write(buf, 0, count);
- count = 0;