儘管能夠經過不一樣的方式組合IO流類,但咱們可能也就只用到其中的幾種組合。下面的例子能夠做爲典型的IO用法的基本參考。在這些示例中,異常處理都被簡化爲將異常傳遞給控制檯,可是這隻有在小型示例和工具中才適用。在代碼中,你須要考慮更加複雜的錯誤處理方式。java
一樣,本文會包括以下幾個方面:正則表達式
緩衝輸入文件數組
從內存輸入app
格式化的內存輸入dom
基本的文件輸出函數
存儲和恢復數據工具
讀寫隨機訪問文件性能
實用工具學習
總結ui
若是想要打開一個文件用於字符輸入,可使用以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()來關閉流。
在下面的示例中,從上面的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才能顯示正確結果。
要讀取格式化數據,可使用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()方法查看還有多少可供存取的字符來判斷是否結束。
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);
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或其餘類型讀入。
使用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類型的數據有可能出現不是咱們想要的結果。
到這裏咱們學習了多種I/O流的典型用法,好比緩衝輸入文件、從內存輸入、基本的文件輸出、存儲和恢復數據、隨機讀寫文件,這些都是Java I/O流比較典型的用法。這裏咱們發現讀取文件、修改、在寫出是一個很常見的程序化的任務,可是Java I/O類庫的設計有一個問題,就是咱們須要編寫不少代碼來實現這些操做,要記住如何打開文件是一件優勢困難的事情。所以,下面是收集的一些幫助類,能夠很容易爲咱們完成這些基本任務,記錄在這裏,方便之後查看。
這裏收集了兩個工具:
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對象中。
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()); } }
本文沒有總結什麼新的知識點,只是總結了一些Java I/O的常見用法好比緩衝輸入文件、從內存輸入、基本的文件輸出、存儲和恢復數據、隨機讀寫文件等,而且蒐集了兩個工具類用來幫助咱們讀寫文件讀取二進制文件,以提升些代碼的效率。