Java—— 流(Stream)、檔案(File)和IO
參考於:https://blog.csdn.net/qq_22063697/article/details/52137369
版權宣告:本文為博主原創文章,轉載請附上博文連結!
一. 什麼是 IO
Java中 I/O 操作主要是指使用 Java 進行輸入,輸出操作。 Java所有的I/O機制都是基於資料流進行輸入輸出,這些資料流表示了字元或者位元組資料的流動序列。
Java 的 I/O 流提供了讀寫資料的標準方法。任何Java中表示資料來源的物件都會提供以資料流的方式讀寫它的資料的方法。
二. 資料流的基本概念
基本流:一組有序,有起點和終點的位元組的資料序列。包括輸入流和輸出流。
輸入流:程式從輸入流讀取資料來源。資料來源包括外界(鍵盤、檔案、網路…),即是將資料來源讀入到程式的通訊通道。
輸出流:程式向輸出流寫入資料。將程式中的資料輸出到外界(顯示器、印表機、檔案、網路…)的通訊通道。
為什麼設計成資料流呢?
InputStream不關心資料來源來自何種裝置(鍵盤,檔案,網路)
OutputStream不關心資料的目的是何種裝置(鍵盤,檔案,網路)
採用資料流的目的就是使得輸出輸入獨立於裝置。
三. I/O體系結構
在整個Java.io包中最重要的就是5個類和一個介面。5個類指的是File、OutputStream、InputStream、Writer、Reader;一個介面指的是Serializable.
Java I/O主要包括如下幾個層次,包含三個部分:
1.流式部分――IO的主體部分;
2.非流式部分――主要包含一些輔助流式部分的類,如:File類、RandomAccessFile類和FileDescriptor等類;
3.其他類–檔案讀取部分的與安全相關的類,如:SerializablePermission類,以及與本地作業系統相關的檔案系統的類,如:FileSystem類和Win32FileSystem類和WinNTFileSystem類。
1. 流式部分
Java中字元是採用Unicode標準,一個字元是16位,即一個字元使用兩個位元組來表示。為此,JAVA中引入了處理字元的流。
a. 對檔案進行操作
FileInputStream(位元組輸入流),FileOutputStream(位元組輸出流),FileReader(字元輸入流),FileWriter(字元輸出流)
b. 對管道進行操作
PipedInputStream(位元組輸入流),PipedOutputStream(位元組輸出流),PipedReader(字元輸入流),PipedWriter(字元輸出流)
PipedInputStream的一個例項要和PipedOutputStream的一個例項共同使用,共同完成管道的讀取寫入操作。主要用於執行緒操作。
c. 位元組/字元陣列
ByteArrayInputStream,ByteArrayOutputStream,CharArrayReader,CharArrayWriter是在記憶體中開闢了一個位元組或字元陣列。
d. Buffered緩衝流
BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter,是帶緩衝區的處理流。
緩衝區的作用的主要目的是:避免每次和硬碟打交道,提高資料訪問的效率。
e. 轉化流
InputStreamReader/OutputStreamWriter,把位元組轉化成字元。
f. 資料流
DataInputStream,DataOutputStream。
因為平時若是我們輸出一個8個位元組的long型別或4個位元組的float型別,那怎麼辦呢?
可以一個位元組一個位元組輸出,也可以把轉換成字串輸出,但是這樣轉換費時間,若是直接輸出該多好啊,因此這個資料流就解決了我們輸出資料型別的困難。
資料流可以直接輸出float型別或long型別,提高了資料讀寫的效率。
g. 列印流
printStream,printWriter,一般是列印到控制檯,可以進行控制列印的地方。
h. 物件流
ObjectInputStream,ObjectOutputStream,把封裝的物件直接輸出,而不是一個個在轉換成字串再輸出。
i. 序列化流
SequenceInputStream。
物件序列化:把物件直接轉換成二進位制,寫入介質中。
【注】
使用物件流需要實現Serializable介面,否則會報錯。
而若用transient關鍵字修飾成員變數,不寫入該成員變數,若是引用型別的成員變數為null,值型別的成員變數為0.
2. 非流式部分主要類
File(檔案特徵與管理):用於檔案或者目錄的描述資訊,例如生成新目錄,修改檔名,刪除檔案,判斷檔案所在路徑等。
RandomAccessFile(隨機檔案操作):它的功能豐富,可以從檔案的任意位置進行存取(輸入輸出)操作。
File類:
在Java語言的java.io包中,由File類提供了描述檔案和目錄的操作與管理方法。
但File類不是InputStream、OutputStream或Reader、Writer的子類,因為它不負責資料的輸入輸出,而專門用來管理磁碟檔案與目錄。
作用:File類主要用於命名檔案、查詢檔案屬性和處理檔案目錄。
a. File類
共提供了四個不同的建構函式,以不同的引數形式靈活地接收檔案和目錄名資訊。建構函式:
1)File (String pathname)
通過將給定路徑名字串轉換成抽象路徑名來建立一個新 File 例項。
File f1=new File("FileTest1.txt"); //建立檔案物件f1,f1所指的檔案是在當前目錄下建立的FileTest1.txt
2)File(URI uri)
通過將給定的 file: URI 轉換成一個抽象路徑名來建立一個新的 File 例項。
3)File (String parent , String child)
根據 parent 路徑名字串和 child 路徑名字串建立一個新 File 例項。
// 注意:D:\\dir1目錄事先必須存在,否則異常
File f2=new File(“D:\\dir1","FileTest2.txt");
4)File (File parent , String child)
通過給定的父抽象路徑名和子路徑名字串建立一個新的File例項。
File f4=new File("E:\\dir3");
File f5=new File(f4,"FileTest5.txt"); //在如果 E:\\dir3目錄不存在則需要先使用f4.mkdir()先建立
一個對應於某磁碟檔案或目錄的File物件一經建立, 就可以通過呼叫它的方法來獲得檔案或目錄的屬性。
序號 | 方法描述 |
---|---|
1 | public String getName() 返回由此抽象路徑名錶示的檔案或目錄的名稱。 |
2 | public String getPath() 將此抽象路徑名轉換為一個路徑名字串。 |
3 | public String getAbsolutePath() 返回抽象路徑名的絕對路徑名字串。 |
4 | public boolean exists() 測試此抽象路徑名錶示的檔案或目錄是否存在。 |
5 | public boolean isDirectory() 測試此抽象路徑名錶示的檔案是否是一個目錄。 |
6 | public boolean isFile() 測試此抽象路徑名錶示的檔案是否是一個標準檔案。 |
7 | public long length() 返回由此抽象路徑名錶示的檔案的長度。 |
8 | public boolean delete() 刪除此抽象路徑名錶示的檔案或目錄。 |
9 | public String[] list() 返回由此抽象路徑名所表示的目錄中的檔案和目錄的名稱所組成字串陣列。 |
10 | public File[] listFiles() 返回一個抽象路徑名陣列,這些路徑名錶示此抽象路徑名所表示目錄中的檔案。 |
11 | public boolean mkdir() 建立此抽象路徑名指定的目錄。 |
12 | public boolean mkdirs() 建立此抽象路徑名指定的目錄,包括建立必需但不存在的父目錄。 |
13 | public boolean renameTo(File dest) 重新命名此抽象路徑名錶示的檔案。 |
14 | public boolean equals(Object obj) 測試此抽象路徑名與給定物件是否相等。 |
15 | public String toString() 返回此抽象路徑名的路徑名字串。 |
【例】輸出一個目錄中的所有檔名(目錄可能是多級目錄,如a目錄中有b、c目錄。。。)
package com.zth;
import java.io.File;
import java.io.IOException;
class FileList{
public static void listDir( String dir) throws IOException{
File file = new File(dir);
// 傳進來的可能不是一個路徑
if(!file.isDirectory()){
throw new IOException(dir + "不是目錄");
}
// 傳進來的可能是一個錯誤的路徑
if(file == null){
throw new IOException("沒有此路徑");
}
File[] files = file.listFiles();
for(File f:files){
// 有可能是一個多級目錄,遞迴呼叫
if(f.isDirectory()){
listDir(f.getAbsolutePath());
}else{
// 是檔案就直接輸出該檔案的絕對路徑
System.out.println(f.getAbsolutePath());
}
}
}
}
public class Test{
public static void main(String[] args) throws IOException{
FileList.listDir("E:/aa");
}
}
執行結果:
E:\aa\a.txt
E:\aa\b.txt
E:\aa\cc\a.txt
E:\aa\cc\b.txt
四. 常用流類
1. InputStream 抽象類
InputStream 為位元組輸入流,它本身為一個抽象類,必須依靠其子類實現各種功能,此抽象類是表示位元組輸入流的所有類的超類。
繼承自InputStream 的流都是向程式中輸入資料的,且資料單位為位元組(8bit);
InputStream是輸入位元組資料用的類,所以InputStream類提供了3種過載的read方法.Inputstream類中的常用方法:
(1) public abstract int read( ):讀取一個byte的資料,返回值是高位補0的int型別值。若返回值=-1說明沒有讀取到任何位元組讀取工作結束。
(2) public int read(byte b[ ]):讀取b.length個位元組的資料放到b陣列中。返回值是讀取的位元組數。
(3) public int read(byte b[ ], int off, int len):從輸入流中最多讀取len個位元組的資料,存放到偏移量為off的b陣列中。
(4) public int available( ):返回輸入流中可以讀取的位元組數。注意:若輸入阻塞,當前執行緒將被掛起,如果InputStream物件呼叫這個方法的話,它只會返回0,這個方法必須由繼承InputStream類的子類物件呼叫才有用,
(5) public long skip(long n):忽略輸入流中的n個位元組,返回值是實際忽略的位元組數, 跳過一些位元組來讀取
(6) public int close( ) :我們在使用完後,必須對我們開啟的流進行關閉.
主要的子類
1) FileInputStream:把一個檔案作為InputStream,實現對檔案的讀取操作
2) ByteArrayInputStream:把記憶體中的一個緩衝區作為InputStream使用
3) StringBufferInputStream:把一個String物件作為InputStream
4) PipedInputStream:實現了pipe的概念,主要線上程中使用
5) SequenceInputStream:把多個InputStream合併為一個InputStream
2. OutputStream抽象類
OutputStream提供了3個write方法來做資料的輸出,這個是和InputStream是相對應的。
(1) public void write(byte b[ ]):將引數b中的位元組寫到輸出流。
(2)public void write(byte b[ ], int off, int len) :將引數b的從偏移量off開始的len個位元組寫到輸出流。
(3) public abstract void write(int b) :先將int轉換為byte型別,把低位元組寫入到輸出流中。
(4)public void flush( ) : 將資料緩衝區中資料全部輸出,並清空緩衝區。
(5) public void close( ) : 關閉輸出流並釋放與流相關的系統資源。
主要的子類
1) ByteArrayOutputStream:把資訊存入記憶體中的一個緩衝區中
2) FileOutputStream:把資訊存入檔案中
3) PipedOutputStream:實現了pipe的概念,主要線上程中使用
4) SequenceOutputStream:把多個OutStream合併為一個OutStream
注:
流結束的判斷:方法read()的返回值為-1時;readLine()的返回值為null時。
3、FileInputStream檔案輸入流
FileInputStream可以使用read()方法一次讀入一個位元組,並以int型別返回,或者是使用read()方法時讀入至一個byte陣列,byte陣列的元素有多少個,就讀入多少個位元組。
在將整個檔案讀取完成或寫入完畢的過程中,這麼一個byte陣列通常被當作緩衝區,因為這麼一個byte陣列通常扮演承接資料的中間角色。
作用:以檔案作為資料輸入源的資料流。或者說是開啟檔案,從檔案讀資料到記憶體的類。
使用方法:
FileInputStream fis = new FileInputStream(“E:\a.txt”);
或 FileInputStream fis = new FileInputStream(“E:/a.txt”);
當然也可以傳一個 File ,它還有好多個構造器。
4. FileOutputStream檔案輸出流
FileOutputStream類用來處理以檔案作為資料輸出目的資料流;一個表示檔名的字串,也可以是File或FileDescriptor物件。
作用:用來處理以檔案作為資料輸出目的資料流;或者說是從記憶體區讀資料到檔案
建立檔案流的方式:
1)FileOutputStream(File file)
建立一個向指定 File 物件表示的檔案中寫入資料的檔案輸出流。
2)FileOutputStream(File file, boolean append)
建立一個向指定 File 物件表示的檔案中寫入資料的檔案輸出流。 append表示內容是否追加
3)FileOutputStream(FileDescriptor fdObj)
建立一個向指定檔案描述符處寫入資料的輸出檔案流,該檔案描述符表示一
個到檔案系統中的某個實際檔案的現有連線。
4)FileOutputStream(String name)
建立一個向具有指定名稱的檔案中寫入資料的輸出檔案流。
例:FileOutputStream out=new FileOutputStream(“d:/myjava/write.txt “);
5)FileOutputStream(String name, boolean append)
建立一個向具有指定 name 的檔案中寫入資料的輸出檔案流。 append表示內容是否追加
【注】
(1)檔案中寫資料時,若檔案已經存在,則覆蓋存在的檔案;
(2)在讀/寫操作結束時,應呼叫close方法關閉流。
5、緩衝輸入輸出流 BufferedInputStream/ BufferedOutputStream(包裝流)
BufferedInputStream:當向緩衝流寫入資料時候,資料先寫到緩衝區,待緩衝區寫滿後,系統一次性將資料傳送給輸出裝置。
BufferedOutputStream :當從向緩衝流讀取資料時候,系統先從緩衝區讀出資料,待緩衝區為空時,系統再從輸入裝置讀取資料到緩衝區。
a、將檔案讀入記憶體:
將BufferedInputStream與FileInputStream相接
FileInputStream in=new FileInputStream( “file1.txt “);
BufferedInputStream bin=new BufferedInputStream(in);
b、將記憶體寫入檔案:
將BufferedOutputStream與 FileOutputStream相接
FileOutputStreamout=new FileOutputStream(“file2.txt”);
BufferedOutputStream bin=new BufferedInputStream(out);
c、鍵盤輸入流讀到記憶體
將BufferedReader與標準的資料流相接
InputStreamReader sin=new InputStreamReader (System.in) ;
BufferedReader bin=new BufferedReader(sin);
6、Writer/Reader字元流
a、Reader抽象類
用於讀取字元流的抽象類。子類必須實現的方法只有 read(char[], int, int) 和 close()。但是,多數子類將重寫此處定義的一些方法,以提供更高的效率和/或其他功能。
子類:
(1) 用指定字元陣列作為引數:CharArrayReader(char[])
將字元陣列作為輸入流:CharArrayReader(char[], int, int)
讀取字串,建構函式如下: public StringReader(String s);
2) CharArrayReader:與ByteArrayInputStream對應
3) StringReader : 與StringBufferInputStream對應
4) InputStreamReader
從輸入流讀取位元組,在將它們轉換成字元:Public inputstreamReader(inputstream is);
5) FilterReader: 允許過濾字元流
protected filterReader(Reader r);
6) BufferReader :接受Reader物件作為引數,並對其新增字元緩衝器,使用readline()方法可以讀取一行。
Public BufferReader(Reader r);
主要方法:
(1) public int read() throws IOException; //讀取一個字元,返回值為讀取的字元
(2) public int read(char cbuf[]) throws IOException; /*讀取一系列字元到陣列cbuf[]中,返回值為實際讀取的字元的數量*/
(3) public abstract int read(char cbuf[],int off,int len) throws IOException; /*讀取len個字元,從陣列cbuf[]的下標off處開始存放,返回值為實際讀取的字元數量,該方法必須由子類實現*/
b、 Writer抽象類
寫入字元流的抽象類。子類必須實現的方法僅有 write(char[], int, int)、flush() 和 close()。但是,多數子類將重寫此處定義的一些方法,以提供更高的效率和/或其他功能。
子類:
1) FileWrite: 與FileOutputStream對應
將字元型別資料寫入檔案,使用預設字元編碼和緩衝器大小。
Public FileWrite(file f);
2) chararrayWrite:與ByteArrayOutputStream對應 ,將字元緩衝器用作輸出。
Public CharArrayWrite();
3) PrintWrite:生成格式化輸出
public PrintWriter(outputstream os);
4) filterWriter:用於寫入過濾字元流
protected FilterWriter(Writer w);
5) PipedWriter:與PipedOutputStream對應
6) StringWriter:無與之對應的以位元組為導向的stream
主要方法:
(1) public void write(int c) throws IOException; //將整型值c的低16位寫入輸出流
(2) public void write(char cbuf[]) throws IOException; //將字元陣列cbuf[]寫入輸出流
(3) public abstract void write(char cbuf[],int off,int len) throws IOException; //將字元陣列cbuf[]中的從索引為off的位置處開始的len個字元寫入輸出流
(4) public void write(String str) throws IOException; //將字串str中的字元寫入輸出流
(5) public void write(String str,int off,int len) throws IOException; //將字串str 中從索引off開始處的len個字元寫入輸出流
(6) flush( ) //刷空輸出流,並輸出所有被快取的位元組。
(7)close() 關閉流 public abstract void close() throws IOException
五. Java IO 的一般使用原則
一)按資料來源(去向)分類:
1 、是檔案: FileInputStream, FileOutputStream, ( 位元組流 )FileReader, FileWriter( 字元 )
2 、是 byte[] : ByteArrayInputStream, ByteArrayOutputStream( 位元組流 )
3 、是 Char[]: CharArrayReader, CharArrayWriter( 字元流 )
4 、是 String: StringBufferInputStream, StringBufferOuputStream ( 位元組流 )StringReader, StringWriter( 字元流 )
5 、網路資料流: InputStream, OutputStream,( 位元組流 ) Reader, Writer( 字元流 )
二)按是否格式化輸出分:
1 、要格式化輸出: PrintStream, PrintWriter
三)按是否要緩衝分:
1 、要緩衝: BufferedInputStream, BufferedOutputStream,( 位元組流 ) BufferedReader, BufferedWriter( 字元流 )
四)按資料格式分:
1 、二進位制格式(只要不能確定是純文字的,比如圖片、音訊、視訊) : InputStream, OutputStream 及其所有帶 Stream 結尾的子類
2 、純文字格式(含純英文與漢字或其他編碼方式); Reader, Writer 及其所有帶 Reader, Writer 的子類
五)按輸入輸出分:
1 、輸入: Reader, InputStream 型別的子類
2 、輸出: Writer, OutputStream 型別的子類
六)特殊需要:
1 、從 Stream 到 Reader,Writer 的轉換類: InputStreamReader, OutputStreamWriter
2 、物件輸入輸出: ObjectInputStream, ObjectOutputStream
3 、程序間通訊: PipeInputStream, PipeOutputStream, PipeReader, PipeWriter
4 、合併輸入: SequenceInputStream
5 、更特殊的需要: PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader
六. 標準輸入輸出
1. 控制檯輸入
a. 讀取控制檯輸入
Java 的控制檯輸入由 System.in 完成。
為了獲得一個繫結到控制檯的字元流,你可以把 System.in 包裝在一個 BufferedReader 物件中來建立一個字元流。
下面是建立 BufferedReader 的基本語法:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedReader 物件建立後,我們便可以使用 read() 方法從控制檯讀取一個字元,或者用 readLine() 方法讀取一個字串。
b. 從控制檯讀取多字元輸入
從 BufferedReader 物件讀取一個字元要使用 read() 方法,它的語法如下:
int read( ) throws IOException
每次呼叫 read() 方法,它從輸入流讀取一個字元並把該字元作為整數值返回。 當流結束的時候返回 -1。該方法丟擲 IOException。
package com.zth;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
public class Demo0 {
public static void main(String[] args) throws IOException {
Reader in = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(in);
System.out.println("輸入字元,輸入q結束");
char c;
do{
c = (char)br.read();
System.out.println(c);
}while(c != 'q');
}
}
執行結果:
輸入字元,輸入q結束
hello
h
e
l
l
o
q
q
c. 從控制檯讀取字串
從標準輸入讀取一個字串需要使用 BufferedReader 的 readLine() 方法。
它的一般格式是:
String readLine( ) throws IOException
package com.zth;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
public class Demo0 {
public static void main(String[] args) throws IOException {
Reader in = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(in);
System.out.println("Enter lines of text and enter end to quit");
String str;
do{
str = br.readLine();
System.out.println(str);
}while(!"end".equals(str));
}
}
執行結果:
Enter lines of text and enter end to quit
123
123
abc
abc
縱有紅顏,百生千劫
縱有紅顏,百生千劫
end
end
2. 控制檯輸出
控制檯的輸出由 print( ) 和 println() 完成。這些方法都由類 PrintStream 定義,System.out 是該類物件的一個引用。
PrintStream 繼承了 OutputStream類,並且實現了方法 write()。這樣,write() 也可以用來往控制檯寫操作。
PrintStream 定義 write() 的最簡單格式如下所示:
void write(int byteval)
該方法將 byteval 的低八位位元組寫到流中。
七. 例項
1. 過濾檔案字尾名
有時需要列出目錄下指定型別的檔案,例如:. java、. txt 等副檔名的檔案。
【例】找出“e:/aa”下的所有 .java 檔案
package com.zth;
import java.io.File;
import java.io.IOException;
public class Demo0{
public static void main(String[] args) throws Exception{
listFile("e:/aa");
}
public static void listFile(String str) throws Exception{
File file = new File(str);
if(! file.isDirectory()){
throw new IOException("不是目錄");
}
if(file == null){
throw new IOException("沒有此路徑");
}
File[] files = file.listFiles();
for(File f:files){
if(f.isDirectory()){
listFile(f.toString());
}else{
if(f.toString().endsWith(".java")){
System.out.println(f.getAbsolutePath());
}
}
}
}
}
2. 從鍵盤輸入、輸出到顯示器
package com.zth;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* @author 時光·漫步
* 起點:鍵盤;終點:螢幕
*/
public class Test1 {
public static void main(String[] args) throws Exception{
OutputStreamWriter osw = new OutputStreamWriter(System.out);
BufferedWriter bw = new BufferedWriter(osw);
InputStreamReader in = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(in);
System.out.println("Enter lines of text and Enter 'end' to quit");
String str;
do{
str = br.readLine();
if(str.equals("end")){
break;
}
bw.write(str);
bw.newLine();
}while(true);
br.close();
bw.close();
}
}
3. 從鍵盤輸入到檔案
package com.zth;
import java.io.*;
/**
* 起點:鍵盤;終點:檔案
* @author 時光·漫步
*/
public class Test2 {
public static void main(String[] args) throws IOException {
InputStreamReader in = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(in);
Writer out = new FileWriter("e:/1.txt");
BufferedWriter bw = new BufferedWriter(out);
String str ;
System.out.println("Enter lines of text and Enter 'end' to quit");
do{
str = br.readLine();
if(str.equals("end")){
break;
}
bw.write(str);
bw.newLine();
}while(true);
System.out.println("Input over");
br.close();
bw.close();
}
}
4. 從檔案讀入,輸出到顯示器
package com.zth;
import java.io.*;
/**
* @author 時光·漫步
* 起點:檔案;終點:螢幕
*/
public class Test3 {
public static void main(String[] args) throws IOException {
Reader in = new FileReader("E:/2.txt");
BufferedReader br = new BufferedReader(in);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
BufferedWriter bw = new BufferedWriter(osw);
String str ;
do{
str = br.readLine();
if(str == null){
break;
}
bw.write(str);
bw.newLine();
}while(true);
br.close();
bw.close();
}
}
5. 從一個檔案讀入到另一個檔案(檔案複製)
package com.zth;
import java.io.*;
/**
* @author 時光·漫步
* 起點:檔案;終點:檔案
*/
public class Test4 {
public static void main(String[] args) throws IOException {
System.out.println("Start");
Reader in = new FileReader("E:/1.txt");
Writer out = new FileWriter("E:/2.txt");
char [] arr = new char[100];
int n;
while((n = in.read(arr)) > 0){
out.write(arr,0,n);
}
in.close();
out.close();
System.out.println("Over");
}
}