Java中的IO流(五)
上一篇《Java中的IO流(四)》記錄了一下Properties類,此類不屬於IO流,它屬於集合框架。接下來說一下IO流中的其它流
一,列印流PrintStream
PrintStream為其他輸出流添加了功能,使它們能夠方便地列印各種資料值表示 形式。並且此注永遠不會丟擲IOException。
此流的建構函式大致分三類
1,接收File檔案型別的
2,接收OutputStream型別的
3,接收檔名形式的
下演示一下此流的兩個方法
1private static void function_demo1() throws IOException { 2PrintStream ps = new PrintStream("print.txt"); 3ps.write(97); 4ps.println(); 5ps.print(97); 6}
執行結果 :
a
97
注:PrintStream的write方法繼承自父類,此方法的說明是
向輸出流寫入一個位元組。要寫入的位元組是引數b
的八個低位。b
的 24 個高位將被忽略。所以我們看到的第一行輸出轉為了”a“;而print方法是直接把引數呼叫String的ValueOf方法轉為字串直接輸出的,所以若想把一個數據的直接表現形式則用print方法。
二,列印流PrintWriter
向文字輸出流列印物件的格式化表示形式。此類實現在中的所有print 方法。
此類的建構函式大致分為四類
1,接收File檔案型別
2,接收OutputStream型別
3,接收檔名形式
4,接收Writer型別
此類有建構函式接收第二個引數型別為boolean型別的,若傳為true則可將資料自動flush到流中
演示如下:
1private static void function_demo2() throws IOException { 2BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 3PrintWriter writer = new PrintWriter(System.out); 4String line = null; 5while ((line = reader.readLine()) != null) { 6if (line.equals("over")) { 7break; 8} else { 9writer.println(line.toUpperCase()); 10} 11} 12writer.close(); 13reader.close(); 14}
注:以上程式碼在演示的時候每次輸入完後並不會立刻輸入到控制檯,當在第九行後加writer.flush();時每次輸入完後即可把相應資訊列印到控制檯,但printwriter類提供了自動重新整理的方法就是用兩個引數的建構函式,演示如下
1private static void function_demo2() throws IOException { 2BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 3PrintWriter writer = new PrintWriter(System.out, true); 4String line = null; 5while ((line = reader.readLine()) != null) { 6if (line.equals("over")) { 7break; 8} else { 9writer.println(line.toUpperCase()); 10} 11} 12writer.close(); 13reader.close(); 14}
注意以上程式碼的第三行,用了兩個引數的建構函式
當需要直接把資訊輸出到檔案中的時候,並且想要每次輸入完成後立即把資訊輸出到檔案中,演示如下
1private static void function_demo2() throws IOException { 2BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 3PrintWriter writer = new PrintWriter(new FileWriter("writer.txt"), true); 4String line = null; 5while ((line = reader.readLine()) != null) { 6if (line.equals("over")) { 7break; 8} else { 9writer.println(line.toUpperCase()); 10} 11} 12writer.close(); 13reader.close(); 14}
三,序列流SequenceInputStream
表示其他輸入流的邏輯串聯。它從輸入流的有序集合開始,並從第一個輸入流開始讀取,直到到達檔案末尾,接著從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的檔案末尾為止。
此流相當於是一個集合,把若干個流放入到此集合,然後一個流接著一個流的讀取,當完第一個判斷此集合裡是否還有其它流,若有接著讀取,讀到最後一個返回-1。
此流有兩個建構函式,一個是接收兩個InputStream型別的引數,即接收兩個流,若想讀取多個流,則用接收Enumeration型別的引數。
接下來我們用一個需求把此流演示一下,需求是把三個txt文件合併為一個文件
1private static void function_demo3() throws IOException { 2Vector<InputStream> v = new Vector<InputStream>();// 定義Vector集合用於儲存所有的流 3v.add(new FileInputStream("1.txt")); 4v.add(new FileInputStream("2.txt")); 5v.add(new FileInputStream("3.txt")); 6Enumeration<InputStream> enu = v.elements();// 因為SequenceInputStream接收的是Enumeration型別的引數,所以用Vector集合 7SequenceInputStream sis = new SequenceInputStream(enu); 8OutputStream out = new FileOutputStream("4.txt"); 9byte[] bt = new byte[1024]; 10int len = 0; 11while ((len = sis.read(bt)) != -1) { 12out.write(bt, 0, len); 13} 14out.close(); 15sis.close();// 序列流的關閉會把其中的所有的流都關閉 16}
大家都知道,Vector物件效率太低,開發中一般會用ArrayList,那麼接下來就把Vector換成ArrayList,演示如下
1private static void function_demo3() throws IOException { 2ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();// 定義ArrayList集合接所有的流 3al.add(new FileInputStream("1.txt")); 4al.add(new FileInputStream("2.txt")); 5al.add(new FileInputStream("3.txt")); 6Iterator<FileInputStream> it = al.iterator();// 獲取一個Iterator物件 7// 沒有列舉物件就new一個出來,然後重寫裡面的兩個方法返回iterator物件的hasNext和next方法 8Enumeration<FileInputStream> enumeration = new Enumeration<FileInputStream>() { 9@Override 10public boolean hasMoreElements() { 11return it.hasNext(); 12} 13 14@Override 15public FileInputStream nextElement() { 16return it.next(); 17} 18}; 19SequenceInputStream sis = new SequenceInputStream(enumeration); 20OutputStream out = new FileOutputStream("4.txt"); 21byte[] bt = new byte[1024]; 22int len = 0; 23while ((len = sis.read(bt)) != -1) { 24out.write(bt, 0, len); 25} 26out.close(); 27sis.close();// 序列流的關閉會把其中的所有的流都關閉 28}
看起來很麻煩的樣子,有沒有辦法簡化一下呢?Collections集合類裡為我們提供了一個獲取Enumeration的工具,演示如下:
1 private static void function_demo3() throws IOException { 2ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();// 定義ArrayList集合接所有的流 3al.add(new FileInputStream("1.txt")); 4al.add(new FileInputStream("2.txt")); 5al.add(new FileInputStream("3.txt")); 6Iterator<FileInputStream> it = al.iterator();// 獲取一個Iterator物件 7Enumeration<FileInputStream> enumeration = java.util.Collections.enumeration(al); 8SequenceInputStream sis = new SequenceInputStream(enumeration); 9OutputStream out = new FileOutputStream("4.txt"); 10byte[] bt = new byte[1024]; 11int len = 0; 12while ((len = sis.read(bt)) != -1) { 13out.write(bt, 0, len); 14} 15out.close(); 16sis.close();// 序列流的關閉會把其中的所有的流都關閉 17}
四,檔案切割及合併
把一個大的檔案切割成若干個小的檔案,方便檔案上傳的時候對大小的限制,實現程式碼如下
1private static final int SIZE = 1024 * 1024; 2 3private static void function_demo4() throws IOException { 4File dir = new File("c:\\"); 5File file = new File(dir, "1.bmp");// 需要分隔的原始檔 6FileInputStream inputStream = new FileInputStream(file);// 位元組流物件 7byte[] bt = new byte[SIZE];// 緩衝陣列,大小由常量固定 8// 由於需要分隔檔案,所以此處不能例項化輸出流物件,需要在讀取的時候每讀取一次例項化一個輸出流物件 9FileOutputStream outputStream = null; 10File dest = null;// 由於切割檔案,每個被切割出來的檔名稱不同,所以此處File不能例項化,需要在每切割出來一個檔案則例項化一個File物件 11int len; 12int i = 0;// 為切割出來的每個檔名新增序列 13while ((len = inputStream.read(bt)) != -1) { 14i++; 15dest = new File(dir, "\\temp\\" + i + ".part");//切割出來的檔案物件 16outputStream = new FileOutputStream(dest);//例項化每個輸出流物件 17outputStream.write(bt, 0, len);//讀取每個切割出來的檔案 18outputStream.close();//關閉每個讀取流物件 19} 20inputStream.close();//關閉輸入流物件 21}
把切割後的檔案合併為一個檔案,實現程式碼如下
1private static void function_demo5() throws IOException { 2File dir = new File("C:\\temp");//源目錄物件 3ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();//儲存輸入流的集合物件 4FileInputStream inputStream = null;//根據原始檔的個數例項化輸入流物件,因需要迴圈原始檔數量,所以此處例項為null 5File file = null;//原始檔名字不一樣,所以此處例項化為null 6for (int i = 1; i < 4; i++) { 7file = new File(dir, i + ".part");//原始檔名 8inputStream = new FileInputStream(file);//封裝輸入流物件 9al.add(inputStream);//將輸入流物件新增到流集合物件中 10} 11Enumeration<FileInputStream> enu = java.util.Collections.enumeration(al);//從輸入流集合物件中獲取Enumeration物件以便傳給序列流SequenceInputStream 12SequenceInputStream sis = new SequenceInputStream(enu);//封裝序列流物件 13BufferedInputStream bis = new BufferedInputStream(sis);//緩衝輸入流物件 14File dest = new File(dir, "4.bmp");//封裝合併全的檔名及路徑 15FileOutputStream outputStream = new FileOutputStream(dest);//輸出流物件 16BufferedOutputStream bos = new BufferedOutputStream(outputStream);//輸出緩衝物件 17//以下程式碼是迴圈讀取及寫入流物件,關閉流 18int len; 19while ((len = bis.read()) != -1) { 20bos.write(len); 21} 22bos.close(); 23bis.close(); 24}
五,完整的檔案切割與合併
由上面的例子大家可以考慮幾個問題
1,被切割後的檔案數量在合併的時候並不知道
2,原始檔的副檔名在合併的時候並不知道
鑑於這兩點,在切割檔案的時候應當把這兩個資訊寫入到配置檔案中,在合併檔案的時候先讀取配置檔案中的資訊然後檢驗配置檔案中的資訊和切割後的檔案資訊是否一致,若一致再進行合併操作,具體操作演示如下
1private static final int SIZE = 1024 * 1024; 2 3private static void function_demo6() throws IOException { 4File dir = new File("c:\\"); 5File file = new File(dir, "1.bmp");// 需要分隔的原始檔 6FileInputStream inputStream = new FileInputStream(file);// 位元組流物件 7byte[] bt = new byte[SIZE];// 緩衝陣列,大小由常量固定 8// 由於需要分隔檔案,所以此處不能例項化輸出流物件,需要在讀取的時候每讀取一次例項化一個輸出流物件 9FileOutputStream outputStream = null; 10File dest = null;// 由於切割檔案,每個被切割出來的檔名稱不同,所以此處File不能例項化,需要在每切割出來一個檔案則例項化一個File物件 11int len; 12int i = 0;// 為切割出來的每個檔名新增序列 13while ((len = inputStream.read(bt)) != -1) { 14i++; 15dest = new File(dir, "\\temp\\" + i + ".part");// 切割出來的檔案物件 16outputStream = new FileOutputStream(dest);// 例項化每個輸出流物件 17outputStream.write(bt, 0, len);// 讀取每個切割出來的檔案 18outputStream.close();// 關閉每個讀取流物件 19} 20inputStream.close();// 關閉輸入流物件 21String fileName = file.getName();// 獲取原始檔名 22File fileCountFilter = new File("C:\\temp");// 被切割的檔案的存放目錄 23String[] fileCountArray = fileCountFilter.list(new PartFileNameFilter(".part"));// 過濾被切割的檔案 24int count = fileCountArray.length;// 被切割的檔案數量 25Properties prop = new Properties();// 把切割的檔案資訊儲存到配置檔案中 26prop.setProperty("fileName", fileName);// 原始檔名 27prop.setProperty("count", String.valueOf(count));// 被切割出來的檔案數量 28Writer writer = new FileWriter("C:\\temp\\config.properties"); 29prop.store(writer, "cut file info"); 30}
1private static void function_demo7() throws IOException { 2File dir = new File("C:\\temp");// 源目錄物件 3String[] partFileArray = dir.list(new PartFileNameFilter(".part")); 4int partFileCount = partFileArray.length;// 獲取源目錄中被切割的檔案數量 5File[] propertiesFile = dir.listFiles(new PartFileNameFilter(".properties"));// 過濾配置檔案 6String fileName = null; 7int cutFileCount = 0; 8if (propertiesFile.length != 1) {// 若切割檔案配置檔案不唯一則丟擲異常 9throw new RuntimeException("切割檔案的配置資訊沒有或多於一個,無法完成檔案合併"); 10} else {// 否則讀取配置檔案資訊 11Properties prop = new Properties(); 12prop.load(new FileReader(propertiesFile[0])); 13fileName = prop.getProperty("fileName");// 獲取配置檔案中的原始檔名 14cutFileCount = Integer.parseInt(prop.getProperty("count"));// 獲取配置檔案中被切割後的檔案數量 15if (partFileCount != cutFileCount) {// 若讀取到的配置檔案中的檔案數與源目錄中被切割的檔案數量不一樣,則丟擲異常 16throw new RuntimeException("檔案不全,無法進行檔案合併"); 17} 18} 19ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();// 儲存輸入流的集合物件 20FileInputStream inputStream = null;// 根據原始檔的個數例項化輸入流物件,因需要迴圈原始檔數量,所以此處例項為null 21File file = null;// 原始檔名字不一樣,所以此處例項化為null 22for (int i = 1; i <= cutFileCount; i++) { 23file = new File(dir, i + ".part");// 原始檔名 24inputStream = new FileInputStream(file);// 封裝輸入流物件 25al.add(inputStream);// 將輸入流物件新增到流集合物件中 26} 27Enumeration<FileInputStream> enu = java.util.Collections.enumeration(al);// 從輸入流集合物件中獲取Enumeration物件以便傳給序列流SequenceInputStream 28SequenceInputStream sis = new SequenceInputStream(enu);// 封裝序列流物件 29BufferedInputStream bis = new BufferedInputStream(sis);// 緩衝輸入流物件 30File dest = new File(dir, fileName);// 封裝合併全的檔名及路徑 31FileOutputStream outputStream = new FileOutputStream(dest);// 輸出流物件 32BufferedOutputStream bos = new BufferedOutputStream(outputStream);// 輸出緩衝物件 33// 以下程式碼是迴圈讀取及寫入流物件,關閉流 34int len; 35while ((len = bis.read()) != -1) { 36bos.write(len); 37} 38bos.close(); 39bis.close(); 40}