摘要:當使用節點流進行輸入輸出時,程序直接連接到實際的數據源,和時間的輸入輸出節點連接處理流則用于對一個已存在的流進行連接或封裝,通過封裝后的流來實現數據讀寫功能,處理流也被稱為高級流。
文件的編碼
文本文件就是字節序列,可以是任意編碼形式。在中文操作系統上直接創建文本文件,則該文本文件只能識別ANSI編碼,其他編碼方式會產生亂碼
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編碼:維斯布魯克WestbrookFile類
文件與目錄都是使用File來操作的,File能新建、刪除、重命名文件和目錄,File不能訪問文件內容本身。如果需要訪問文件內容本身,則需要使用輸入/輸出流
訪問文件和目錄訪問文件名相關的方法
String getName():返回此File對象所表示的文件名和路徑名(如果是路徑,則返回最后一級子路徑名)
String getPath():返回此File對象所對應的路徑名
File getAbsoluteFile():返回此File對象的絕對路徑
String getAbsolutePath():返回此File對象所對應的絕對路徑名
String getParent():返回此File對象所對應目錄(最后一級子目錄)的父路徑名
boolean renameTo(File newName):重命名此File對象所對應的文件或目錄,如果重命名成功,則返回true;否則返回false
文件檢測相關方法
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:codingJava路徑")); } }理解Java的IO流
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和ReaderInputStream和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和WriterOutputStream和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全明星陣容 "); fw.write("西部首發:斯蒂芬-庫里、詹姆斯-哈登、凱文-杜蘭特、科懷-倫納德、安東尼-戴維斯 "); fw.write("東部首發:勒布朗-詹姆斯、凱爾-歐文、揚尼斯-阿德托昆博、德瑪爾-德羅贊、吉米-巴特勒 "); fw.write("西部替補:拉塞爾-威斯布魯克、克萊-湯普森、戈登-海沃德、德拉蒙德-格林、德馬庫斯-考辛斯、馬克-加索爾、德安德魯-喬丹 "); fw.write("東部替補:以賽亞-托馬斯、凱爾-洛瑞、肯巴-沃克、約翰-沃爾、保羅-喬治、凱文-樂福、保羅-米爾薩普 "); } 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后進行輸出
在處理處理流包裝了底層節點流之后,關閉輸入/輸出流資源時,只要關閉最上層的處理流即可。關閉最上層的處理流時,系統會自動關閉被該處理流包裝的節點流
Java輸入/輸出流體系中常用的流的分類表分類 | 字節輸入流 | 字節輸出流 | 字符輸入流 | 字符輸出流 |
---|---|---|---|---|
抽象基類 | 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(" "); // 判斷是否還有下一個輸入項 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(); } } }Java虛擬機讀取其他進程的數據
使用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(" "); // 判斷是否還有下一個輸入項 while (sc.hasNext()) { // 輸出輸入項 System.out.println("鍵盤輸入的內容為:" + sc.next()); } } catch (IOException ioe) { ioe.printStackTrace(); } } }RandomAccessFile
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("追加的內容! ".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, "插入的內容 "); } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66541.html
摘要:首先文件讀入輸出流常用有三種,,。可以看出和通道支持的解析流的方式是字節流。以后也可以穿著長袍問別人你知道讀寫文件的種方法么 之前在面試中被問到過兩次Java中文件讀入輸出怎么寫,當時只記得一個大概,沒有辦法很清晰的說出一個條理,今天特地看出總結了一下這方面的內容,想要寫出來給大家分享。 首先文件讀入輸出流常用有三種:FileInputStream/FileOutputStream,F...
摘要:通道可以異步讀寫。使用的方法讀取數據創建一個讀數據緩沖區對象從通道中讀取數據使用的方法寫入數據創建一個寫數據緩沖區對象寫入數據關閉完成使用后,您必須關閉它。五提供了一種被稱為的新功能,也稱為本地矢量。功能是通道提供的并不是。 歷史回顧: Java NIO 概覽 Java NIO 之 Buffer(緩沖區) 其他高贊文章: 面試中關于Redis的問題看這篇就夠了 一文輕松搞懂redis集...
摘要:虛擬機讀取其他進程的數據對象的方法可以運行平臺上的其他程序該方法產生一個對象對象代表由該程序啟動啟動的子進程類提供如下三個方法用于和其子進程通信獲取子進程的錯誤流獲取子進程的輸入流獲取子進程的輸出流這里的輸入流輸出流容易混淆從程序的角度思考 Java虛擬機讀取其他進程的數據 Runtime對象的exec方法可以運行平臺上的其他程序,該方法產生一個Process對象,Process對象...
摘要:當數據被寫入到緩沖區時,線程可以繼續處理它。當滿足下列條件時,表示兩個相等有相同的類型等。調用通道的方法時,可能會導致線程暫時阻塞,就算通道處于非阻塞模式也不例外。當一個通道關閉時,休眠在該通道上的所有線程都將被喚醒并收到一個異常。 1、NIO和I/O區別 I/O和NIO的區別在于數據的打包和傳輸方式。 I/O流的方式處理數據 NIO塊的方式處理數據 面向流 的 I/O 系統一次一...
摘要:而我們現在都已經發布了,的都不知道,這有點說不過去了。而對一個的讀寫也會有響應的描述符,稱為文件描述符,描述符就是一個數字,指向內核中的一個結構體文件路徑,數據區等一些屬性。 前言 只有光頭才能變強 回顧前面: 給女朋友講解什么是代理模式 包裝模式就是這么簡單啦 本來我預想是先來回顧一下傳統的IO模式的,將傳統的IO模式的相關類理清楚(因為IO的類很多)。 但是,發現在整理的過程已...
閱讀 3215·2021-11-23 09:51
閱讀 3558·2021-11-09 09:46
閱讀 3654·2021-11-09 09:45
閱讀 2938·2019-08-29 17:31
閱讀 1860·2019-08-26 13:39
閱讀 2712·2019-08-26 12:12
閱讀 3614·2019-08-26 12:08
閱讀 2235·2019-08-26 11:31