摘要:當使用節(jié)點流進行輸入輸出時,程序直接連接到實際的數(shù)據(jù)源,和時間的輸入輸出節(jié)點連接處理流則用于對一個已存在的流進行連接或封裝,通過封裝后的流來實現(xiàn)數(shù)據(jù)讀寫功能,處理流也被稱為高級流。
文件的編碼
文本文件就是字節(jié)序列,可以是任意編碼形式。在中文操作系統(tǒng)上直接創(chuàng)建文本文件,則該文本文件只能識別ANSI編碼,其他編碼方式會產(chǎn)生亂碼
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(); // 轉(zhuǎn)換成字節(jié)序列用的是項目默認的編碼GBK for (byte b : bs) { // 把字節(jié)(轉(zhuǎn)換成了int)以16進制顯式 System.out.print(Integer.toHexString(b & 0xff) + " "); } System.out.println(); byte[] bs2 = player.getBytes("gbk"); // GBK編碼中文占2個字節(jié),英文占1個字節(jié) for (byte b : bs2) { System.out.print(Integer.toHexString(b & 0xff) + " "); } System.out.println(); byte[] bs3 = player.getBytes("utf-8"); // utf-8編碼中文占3個字節(jié),英文占1個字節(jié) for (byte b : bs3) { System.out.print(Integer.toHexString(b & 0xff) + " "); } System.out.println(); // java是雙字節(jié)編碼utf-16be byte[] bs4 = player.getBytes("utf-16be"); // utf-16be編碼中文占2個字節(jié),英文占2個字節(jié) for (byte b : bs4) { System.out.print(Integer.toHexString(b & 0xff) + " "); } System.out.println(); /* * 當字節(jié)序列是某種編碼時,若想把字節(jié)序列變成字符串 * 需要采用以上編碼方式,否則將出現(xiàn)亂碼 */ // 使用項目默認編碼 String string = new String(bs4); System.out.println("項目默認編碼:" + string); // 使用字符串構(gòu)造的第二個參數(shù) String string2 = new String(bs4, "utf-16be"); System.out.println("utf-16be編碼:" + string2); } }
運行結(jié)果:
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來操作的,F(xiàn)ile能新建、刪除、重命名文件和目錄,F(xiàn)ile不能訪問文件內(nèi)容本身。如果需要訪問文件內(nèi)容本身,則需要使用輸入/輸出流
訪問文件和目錄訪問文件名相關(guān)的方法
String getName():返回此File對象所表示的文件名和路徑名(如果是路徑,則返回最后一級子路徑名)
String getPath():返回此File對象所對應(yīng)的路徑名
File getAbsoluteFile():返回此File對象的絕對路徑
String getAbsolutePath():返回此File對象所對應(yīng)的絕對路徑名
String getParent():返回此File對象所對應(yīng)目錄(最后一級子目錄)的父路徑名
boolean renameTo(File newName):重命名此File對象所對應(yīng)的文件或目錄,如果重命名成功,則返回true;否則返回false
文件檢測相關(guān)方法
boolean exists():判斷File對象所對應(yīng)的文件或目錄是否存在
boolean canWrite():判斷File對象所對應(yīng)的目錄或文件是否可寫
boolean canRead():判斷File對象所對應(yīng)的目錄或文件是否可讀
boolean isFile():判斷File對象所對應(yīng)的是否是文件,而不是目錄
boolean isDirectory():判斷File對象所對應(yīng)的是否是目錄,而不是文件
boolean isAbsolute():判斷File對象所對應(yīng)的文件或目錄是否是絕對路徑。該方法消除了不同平臺的差異,可以直接判斷File對象是否為絕對路徑。在UNIX/Linux/BSD等系統(tǒng)上,如果路徑名開頭是一條斜線(/),則表明該File對象對應(yīng)一個絕對路徑;在Windows等系統(tǒng)上,如果路徑開頭是盤符,則說明它是絕對路徑
獲取常規(guī)文件信息
long lastModified():返回文件最后修改時間
long length():返回文件內(nèi)容的長度
文件操作相關(guān)的方法
boolean createNewFile():當此File對象所對應(yīng)的文件不存在時,該方法將新建的一個該File對象所指定的新文件,如果創(chuàng)建成功則返回true;否則返回false
boolean delete():刪除File對象所對應(yīng)的文件或路徑
static File CreateTempFile(String prefix,String suffix):在默認的臨時文件目錄創(chuàng)建一個臨時空文件,使用給定前綴、系統(tǒng)生成的隨機數(shù)和給定后綴作為文件名。這是一個靜態(tài)方法,可以直接通過File來調(diào)用。preFix參數(shù)必須至少是3個字節(jié)長。建議前綴使用一個短的、有意義的字符串。建議前綴使用一個短的、有意義的字符串,比如”hjb“ 或”main”. suffix參數(shù)可以為null,在這種情況下,將使用默認的后綴”.tmp”
static File CreateTempFile(String prefix,String suffix,File directory):在directory所指定的目錄中創(chuàng)建一個臨時空文件,使用給定前綴、系統(tǒng)生成的隨機數(shù)和給定后綴作為文件名。這是一個靜態(tài)方法,可以直接通過File來調(diào)用
void deleteOnExit():注冊一個刪除鉤子,指定當Java虛擬機退出時,刪除File對象隨對應(yīng)的文件和目錄
目錄操作相關(guān)方法
boolean mkdir(); 試圖創(chuàng)建一個File對象所對應(yīng)的目錄,如果創(chuàng)建成功,則返回true;否則返回false. 調(diào)用該方法時File對象必須對應(yīng)一個路徑,而不是一個文件
String[] list(); 列出File對象的所有子文件名和路徑名,返回String數(shù)組
File[] listFiles(); 列出File對象的所有子文件和路徑,返回File數(shù)組
static File[] listRoots(); 列出系統(tǒng)所有的根路徑。這是一個靜態(tài)方法,可以直接通過File類來調(diào)用
import java.io.*; public class FileTest { public static void main(String[] args) throws IOException { // 以當前路徑來創(chuàng)建一個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()); // 在當前路徑下創(chuàng)建一個臨時文件 File tmpFile = File.createTempFile("aaa", ".txt", file); // 指定當JVM退出時刪除該文件 tmpFile.deleteOnExit(); // 以系統(tǒng)當前時間作為新文件名來創(chuàng)建新文件 File newFile = new File(System.currentTimeMillis() + ""); System.out.println("newFile對象是否存在:" + newFile.exists()); // 以指定newFile對象來創(chuàng)建一個文件 newFile.createNewFile(); // 以newFile對象來創(chuàng)建一個目錄,因為newFile已經(jīng)存在, // 所以下面方法返回false,即無法創(chuàng)建該目錄 newFile.mkdir(); // 使用list()方法來列出當前路徑下的所有文件和路徑 String[] fileList = file.list(); System.out.println("====當前路徑下所有文件和路徑如下===="); for (String fileName : fileList) { System.out.println(fileName); } // listRoots()靜態(tài)方法列出所有的磁盤根路徑。 File[] roots = File.listRoots(); System.out.println("====系統(tǒng)所有根路徑如下===="); for (File root : roots) { System.out.println(root); } } }文件過濾器
File類的list()方法可以接收一個FilenameFilter參數(shù),通過該參數(shù)可以只列出符合條件的文件
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)實現(xiàn)文件過濾器。 // 如果文件名以.java結(jié)尾,或者文件對應(yīng)一個路徑,返回true String[] nameList = file.list((dir, name) -> name.endsWith(".java") || new File(name).isDirectory()); for(String name : nameList) { System.out.println(name); } } }遍歷目錄
實現(xiàn)類:
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(); // 返回字符串數(shù)組 for (String string : filenames) { System.out.println(dir + string); } // 遍歷子目錄下的內(nèi)容,需構(gòu)造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流是實現(xiàn)輸入輸出的基礎(chǔ),它可以方便地實現(xiàn)數(shù)據(jù)的輸入/輸出操作,在Java中把不同的輸入/輸出源抽象為"流"(stream),通過流的方式允許Java程序使用相同的方式來訪問不同的輸入/輸出源。stream是從起源(source)到接收(sink)的有序數(shù)據(jù)
流的分類 輸入流和輸出流按照流的流向來分,可以分為輸入流和輸出流:
輸入流:只能從中讀取數(shù)據(jù),而不能向其寫入數(shù)據(jù)
輸出流:只能向其寫入數(shù)據(jù),而不能從中讀取數(shù)據(jù)
這里的輸入、輸出都是從程序運行所在內(nèi)存的角度來劃分的
Java的輸入流主要由InputStream和Reader作為基類,而輸出流則主要由OutputStream和Writer作為基類。均為抽象類,無法創(chuàng)建實例
字節(jié)流和字符流字節(jié)流和字符流的用法幾乎完全一樣,區(qū)別在于字節(jié)流和字符流所操作的數(shù)據(jù)單元不同--字節(jié)流操作的數(shù)據(jù)單元是8位字節(jié),而字符流操作的數(shù)據(jù)單元是16位的字符
字節(jié)流主要有InputStream和OutputStream作為基類,而字符流則組要由Reader和Writer作為基類
節(jié)點流和處理流按照流的角色來分,可以分為節(jié)點流和處理流:
可以從/從一個特定的IO設(shè)備(如磁盤、網(wǎng)絡(luò))讀/寫數(shù)據(jù)的流,稱為節(jié)點流,節(jié)點流也被稱為低級流(Low Level Stream)。當使用節(jié)點流進行輸入/輸出時,程序直接連接到實際的數(shù)據(jù)源,和時間的輸入/輸出節(jié)點連接
處理流則用于對一個已存在的流進行連接或封裝,通過封裝后的流來實現(xiàn)數(shù)據(jù)讀/寫功能,處理流也被稱為高級流。使用處理流進行輸入/輸出時,程序并不會直接連接到實際的數(shù)據(jù)源,沒有和實際的輸入/輸出節(jié)點連接
使用處理流的一個明顯好處是,只要使用相同的處理流,程序就可以采用完全相同的輸入/輸出代碼來訪問不同的數(shù)據(jù)源,隨著處理流所包裝節(jié)點流的變化,程序?qū)嶋H所訪問的數(shù)據(jù)源也相應(yīng)的發(fā)生變化
流的概念模型InputStream/Reader:所有輸入流的基類,前者是字節(jié)輸入流,后者是字符輸入流
OutputStream/Writer:所有輸出流的基類,前者是字節(jié)輸出流,后者是字符輸出流
處理流的功能主要體現(xiàn)在以下兩個方面:
性能的提高:主要以增加緩沖的方式來提供輸入/輸出的效率
操作的便捷:處理流可能提供了一系列便捷的方法來一次輸入/輸出大批量的內(nèi)容,而不是輸入/輸出一個或多個“水滴”
處理流可以“嫁接”在任何已存在的流的基礎(chǔ)之上,Java應(yīng)用程序采用相同的代碼、透明的方式來訪問不同的輸入/輸出設(shè)備的數(shù)據(jù)流
字節(jié)流和字符流以下介紹4個訪問文件的節(jié)點流用法
InputStream和ReaderInputStream和Reader是所有輸入流的抽象基類,本身不能創(chuàng)建實例來執(zhí)行輸入,是所有輸入流的模板,其方法所有輸入流都可使用
InputStream包含如下3個方法
int read():從輸入流中讀取單個字節(jié),返回所讀取的字節(jié)數(shù)據(jù)(字節(jié)數(shù)據(jù)可直接轉(zhuǎn)換為int類型)
int read(byte[] b):從輸入流中最多讀取b.length個字節(jié)的數(shù)據(jù),并將其存儲在字節(jié)數(shù)組b中,返回實際讀取的字節(jié)數(shù)
int read(byte[] b, int off, int len):從輸入流中最多讀取len個字節(jié)的數(shù)據(jù),并將其存儲在數(shù)組b中,放入數(shù)組b中時,并不是從數(shù)組起點開始,而是從off位置開始,返回實際讀取的字節(jié)數(shù)
在Reader中包含如下3個方法
int read():從輸入流中讀取單個字符,返回所讀取的字符數(shù)據(jù)(字符數(shù)據(jù)可直接轉(zhuǎn)換為int類型)
int read(char[] cbuf):從輸入流中最多讀取cbuf.length個字符的數(shù)據(jù),并將其存儲在字節(jié)數(shù)組cbuf中,返回實際讀取的字符數(shù)
int read(char[] cbuf, int off ,int len):從輸入流中最多讀取len個字符的數(shù)據(jù),并將其存儲在數(shù)組cbuf中,放入數(shù)組cbuf中時,并不是從數(shù)組起點開始,而是從off位置開始,返回實際讀取的字符數(shù)
InputStream和Reader都是抽象類,本身不能創(chuàng)建實例,分別有一個用于讀取文件的輸入流:FileInputStream和FileReader,它們都是節(jié)點流——會直接和指定文件關(guān)聯(lián)
使用FileInputStream讀取自身:
import java.io.*; public class FileInputStreamTest { public static void main(String[] args) throws IOException { // 創(chuàng)建字節(jié)輸入流 FileInputStream fis = new FileInputStream( "FileInputStreamTest.java"); // 創(chuàng)建一個長度為1024的“竹筒” byte[] bbuf = new byte[1024]; // 用于保存實際讀取的字節(jié)數(shù) int hasRead = 0; // 使用循環(huán)來重復(fù)“取水”過程 while ((hasRead = fis.read(bbuf)) > 0 ) { // 取出“竹筒”中水滴(字節(jié)),將字節(jié)數(shù)組轉(zhuǎn)換成字符串輸入! System.out.print(new String(bbuf , 0 , hasRead )); } // 關(guān)閉文件輸入流,放在finally塊里更安全 fis.close(); } }
使用FileReader讀取文件本身:
import java.io.*; public class FileReaderTest { public static void main(String[] args) { try( // 創(chuàng)建字符輸入流 FileReader fr = new FileReader("FileReaderTest.java")) { // 創(chuàng)建一個長度為32的“竹筒” char[] cbuf = new char[32]; // 用于保存實際讀取的字符數(shù) int hasRead = 0; // 使用循環(huán)來重復(fù)“取水”過程 while ((hasRead = fr.read(cbuf)) > 0 ) { // 取出“竹筒”中水滴(字符),將字符數(shù)組轉(zhuǎn)換成字符串輸入! 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個字節(jié)/字符
OutputStream和WriterOutputStream和Writer的用法也非常相似,兩個流都提供了如下三個方法:
void write(int c):將指定的字節(jié)/字符輸出到輸出流中,其中c即可以代表字節(jié),也可以代表字符
void write(byte[]/char[] buf):將字節(jié)數(shù)組/字符數(shù)組中的數(shù)據(jù)輸出到指定輸出流中
void write(byte[]/char[] buf, int off, int len ):將字節(jié)數(shù)組/字符數(shù)組中從off位置開始,長度為len的字節(jié)/字符輸出到輸出流中
因為字符流直接以字符作為操作單位,所以Writer可以用字符串來代替字符數(shù)組,即以String對象作為參數(shù)。Writer里面還包含如下兩個方法
void write(String str):將str字符串里包含的字符輸出到指定輸出流中。
void write (String str, int off, int len):將str字符串里面從off位置開始,長度為len的字符輸出到指定輸出流中
以下程序,使用FileInputStream執(zhí)行輸入,F(xiàn)ileOutputStream執(zhí)行輸出,用以負責FileOutputStreamTest.java文件的功能:
import java.io.*; public class FileOutputStreamTest { public static void main(String[] args) { try( // 創(chuàng)建字節(jié)輸入流 FileInputStream fis = new FileInputStream("FileOutputStreamTest.java"); // 創(chuàng)建字節(jié)輸出流 FileOutputStream fos = new FileOutputStream("newFile.txt")) { byte[] bbuf = new byte[32]; int hasRead = 0; // 循環(huán)從輸入流中取出數(shù)據(jù) while ((hasRead = fis.read(bbuf)) > 0 ) { // 每讀取一次,即寫入文件輸出流,讀了多少,就寫多少。 fos.write(bbuf , 0 , hasRead); } } catch (IOException ioe) { ioe.printStackTrace(); } } }
使用Java的IO流執(zhí)行輸出時,必須關(guān)閉輸出流,關(guān)閉輸出流除了可以保證流的物理資源被回收之外,可能還可以將輸出流緩沖區(qū)中的數(shù)據(jù)flush到物流節(jié)點里(在執(zhí)行close()方法之前,自動執(zhí)行輸出流的flush()方法)
Writer對于直接輸出字符串內(nèi)容有著更好的效果
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("西部首發(fā):斯蒂芬-庫里、詹姆斯-哈登、凱文-杜蘭特、科懷-倫納德、安東尼-戴維斯 "); fw.write("東部首發(fā):勒布朗-詹姆斯、凱爾-歐文、揚尼斯-阿德托昆博、德瑪爾-德羅贊、吉米-巴特勒 "); fw.write("西部替補:拉塞爾-威斯布魯克、克萊-湯普森、戈登-海沃德、德拉蒙德-格林、德馬庫斯-考辛斯、馬克-加索爾、德安德魯-喬丹 "); fw.write("東部替補:以賽亞-托馬斯、凱爾-洛瑞、肯巴-沃克、約翰-沃爾、保羅-喬治、凱文-樂福、保羅-米爾薩普 "); } catch (IOException ioe) { ioe.printStackTrace(); } } }輸入/輸出流體系 處理流的用法
處理流可以隱藏底層設(shè)備上節(jié)點流的差異,并對外提供更加方便的輸入/輸出方法,讓程序員只需關(guān)心高級流的操作
使用處理流的典型思路是,使用處理流來包裝節(jié)點流,程序通過處理流來執(zhí)行輸入/輸出功能,讓節(jié)點流與底層的I/O設(shè)備、文件交互
處理流:流的構(gòu)造器參數(shù)不是一個物理節(jié)點,而是已經(jīng)存在的流;節(jié)點流:都是直接以物理IO節(jié)點作為構(gòu)造器參數(shù)的
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執(zhí)行輸出 ps.println("全明星陣容"); // 使用PrintStream輸出對象 ps.println(new PrintStreamTest()); } catch (IOException ioe) { ioe.printStackTrace(); } } }
輸出文本內(nèi)容,通常將輸出流包裝成PrintStream后進行輸出
在處理處理流包裝了底層節(jié)點流之后,關(guān)閉輸入/輸出流資源時,只要關(guān)閉最上層的處理流即可。關(guān)閉最上層的處理流時,系統(tǒng)會自動關(guān)閉被該處理流包裝的節(jié)點流
Java輸入/輸出流體系中常用的流的分類表分類 | 字節(jié)輸入流 | 字節(jié)輸出流 | 字符輸入流 | 字符輸出流 |
---|---|---|---|---|
抽象基類 | InputStream | OutputStream | Reader | Writer |
訪問文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
訪問數(shù)組 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
訪問管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
訪問字符串 | StringReader | StringWriter | ||
緩沖流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
轉(zhuǎn)換流 | InputStreamReader | OutputStreamWriter | ||
對象流 | ObjectInputStream | ObjectOutputStream | ||
抽象基類 | FilterInputStream | FilterOutputStream | FilterReader | FilterWriter |
打印流 | PrintStream | PrintWriter | ||
推回輸入流 | PushbackInputStream | PushbackReader | ||
特殊流 | DataInputStream | DataOutputStream |
如果進行輸入/輸出的內(nèi)容是文本內(nèi)容,則應(yīng)該考慮使用字符流;如果進行輸入/輸出的內(nèi)容是二進制內(nèi)容,則應(yīng)該考慮使用字節(jié)流
轉(zhuǎn)換流轉(zhuǎn)換流用于實現(xiàn)將字節(jié)流轉(zhuǎn)換成字符流,其中InputStreamReader將字節(jié)輸入流轉(zhuǎn)換成字符輸入流,OutputStreamWriter將字節(jié)輸出流轉(zhuǎn)換成字符輸出流
Java沒有將字符流轉(zhuǎn)換為字節(jié)流的轉(zhuǎn)換流,因為:字節(jié)流比字符流的使用范圍更廣,但字符流比字節(jié)流操作方便。
Java使用System.in代表標準輸入,即鍵盤輸入,但這個標準輸入流是InputStream類的實例,使用不太方便,而且鍵盤輸入內(nèi)容都是文本內(nèi)容,所以可以使用InputStreamReader將其轉(zhuǎn)換成字符輸入流,普通的Reader讀取輸入內(nèi)容時依然不太方便,我們可以將普通的Reader再次包裝成BufferedReader,利用BufferedReader的readLine()方法可以一次讀取一行內(nèi)容
import java.io.*; public class KeyinTest { public static void main(String[] args) { try( // 將Sytem.in對象轉(zhuǎn)換成Reader對象 InputStreamReader reader = new InputStreamReader(System.in); // 將普通Reader包裝成BufferedReader BufferedReader br = new BufferedReader(reader)) { String line = null; // 采用循環(huán)方式來一行一行的讀取 while ((line = br.readLine()) != null) { // 如果讀取的字符串為"exit",程序退出 if (line.equals("exit")) { System.exit(1); } // 打印讀取的內(nèi)容 System.out.println("輸入內(nèi)容為:" + line); } } catch (IOException ioe) { ioe.printStackTrace(); } } }
BufferedReader流具有緩沖功能,可以一次讀取一行文本——以換行符為標志,如果它沒有讀到換行符,則程序阻塞,等到讀到換行符為止
推回輸入流PushbackInputStream、PushbackReader,它們有以下常用方法:
void unread(byte[]/char[] buf):將一個字節(jié)/字符數(shù)組內(nèi)容推回緩沖區(qū)里,從而允許重復(fù)讀取剛剛讀取的內(nèi)容
void unread(byte[]/char[] buf, int off, int len):將一個字節(jié)/字符數(shù)組里從off位置開始讀取,長度是len的字符/字節(jié)的內(nèi)容推回到推回緩沖區(qū)里,從而允許重復(fù)剛才讀取的內(nèi)容
void unread(int b):將一個字節(jié)、字符推回到推回緩沖區(qū)里,從而允許重復(fù)讀取剛剛讀取的內(nèi)容
兩個推回輸入流都帶有一個推回緩沖區(qū),當程序調(diào)用unread()方法時,系統(tǒng)就會把指定數(shù)組的內(nèi)容推回到該緩沖區(qū),而推回輸入流每次調(diào)用read()方法時,總會先從推回緩沖區(qū)讀取,只有完全讀取了緩沖區(qū)里面的內(nèi)容后,且還沒有裝滿read()所需的數(shù)組時,才會到原輸入流中讀取內(nèi)容
import java.io.*; public class PushbackTest { public static void main(String[] args) { try( // 創(chuàng)建一個PushbackReader對象,指定推回緩沖區(qū)的長度為64 PushbackReader pr = new PushbackReader(new FileReader("PushbackTest.java") , 64)) { char[] buf = new char[32]; // 用以保存上次讀取的字符串內(nèi)容 String lastContent = ""; int hasRead = 0; // 循環(huán)讀取文件內(nèi)容 while ((hasRead = pr.read(buf)) > 0) { // 將讀取的內(nèi)容轉(zhuǎn)換成字符串 String content = new String(buf , 0 , hasRead); int targetIndex = 0; // 將上次讀取的字符串和本次讀取的字符串拼起來, // 查看是否包含目標字符串, 如果包含目標字符串 if ((targetIndex = (lastContent + content) .indexOf("new PushbackReader")) > 0) { // 將本次內(nèi)容和上次內(nèi)容一起推回緩沖區(qū) pr.unread((lastContent + content).toCharArray()); // 重新定義一個長度為targetIndex的char數(shù)組 if(targetIndex > 32) { buf = new char[targetIndex]; } // 再次讀取指定長度的內(nèi)容(就是目標字符串之前的內(nèi)容) pr.read(buf , 0 , targetIndex); // 打印讀取的內(nèi)容 System.out.print(new String(buf , 0 ,targetIndex)); System.exit(0); } else { // 打印上次讀取的內(nèi)容 System.out.print(lastContent); // 將本次內(nèi)容設(shè)為上次讀取的內(nèi)容 lastContent = content; } } } catch (IOException ioe) { ioe.printStackTrace(); } } }重定向標準輸入/輸出
Java的標準輸入/輸出分別通過System.in和System.out來代表,在默認的情況下分別代表鍵盤和顯示器,當程序通過System.in來獲得輸入時,實際上是通過鍵盤獲得輸入。當程序通過System.out執(zhí)行輸出時,程序總是輸出到屏幕
在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創(chuàng)建Scanner對象,用于獲取標準輸入 Scanner sc = new Scanner(System.in); // 增加下面一行將只把回車作為分隔符 sc.useDelimiter(" "); // 判斷是否還有下一個輸入項 while(sc.hasNext()) { // 輸出輸入項 System.out.println("鍵盤輸入的內(nèi)容是:" + sc.next()); } } catch (IOException ex) { ex.printStackTrace(); } } }重定向“標準”輸出流
import java.io.*; public class RedirectOut { public static void main(String[] args) { try( // 一次性創(chuàng)建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虛擬機讀取其他進程的數(shù)據(jù)
使用Runtime對象的exec()方法運行平臺上的其他程序并產(chǎn)生一個Process對象,該對象代表由該Java程序啟動的子進程,Process類提供了如下3個方法,用于讓程序和其子進程進行通訊:
InputStream getErrorStream():獲取子進程的錯誤流
InputStream getInputStream():獲取子進程的輸入流
OutputStream getOutputStream():獲取子進程的輸出流
子進程讀取Java程序的數(shù)據(jù),就是讓Java程序把數(shù)據(jù)輸出到子進程中,使用輸出流
下面的代碼實現(xiàn)了獲取子進程的錯誤輸出
import java.io.*; public class ReadFromProcess { public static void main(String[] args) throws IOException { // 運行javac命令,返回運行該命令的子進程 Process p = Runtime.getRuntime().exec("javac"); try( // 以p進程的錯誤流創(chuàng)建BufferedReader對象 // 這個錯誤流對本程序是輸入流,對p進程則是輸出流 BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()))) { String buff = null; // 采取循環(huán)方式來讀取p進程的錯誤輸出 while((buff = br.readLine()) != null) { System.out.println(buff); } } } }
Java程序中啟動Java虛擬機運行另一個Java程序,并向另一Java程序輸入數(shù)據(jù):
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進程的輸出流創(chuàng)建PrintStream對象 // 這個輸出流對本程序是輸出流,對p進程則是輸入流 PrintStream ps = new PrintStream(p.getOutputStream())) { // 向ReadStandard程序?qū)懭雰?nèi)容,這些內(nèi)容將被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創(chuàng)建Scanner對象,用于獲取標準輸入 Scanner sc = new Scanner(System.in); PrintStream ps = new PrintStream(new FileOutputStream("outtext.txt")) ) { // 只把回車作為分隔符 sc.useDelimiter(" "); // 判斷是否還有下一個輸入項 while (sc.hasNext()) { // 輸出輸入項 System.out.println("鍵盤輸入的內(nèi)容為:" + sc.next()); } } catch (IOException ioe) { ioe.printStackTrace(); } } }RandomAccessFile
RandomAccessFile是Java輸入輸出流體系中功能最豐富的文件內(nèi)容訪問類,它提供了眾多的方法來訪問文件內(nèi)容,它即可以讀取文件內(nèi)容,也可以向文件輸出數(shù)據(jù)。與普通的輸入/輸出流不同的是,RandomAccessFile 支持“隨機訪問”的方式,程序可以直接跳轉(zhuǎn)到文件的任意地方來讀寫數(shù)據(jù)。它的最大局限是只能讀寫文件,不能讀寫其他IO節(jié)點
RandomAccessFile對象包含一個記錄指針,用以標識當前讀寫處的位置,當程序創(chuàng)建一個新的RandomAccessFile對象時,該對象的文件記錄指針對于文件頭(也就是0處),當讀寫n個字節(jié)后,文件記錄指針將會向后移動n個字節(jié)。RandomAccessFile包含兩個方法來操作文件記錄指針:
long getFilePointer():返回文件記錄指針的當前位置
void seek(long pos):將文件記錄指針定位到pos位置
RandomAccessFile類在創(chuàng)建對象時,除了指定文件本身,還需要指定一個mode參數(shù),該參數(shù)指定RandomAccessFile的訪問模式,該參數(shù)有如下四個值:
r:以只讀方式打開指定文件。如果試圖對該RandomAccessFile指定的文件執(zhí)行寫入方法則會拋出IOException
rw:以讀取、寫入方式打開指定文件。如果該文件不存在,則嘗試創(chuàng)建文件
rws:以讀取、寫入方式打開指定文件。相對于rw模式,還要求對文件的內(nèi)容或元數(shù)據(jù)的每個更新都同步寫入到底層存儲設(shè)備
rwd:以讀取、寫入方式打開指定文件。相對于rw模式,還要求對文件的內(nèi)容的每個更新都同步寫入到底層存儲設(shè)備
訪問指定的中間部分數(shù)據(jù)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]; // 用于保存實際讀取的字節(jié)數(shù) int hasRead = 0; // 使用循環(huán)來重復(fù)“取水”過程 while ((hasRead = raf.read(bbuf)) > 0 ) { // 取出“竹筒”中水滴(字節(jié)),將字節(jié)數(shù)組轉(zhuǎn)換成字符串輸入! System.out.print(new String(bbuf , 0 , hasRead )); } } catch (IOException ex) { ex.printStackTrace(); } } }向指定文件后追加內(nèi)容
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("追加的內(nèi)容! ".getBytes()); } catch (IOException ex) { ex.printStackTrace(); } } }向指定文件、指定位置插入內(nèi)容
RandomAccessFile如果向文件的指定的位置插入內(nèi)容,則新輸出的內(nèi)容會覆蓋文件中原有的內(nèi)容。如果需要向指定位置插入內(nèi)容,程序需要先把插入點后面的內(nèi)容讀入緩沖區(qū),等把需要的插入數(shù)據(jù)寫入文件后,再將緩沖區(qū)的內(nèi)容追加到文件后面
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"); // 使用臨時文件來保存插入點后的數(shù)據(jù) FileOutputStream tmpOut = new FileOutputStream(tmp); FileInputStream tmpIn = new FileInputStream(tmp)) { raf.seek(pos); // ------下面代碼將插入點后的內(nèi)容讀入臨時文件中保存------ byte[] bbuf = new byte[64]; // 用于保存實際讀取的字節(jié)數(shù) int hasRead = 0; // 使用循環(huán)方式讀取插入點后的數(shù)據(jù) while ((hasRead = raf.read(bbuf)) > 0 ) { // 將讀取的數(shù)據(jù)寫入臨時文件 tmpOut.write(bbuf, 0, hasRead); } // ----------下面代碼插入內(nèi)容---------- // 把文件記錄指針重新定位到pos位置 raf.seek(pos); // 追加需要插入的內(nèi)容 raf.write(insertContent.getBytes()); // 追加臨時文件中的內(nèi)容 while ((hasRead = tmpIn.read(bbuf)) > 0 ) { raf.write(bbuf, 0, hasRead); } } } public static void main(String[] args) throws IOException { insert("InsertContent.java", 45, "插入的內(nèi)容 "); } }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/66541.html
摘要:首先文件讀入輸出流常用有三種,,。可以看出和通道支持的解析流的方式是字節(jié)流。以后也可以穿著長袍問別人你知道讀寫文件的種方法么 之前在面試中被問到過兩次Java中文件讀入輸出怎么寫,當時只記得一個大概,沒有辦法很清晰的說出一個條理,今天特地看出總結(jié)了一下這方面的內(nèi)容,想要寫出來給大家分享。 首先文件讀入輸出流常用有三種:FileInputStream/FileOutputStream,F(xiàn)...
摘要:通道可以異步讀寫。使用的方法讀取數(shù)據(jù)創(chuàng)建一個讀數(shù)據(jù)緩沖區(qū)對象從通道中讀取數(shù)據(jù)使用的方法寫入數(shù)據(jù)創(chuàng)建一個寫數(shù)據(jù)緩沖區(qū)對象寫入數(shù)據(jù)關(guān)閉完成使用后,您必須關(guān)閉它。五提供了一種被稱為的新功能,也稱為本地矢量。功能是通道提供的并不是。 歷史回顧: Java NIO 概覽 Java NIO 之 Buffer(緩沖區(qū)) 其他高贊文章: 面試中關(guān)于Redis的問題看這篇就夠了 一文輕松搞懂redis集...
摘要:虛擬機讀取其他進程的數(shù)據(jù)對象的方法可以運行平臺上的其他程序該方法產(chǎn)生一個對象對象代表由該程序啟動啟動的子進程類提供如下三個方法用于和其子進程通信獲取子進程的錯誤流獲取子進程的輸入流獲取子進程的輸出流這里的輸入流輸出流容易混淆從程序的角度思考 Java虛擬機讀取其他進程的數(shù)據(jù) Runtime對象的exec方法可以運行平臺上的其他程序,該方法產(chǎn)生一個Process對象,Process對象...
摘要:當數(shù)據(jù)被寫入到緩沖區(qū)時,線程可以繼續(xù)處理它。當滿足下列條件時,表示兩個相等有相同的類型等。調(diào)用通道的方法時,可能會導(dǎo)致線程暫時阻塞,就算通道處于非阻塞模式也不例外。當一個通道關(guān)閉時,休眠在該通道上的所有線程都將被喚醒并收到一個異常。 1、NIO和I/O區(qū)別 I/O和NIO的區(qū)別在于數(shù)據(jù)的打包和傳輸方式。 I/O流的方式處理數(shù)據(jù) NIO塊的方式處理數(shù)據(jù) 面向流 的 I/O 系統(tǒng)一次一...
摘要:而我們現(xiàn)在都已經(jīng)發(fā)布了,的都不知道,這有點說不過去了。而對一個的讀寫也會有響應(yīng)的描述符,稱為文件描述符,描述符就是一個數(shù)字,指向內(nèi)核中的一個結(jié)構(gòu)體文件路徑,數(shù)據(jù)區(qū)等一些屬性。 前言 只有光頭才能變強 回顧前面: 給女朋友講解什么是代理模式 包裝模式就是這么簡單啦 本來我預(yù)想是先來回顧一下傳統(tǒng)的IO模式的,將傳統(tǒng)的IO模式的相關(guān)類理清楚(因為IO的類很多)。 但是,發(fā)現(xiàn)在整理的過程已...
閱讀 3222·2021-11-23 09:51
閱讀 3566·2021-11-09 09:46
閱讀 3673·2021-11-09 09:45
閱讀 2949·2019-08-29 17:31
閱讀 1867·2019-08-26 13:39
閱讀 2726·2019-08-26 12:12
閱讀 3624·2019-08-26 12:08
閱讀 2243·2019-08-26 11:31