文本文件就是字節序列,能夠是任意編碼形式。在中文操做系統上直接建立文本文件,則該文本文件只能識別ANSI編碼,其餘編碼方式會產生亂碼java
package imooc.io; import java.io.UnsupportedEncodingException; import java.util.Iterator; public class EncodeDemo { public static void main(String[] args) throws Exception { String player = "維斯布魯克Westbrook"; byte[] bs = player.getBytes(); // 轉換成字節序列用的是項目默認的編碼GBK for (byte b : bs) { // 把字節(轉換成了int)以16進制顯式 System.out.print(Integer.toHexString(b & 0xff) + " "); } System.out.println(); byte[] bs2 = player.getBytes("gbk"); // GBK編碼中文佔2個字節,英文佔1個字節 for (byte b : bs2) { System.out.print(Integer.toHexString(b & 0xff) + " "); } System.out.println(); byte[] bs3 = player.getBytes("utf-8"); // utf-8編碼中文佔3個字節,英文佔1個字節 for (byte b : bs3) { System.out.print(Integer.toHexString(b & 0xff) + " "); } System.out.println(); // java是雙字節編碼utf-16be byte[] bs4 = player.getBytes("utf-16be"); // utf-16be編碼中文佔2個字節,英文佔2個字節 for (byte b : bs4) { System.out.print(Integer.toHexString(b & 0xff) + " "); } System.out.println(); /* * 當字節序列是某種編碼時,若想把字節序列變成字符串 * 須要採用以上編碼方式,不然將出現亂碼 */ // 使用項目默認編碼 String string = new String(bs4); System.out.println("項目默認編碼:" + string); // 使用字符串構造的第二個參數 String string2 = new String(bs4, "utf-16be"); System.out.println("utf-16be編碼:" + string2); } }
運行結果:程序員
ce ac cb b9 b2 bc c2 b3 bf cb 57 65 73 74 62 72 6f 6f 6b ce ac cb b9 b2 bc c2 b3 bf cb 57 65 73 74 62 72 6f 6f 6b e7 bb b4 e6 96 af e5 b8 83 e9 b2 81 e5 85 8b 57 65 73 74 62 72 6f 6f 6b 7e f4 65 af 5e 3 9c 81 51 4b 0 57 0 65 0 73 0 74 0 62 0 72 0 6f 0 6f 0 6b 項目默認編碼:~鬳痎渷QK utf-16be編碼:維斯布魯克Westbrook
文件與目錄都是使用File來操做的,File能新建、刪除、重命名文件和目錄,File不能訪問文件內容自己。若是須要訪問文件內容自己,則須要使用輸入/輸出流數組
訪問文件名相關的方法安全
String getName():返回此File對象所表示的文件名和路徑名(若是是路徑,則返回最後一級子路徑名)網絡
String getPath():返回此File對象所對應的路徑名dom
File getAbsoluteFile():返回此File對象的絕對路徑性能
String getAbsolutePath():返回此File對象所對應的絕對路徑名測試
String getParent():返回此File對象所對應目錄(最後一級子目錄)的父路徑名編碼
boolean renameTo(File newName):重命名此File對象所對應的文件或目錄,若是重命名成功,則返回true;不然返回falsespa
文件檢測相關方法
boolean exists():判斷File對象所對應的文件或目錄是否存在
boolean canWrite():判斷File對象所對應的目錄或文件是否可寫
boolean canRead():判斷File對象所對應的目錄或文件是否可讀
boolean isFile():判斷File對象所對應的是不是文件,而不是目錄
boolean isDirectory():判斷File對象所對應的是不是目錄,而不是文件
boolean isAbsolute():判斷File對象所對應的文件或目錄是不是絕對路徑。該方法消除了不一樣平臺的差別,能夠直接判斷File對象是否爲絕對路徑。在UNIX/Linux/BSD等系統上,若是路徑名開頭是一條斜線(/),則代表該File對象對應一個絕對路徑;在Windows等系統上,若是路徑開頭是盤符,則說明它是絕對路徑
獲取常規文件信息
long lastModified():返回文件最後修改時間
long length():返回文件內容的長度
文件操做相關的方法
boolean createNewFile():當此File對象所對應的文件不存在時,該方法將新建的一個該File對象所指定的新文件,若是建立成功則返回true;不然返回false
boolean delete():刪除File對象所對應的文件或路徑
static File CreateTempFile(String prefix,String suffix):在默認的臨時文件目錄建立一個臨時空文件,使用給定前綴、系統生成的隨機數和給定後綴做爲文件名。這是一個靜態方法,能夠直接經過File來調用。preFix參數必須至少是3個字節長。建議前綴使用一個短的、有意義的字符串。建議前綴使用一個短的、有意義的字符串,好比」hjb「 或」main」. suffix參數能夠爲null,在這種狀況下,將使用默認的後綴」.tmp」
static File CreateTempFile(String prefix,String suffix,File directory):在directory所指定的目錄中建立一個臨時空文件,使用給定前綴、系統生成的隨機數和給定後綴做爲文件名。這是一個靜態方法,能夠直接經過File來調用
void deleteOnExit():註冊一個刪除鉤子,指定當Java虛擬機退出時,刪除File對象隨對應的文件和目錄
目錄操做相關方法
boolean mkdir(); 試圖建立一個File對象所對應的目錄,若是建立成功,則返回true;不然返回false. 調用該方法時File對象必須對應一個路徑,而不是一個文件
String[] list(); 列出File對象的全部子文件名和路徑名,返回String數組
File[] listFiles(); 列出File對象的全部子文件和路徑,返回File數組
static File[] listRoots(); 列出系統全部的根路徑。這是一個靜態方法,能夠直接經過File類來調用
import java.io.*; public class FileTest { public static void main(String[] args) throws IOException { // 以當前路徑來建立一個File對象 File file = new File("."); // 直接獲取文件名,輸出一點 System.out.println(file.getName()); // 獲取相對路徑的父路徑可能出錯,下面代碼輸出null System.out.println(file.getParent()); // 獲取絕對路徑 System.out.println(file.getAbsoluteFile()); // 獲取上一級路徑 System.out.println(file.getAbsoluteFile().getParent()); // 在當前路徑下建立一個臨時文件 File tmpFile = File.createTempFile("aaa", ".txt", file); // 指定當JVM退出時刪除該文件 tmpFile.deleteOnExit(); // 以系統當前時間做爲新文件名來建立新文件 File newFile = new File(System.currentTimeMillis() + ""); System.out.println("newFile對象是否存在:" + newFile.exists()); // 以指定newFile對象來建立一個文件 newFile.createNewFile(); // 以newFile對象來建立一個目錄,由於newFile已經存在, // 因此下面方法返回false,即沒法建立該目錄 newFile.mkdir(); // 使用list()方法來列出當前路徑下的全部文件和路徑 String[] fileList = file.list(); System.out.println("====當前路徑下全部文件和路徑以下===="); for (String fileName : fileList) { System.out.println(fileName); } // listRoots()靜態方法列出全部的磁盤根路徑。 File[] roots = File.listRoots(); System.out.println("====系統全部根路徑以下===="); for (File root : roots) { System.out.println(root); } } }
File類的list()方法能夠接收一個FilenameFilter參數,經過該參數能夠只列出符合條件的文件
FilenameFilter接口裏包含一個accept(File dir, String name)方法,該方法將依次對指定的File的全部子目錄或者文件進行迭代,若是該方法返回true,則list()方法將會列出該子目錄或者文件
import java.io.*; public class FilenameFilterTest { public static void main(String[] args) { File file = new File("."); // 使用Lambda表達式(目標類型爲FilenameFilter)實現文件過濾器。 // 若是文件名以.java結尾,或者文件對應一個路徑,返回true String[] nameList = file.list((dir, name) -> name.endsWith(".java") || new File(name).isDirectory()); for(String name : nameList) { System.out.println(name); } } }
實現類:
import java.io.File; import java.io.IOException; import java.util.Iterator; // 列出File的經常使用操做好比過濾、遍歷等操做 public class FileUtils { public static void listDirectory(File dir) throws IOException { /* * 列出指定目錄下(包括其子目錄)的全部文件 * @param dir * @throws IOExcepton */ if (!dir.exists()) { throw new IllegalArgumentException("目錄:" + dir + "不存在"); } if (!dir.isDirectory()) { throw new IllegalArgumentException(dir + "不是目錄"); } String[] filenames = dir.list(); // 返回字符串數組 for (String string : filenames) { System.out.println(dir + string); } // 遍歷子目錄下的內容,需構造File對象,進行遞歸操做 File[] files = dir.listFiles(); // 返回直接子目錄(文件)的抽象 if (files != null && files.length >0) { for (File file:files) { if (file.isDirectory()) { // 遞歸操做 listDirectory(file); } else { System.out.println(file); } } } } }
測試類:
import java.io.File; import java.io.IOException; public class FileTest1 { public static void main(String[] args) throws IOException { FileUtils.listDirectory(new File("D:\\coding\\Java路徑")); } }
Java的IO流是實現輸入輸出的基礎,它能夠方便地實現數據的輸入/輸出操做,在Java中把不一樣的輸入/輸出源抽象爲"流"(stream),經過流的方式容許Java程序使用相同的方式來訪問不一樣的輸入/輸出源。stream是從起源(source)到接收(sink)的有序數據
按照流的流向來分,能夠分爲輸入流和輸出流:
輸入流:只能從中讀取數據,而不能向其寫入數據
輸出流:只能向其寫入數據,而不能從中讀取數據
這裏的輸入、輸出都是從程序運行所在內存的角度來劃分的
Java的輸入流主要由InputStream和Reader做爲基類,而輸出流則主要由OutputStream和Writer做爲基類。均爲抽象類,沒法建立實例
字節流和字符流的用法幾乎徹底同樣,區別在於字節流和字符流所操做的數據單元不一樣--字節流操做的數據單元是8位字節,而字符流操做的數據單元是16位的字符
字節流主要有InputStream和OutputStream做爲基類,而字符流則組要由Reader和Writer做爲基類
按照流的角色來分,能夠分爲節點流和處理流:
能夠從/從一個特定的IO設備(如磁盤、網絡)讀/寫數據的流,稱爲節點流,節點流也被稱爲低級流(Low Level Stream)。當使用節點流進行輸入/輸出時,程序直接鏈接到實際的數據源,和時間的輸入/輸出節點鏈接
處理流則用於對一個已存在的流進行鏈接或封裝,經過封裝後的流來實現數據讀/寫功能,處理流也被稱爲高級流。使用處理流進行輸入/輸出時,程序並不會直接鏈接到實際的數據源,沒有和實際的輸入/輸出節點鏈接
使用處理流的一個明顯好處是,只要使用相同的處理流,程序就能夠採用徹底相同的輸入/輸出代碼來訪問不一樣的數據源,隨着處理流所包裝節點流的變化,程序實際所訪問的數據源也相應的發生變化
InputStream/Reader:全部輸入流的基類,前者是字節輸入流,後者是字符輸入流
OutputStream/Writer:全部輸出流的基類,前者是字節輸出流,後者是字符輸出流
處理流的功能主要體如今如下兩個方面:
性能的提升:主要以增長緩衝的方式來提供輸入/輸出的效率
操做的便捷:處理流可能提供了一系列便捷的方法來一次輸入/輸出大批量的內容,而不是輸入/輸出一個或多個「水滴」
處理流能夠「嫁接」在任何已存在的流的基礎之上,Java應用程序採用相同的代碼、透明的方式來訪問不一樣的輸入/輸出設備的數據流
如下介紹4個訪問文件的節點流用法
InputStream和Reader是全部輸入流的抽象基類,自己不能建立實例來執行輸入,是全部輸入流的模板,其方法全部輸入流均可使用
InputStream包含以下3個方法
int read():從輸入流中讀取單個字節,返回所讀取的字節數據(字節數據可直接轉換爲int類型)
int read(byte[] b):從輸入流中最多讀取b.length個字節的數據,並將其存儲在字節數組b中,返回實際讀取的字節數
int read(byte[] b, int off, int len):從輸入流中最多讀取len個字節的數據,並將其存儲在數組b中,放入數組b中時,並非從數組起點開始,而是從off位置開始,返回實際讀取的字節數
在Reader中包含以下3個方法
int read():從輸入流中讀取單個字符,返回所讀取的字符數據(字符數據可直接轉換爲int類型)
int read(char[] cbuf):從輸入流中最多讀取cbuf.length個字符的數據,並將其存儲在字節數組cbuf中,返回實際讀取的字符數
int read(char[] cbuf, int off ,int len):從輸入流中最多讀取len個字符的數據,並將其存儲在數組cbuf中,放入數組cbuf中時,並非從數組起點開始,而是從off位置開始,返回實際讀取的字符數
InputStream和Reader都是抽象類,自己不能建立實例,分別有一個用於讀取文件的輸入流:FileInputStream和FileReader,它們都是節點流——會直接和指定文件關聯
使用FileInputStream讀取自身:
import java.io.*; public class FileInputStreamTest { public static void main(String[] args) throws IOException { // 建立字節輸入流 FileInputStream fis = new FileInputStream( "FileInputStreamTest.java"); // 建立一個長度爲1024的「竹筒」 byte[] bbuf = new byte[1024]; // 用於保存實際讀取的字節數 int hasRead = 0; // 使用循環來重複「取水」過程 while ((hasRead = fis.read(bbuf)) > 0 ) { // 取出「竹筒」中水滴(字節),將字節數組轉換成字符串輸入! System.out.print(new String(bbuf , 0 , hasRead )); } // 關閉文件輸入流,放在finally塊裏更安全 fis.close(); } }
使用FileReader讀取文件自己:
import java.io.*; public class FileReaderTest { public static void main(String[] args) { try( // 建立字符輸入流 FileReader fr = new FileReader("FileReaderTest.java")) { // 建立一個長度爲32的「竹筒」 char[] cbuf = new char[32]; // 用於保存實際讀取的字符數 int hasRead = 0; // 使用循環來重複「取水」過程 while ((hasRead = fr.read(cbuf)) > 0 ) { // 取出「竹筒」中水滴(字符),將字符數組轉換成字符串輸入! System.out.print(new String(cbuf , 0 , hasRead)); } } catch (IOException ex) { ex.printStackTrace(); } } }
InputStream和Reader移動記錄指針的方法
void mark(int readAheadLimit):在記錄指針當前位置記錄一個標記(mark)
boolean markSupported():判斷輸入流是否支持mark()操做,便是否支持標記記錄
void reset():將此流的記錄指針從新定位到上一次標記(mark)的位置
long skip(long n):記錄指針向前移動n個字節/字符
OutputStream和Writer的用法也很是類似,兩個流都提供了以下三個方法:
void write(int c):將指定的字節/字符輸出到輸出流中,其中c便可以表明字節,也能夠表明字符
void write(byte[]/char[] buf):將字節數組/字符數組中的數據輸出到指定輸出流中
void write(byte[]/char[] buf, int off, int len ):將字節數組/字符數組中從off位置開始,長度爲len的字節/字符輸出到輸出流中
由於字符流直接以字符做爲操做單位,因此Writer能夠用字符串來代替字符數組,即以String對象做爲參數。Writer裏面還包含以下兩個方法
void write(String str):將str字符串裏包含的字符輸出到指定輸出流中。
void write (String str, int off, int len):將str字符串裏面從off位置開始,長度爲len的字符輸出到指定輸出流中
如下程序,使用FileInputStream執行輸入,FileOutputStream執行輸出,用以負責FileOutputStreamTest.java文件的功能:
import java.io.*; public class FileOutputStreamTest { public static void main(String[] args) { try( // 建立字節輸入流 FileInputStream fis = new FileInputStream("FileOutputStreamTest.java"); // 建立字節輸出流 FileOutputStream fos = new FileOutputStream("newFile.txt")) { byte[] bbuf = new byte[32]; int hasRead = 0; // 循環從輸入流中取出數據 while ((hasRead = fis.read(bbuf)) > 0 ) { // 每讀取一次,即寫入文件輸出流,讀了多少,就寫多少。 fos.write(bbuf , 0 , hasRead); } } catch (IOException ioe) { ioe.printStackTrace(); } } }
使用Java的IO流執行輸出時,必須關閉輸出流,關閉輸出流除了能夠保證流的物理資源被回收以外,可能還能夠將輸出流緩衝區中的數據flush到物流節點裏(在執行close()方法以前,自動執行輸出流的flush()方法)
Writer對於直接輸出字符串內容有着更好的效果
import java.io.*; public class FileWriterTest { public static void main(String[] args) { try( FileWriter fw = new FileWriter("AllStar.txt")) { fw.write("2016-2017賽季NBA全明星陣容\r\n"); fw.write("西部首發:斯蒂芬-庫裏、詹姆斯-哈登、凱文-杜蘭特、科懷-倫納德、安東尼-戴維斯\r\n"); fw.write("東部首發:勒布朗-詹姆斯、凱爾-歐文、揚尼斯-阿德託昆博、德瑪爾-德羅贊、吉米-巴特勒\r\n"); fw.write("西部替補:拉塞爾-威斯布魯克、克萊-湯普森、戈登-海沃德、德拉蒙德-格林、德馬庫斯-考辛斯、馬克-加索爾、德安德魯-喬丹\r\n"); fw.write("東部替補:以賽亞-托馬斯、凱爾-洛瑞、肯巴-沃克、約翰-沃爾、保羅-喬治、凱文-樂福、保羅-米爾薩普\r\n"); } catch (IOException ioe) { ioe.printStackTrace(); } } }
處理流能夠隱藏底層設備上節點流的差別,並對外提供更加方便的輸入/輸出方法,讓程序員只需關心高級流的操做
使用處理流的典型思路是,使用處理流來包裝節點流,程序經過處理流來執行輸入/輸出功能,讓節點流與底層的I/O設備、文件交互
處理流:流的構造器參數不是一個物理節點,而是已經存在的流;節點流:都是直接以物理IO節點做爲構造器參數的
import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; public class PrintStreamTest { public static void main(String[] args) { try ( FileOutputStream fos = new FileOutputStream("AllStar.txt"); PrintStream ps = new PrintStream(fos); ) { // 使用PrintStream執行輸出 ps.println("全明星陣容"); // 使用PrintStream輸出對象 ps.println(new PrintStreamTest()); } catch (IOException ioe) { ioe.printStackTrace(); } } }
輸出文本內容,一般將輸出流包裝成PrintStream後進行輸出
在處理處理流包裝了底層節點流以後,關閉輸入/輸出流資源時,只要關閉最上層的處理流便可。關閉最上層的處理流時,系統會自動關閉被該處理流包裝的節點流
分類 | 字節輸入流 | 字節輸出流 | 字符輸入流 | 字符輸出流 |
---|---|---|---|---|
抽象基類 | InputStream | OutputStream | Reader | Writer |
訪問文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
訪問數組 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
訪問管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
訪問字符串 | StringReader | StringWriter | ||
緩衝流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
轉換流 | InputStreamReader | OutputStreamWriter | ||
對象流 | ObjectInputStream | ObjectOutputStream | ||
抽象基類 | FilterInputStream | FilterOutputStream | FilterReader | FilterWriter |
打印流 | PrintStream | PrintWriter | ||
推回輸入流 | PushbackInputStream | PushbackReader | ||
特殊流 | DataInputStream | DataOutputStream |
若是進行輸入/輸出的內容是文本內容,則應該考慮使用字符流;若是進行輸入/輸出的內容是二進制內容,則應該考慮使用字節流
轉換流用於實現將字節流轉換成字符流,其中InputStreamReader將字節輸入流轉換成字符輸入流,OutputStreamWriter將字節輸出流轉換成字符輸出流
Java沒有將字符流轉換爲字節流的轉換流,由於:字節流比字符流的使用範圍更廣,但字符流比字節流操做方便。
Java使用System.in表明標準輸入,即鍵盤輸入,但這個標準輸入流是InputStream類的實例,使用不太方便,並且鍵盤輸入內容都是文本內容,因此可使用InputStreamReader將其轉換成字符輸入流,普通的Reader讀取輸入內容時依然不太方便,咱們能夠將普通的Reader再次包裝成BufferedReader,利用BufferedReader的readLine()方法能夠一次讀取一行內容
import java.io.*; public class KeyinTest { public static void main(String[] args) { try( // 將Sytem.in對象轉換成Reader對象 InputStreamReader reader = new InputStreamReader(System.in); // 將普通Reader包裝成BufferedReader BufferedReader br = new BufferedReader(reader)) { String line = null; // 採用循環方式來一行一行的讀取 while ((line = br.readLine()) != null) { // 若是讀取的字符串爲"exit",程序退出 if (line.equals("exit")) { System.exit(1); } // 打印讀取的內容 System.out.println("輸入內容爲:" + line); } } catch (IOException ioe) { ioe.printStackTrace(); } } }
BufferedReader流具備緩衝功能,能夠一次讀取一行文本——以換行符爲標誌,若是它沒有讀到換行符,則程序阻塞,等到讀到換行符爲止
PushbackInputStream、PushbackReader,它們有如下經常使用方法:
void unread(byte[]/char[] buf):將一個字節/字符數組內容推回緩衝區裏,從而容許重複讀取剛剛讀取的內容
void unread(byte[]/char[] buf, int off, int len):將一個字節/字符數組裏從off位置開始讀取,長度是len的字符/字節的內容推回到推回緩衝區裏,從而容許重複剛纔讀取的內容
void unread(int b):將一個字節、字符推回到推回緩衝區裏,從而容許重複讀取剛剛讀取的內容
兩個推回輸入流都帶有一個推回緩衝區,當程序調用unread()方法時,系統就會把指定數組的內容推回到該緩衝區,而推回輸入流每次調用read()方法時,總會先從推回緩衝區讀取,只有徹底讀取了緩衝區裏面的內容後,且尚未裝滿read()所需的數組時,纔會到原輸入流中讀取內容
import java.io.*; public class PushbackTest { public static void main(String[] args) { try( // 建立一個PushbackReader對象,指定推回緩衝區的長度爲64 PushbackReader pr = new PushbackReader(new FileReader("PushbackTest.java") , 64)) { char[] buf = new char[32]; // 用以保存上次讀取的字符串內容 String lastContent = ""; int hasRead = 0; // 循環讀取文件內容 while ((hasRead = pr.read(buf)) > 0) { // 將讀取的內容轉換成字符串 String content = new String(buf , 0 , hasRead); int targetIndex = 0; // 將上次讀取的字符串和本次讀取的字符串拼起來, // 查看是否包含目標字符串, 若是包含目標字符串 if ((targetIndex = (lastContent + content) .indexOf("new PushbackReader")) > 0) { // 將本次內容和上次內容一塊兒推回緩衝區 pr.unread((lastContent + content).toCharArray()); // 從新定義一個長度爲targetIndex的char數組 if(targetIndex > 32) { buf = new char[targetIndex]; } // 再次讀取指定長度的內容(就是目標字符串以前的內容) pr.read(buf , 0 , targetIndex); // 打印讀取的內容 System.out.print(new String(buf , 0 ,targetIndex)); System.exit(0); } else { // 打印上次讀取的內容 System.out.print(lastContent); // 將本次內容設爲上次讀取的內容 lastContent = content; } } } catch (IOException ioe) { ioe.printStackTrace(); } } }
Java的標準輸入/輸出分別經過System.in和System.out來表明,在默認的狀況下分別表明鍵盤和顯示器,當程序經過System.in來得到輸入時,其實是經過鍵盤得到輸入。當程序經過System.out執行輸出時,程序老是輸出到屏幕
在System類中提供了三個重定向標準輸入/輸出的方法:
static void setErr(PrintStream err):重定向「標準」錯誤輸出流
static void setIn(InputStream in):重定向「標準」輸入流
static void setOut(PrintStream out):重定向「標準」輸出流
import java.util.*; import java.io.*; public class RedirectIn { public static void main(String[] args) { try( FileInputStream fis = new FileInputStream("RedirectIn.java")) { // 將標準輸入重定向到fis輸入流 System.setIn(fis); // 使用System.in建立Scanner對象,用於獲取標準輸入 Scanner sc = new Scanner(System.in); // 增長下面一行將只把回車做爲分隔符 sc.useDelimiter("\n"); // 判斷是否還有下一個輸入項 while(sc.hasNext()) { // 輸出輸入項 System.out.println("鍵盤輸入的內容是:" + sc.next()); } } catch (IOException ex) { ex.printStackTrace(); } } }
import java.io.*; public class RedirectOut { public static void main(String[] args) { try( // 一次性建立PrintStream輸出流 PrintStream ps = new PrintStream(new FileOutputStream("out.txt"))) { // 將標準輸出重定向到ps輸出流 System.setOut(ps); // 向標準輸出輸出一個字符串 System.out.println("普通字符串"); // 向標準輸出輸出一個對象 System.out.println(new RedirectOut()); } catch (IOException ex) { ex.printStackTrace(); } } }
使用Runtime對象的exec()方法運行平臺上的其餘程序併產生一個Process對象,該對象表明由該Java程序啓動的子進程,Process類提供了以下3個方法,用於讓程序和其子進程進行通信:
InputStream getErrorStream():獲取子進程的錯誤流
InputStream getInputStream():獲取子進程的輸入流
OutputStream getOutputStream():獲取子進程的輸出流
子進程讀取Java程序的數據,就是讓Java程序把數據輸出到子進程中,使用輸出流
下面的代碼實現了獲取子進程的錯誤輸出
import java.io.*; public class ReadFromProcess { public static void main(String[] args) throws IOException { // 運行javac命令,返回運行該命令的子進程 Process p = Runtime.getRuntime().exec("javac"); try( // 以p進程的錯誤流建立BufferedReader對象 // 這個錯誤流對本程序是輸入流,對p進程則是輸出流 BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()))) { String buff = null; // 採起循環方式來讀取p進程的錯誤輸出 while((buff = br.readLine()) != null) { System.out.println(buff); } } } }
Java程序中啓動Java虛擬機運行另外一個Java程序,並向另外一Java程序輸入數據:
import java.io.*; public class WriteToProcess { public static void main(String[] args) throws IOException { // 運行java ReadStandard命令,返回運行該命令的子進程 Process p = Runtime.getRuntime().exec("java ReadStandard"); try( // 以p進程的輸出流建立PrintStream對象 // 這個輸出流對本程序是輸出流,對p進程則是輸入流 PrintStream ps = new PrintStream(p.getOutputStream())) { // 向ReadStandard程序寫入內容,這些內容將被ReadStandard讀取 ps.println("普通字符串"); ps.println(new WriteToProcess()); } } }
定義一個ReadStandard類,該類能夠接受標準輸入並將標準輸入寫入out.txt文件
import java.io.*; import java.util.Scanner; class ReadStandard { public static void main(String[] args) throws IOException { try( // 使用Scanner.in建立Scanner對象,用於獲取標準輸入 Scanner sc = new Scanner(System.in); PrintStream ps = new PrintStream(new FileOutputStream("outtext.txt")) ) { // 只把回車做爲分隔符 sc.useDelimiter("\n"); // 判斷是否還有下一個輸入項 while (sc.hasNext()) { // 輸出輸入項 System.out.println("鍵盤輸入的內容爲:" + sc.next()); } } catch (IOException ioe) { ioe.printStackTrace(); } } }
RandomAccessFile是Java輸入輸出流體系中功能最豐富的文件內容訪問類,它提供了衆多的方法來訪問文件內容,它便可以讀取文件內容,也能夠向文件輸出數據。與普通的輸入/輸出流不一樣的是,RandomAccessFile 支持「隨機訪問」的方式,程序能夠直接跳轉到文件的任意地方來讀寫數據。它的最大侷限是隻能讀寫文件,不能讀寫其餘IO節點
RandomAccessFile對象包含一個記錄指針,用以標識當前讀寫處的位置,當程序建立一個新的RandomAccessFile對象時,該對象的文件記錄指針對於文件頭(也就是0處),當讀寫n個字節後,文件記錄指針將會向後移動n個字節。RandomAccessFile包含兩個方法來操做文件記錄指針:
long getFilePointer():返回文件記錄指針的當前位置
void seek(long pos):將文件記錄指針定位到pos位置
RandomAccessFile類在建立對象時,除了指定文件自己,還須要指定一個mode參數,該參數指定RandomAccessFile的訪問模式,該參數有以下四個值:
r:以只讀方式打開指定文件。若是試圖對該RandomAccessFile指定的文件執行寫入方法則會拋出IOException
rw:以讀取、寫入方式打開指定文件。若是該文件不存在,則嘗試建立文件
rws:以讀取、寫入方式打開指定文件。相對於rw模式,還要求對文件的內容或元數據的每一個更新都同步寫入到底層存儲設備
rwd:以讀取、寫入方式打開指定文件。相對於rw模式,還要求對文件的內容的每一個更新都同步寫入到底層存儲設備
import java.io.*; public class RandomAccessFileTest { public static void main(String[] args) { try( RandomAccessFile raf = new RandomAccessFile( "RandomAccessFileTest.java" , "r")) { // 獲取RandomAccessFile對象文件指針的位置,初始位置是0 System.out.println("RandomAccessFile的文件指針的初始位置:" + raf.getFilePointer()); // 移動raf的文件記錄指針的位置 raf.seek(300); byte[] bbuf = new byte[1024]; // 用於保存實際讀取的字節數 int hasRead = 0; // 使用循環來重複「取水」過程 while ((hasRead = raf.read(bbuf)) > 0 ) { // 取出「竹筒」中水滴(字節),將字節數組轉換成字符串輸入! System.out.print(new String(bbuf , 0 , hasRead )); } } catch (IOException ex) { ex.printStackTrace(); } } }
import java.io.*; public class AppendContent { public static void main(String[] args) { try( //以讀、寫方式打開一個RandomAccessFile對象 RandomAccessFile raf = new RandomAccessFile("out.txt" , "rw")) { //將記錄指針移動到out.txt文件的最後 raf.seek(raf.length()); raf.write("追加的內容!\r\n".getBytes()); } catch (IOException ex) { ex.printStackTrace(); } } }
RandomAccessFile若是向文件的指定的位置插入內容,則新輸出的內容會覆蓋文件中原有的內容。若是須要向指定位置插入內容,程序須要先把插入點後面的內容讀入緩衝區,等把須要的插入數據寫入文件後,再將緩衝區的內容追加到文件後面
import java.io.*; public class InsertContent { public static void insert(String fileName, long pos, String insertContent) throws IOException { File tmp = File.createTempFile("tmp" , null); tmp.deleteOnExit(); try( RandomAccessFile raf = new RandomAccessFile(fileName , "rw"); // 使用臨時文件來保存插入點後的數據 FileOutputStream tmpOut = new FileOutputStream(tmp); FileInputStream tmpIn = new FileInputStream(tmp)) { raf.seek(pos); // ------下面代碼將插入點後的內容讀入臨時文件中保存------ byte[] bbuf = new byte[64]; // 用於保存實際讀取的字節數 int hasRead = 0; // 使用循環方式讀取插入點後的數據 while ((hasRead = raf.read(bbuf)) > 0 ) { // 將讀取的數據寫入臨時文件 tmpOut.write(bbuf, 0, hasRead); } // ----------下面代碼插入內容---------- // 把文件記錄指針從新定位到pos位置 raf.seek(pos); // 追加須要插入的內容 raf.write(insertContent.getBytes()); // 追加臨時文件中的內容 while ((hasRead = tmpIn.read(bbuf)) > 0 ) { raf.write(bbuf, 0, hasRead); } } } public static void main(String[] args) throws IOException { insert("InsertContent.java", 45, "插入的內容\r\n"); } }