勿以惡小而爲之,勿以善小而不爲--------------------------劉備
勸諸君,多行善事積福報,莫作惡
上一章簡單介紹了File類的使用(一),如果沒有看過,請觀看上一章
一. OutputStream
OutputStream 是字節輸出流, 用於從程序中往文件裏寫入內容。 常常使用其子類, FileOutputStream.
一.一 OutputStream 接口方法
方法名 | 作用 |
---|---|
abstract void write(int b) | 寫入單個字節,寫入內容爲 b |
void write(byte[] b) | 寫入整個字節數組, 寫入內容爲 字節數組 |
void write(byte[] b, int off, int len) | 寫入字節數組,從off 到off+len 的內容 |
void close() | 關閉此輸出流並釋放與此流相關聯的任何系統資源。 |
void flush() | 刷新此輸出流並強制任何緩衝的輸出字節被寫出 |
一定要關閉流。
一.二 FileOutputStream 類
對於文件的字節操作,常常使用 FileOutputStream 類。
一.二.一 構造方法
一.二.一.一 方法
方法 | 作用 |
---|---|
FileOutputStream(File file) | 傳入文件, 輸出時重寫文件內容 |
FileOutputStream(File file, boolean append) | 傳入文件,append爲true時,追加文件內容,爲false時,重寫文件內容。 |
FileOutputStream(String name) | 傳入文件的路徑,相對路徑和絕對路徑均可以,輸出時重寫文件內容 |
FileOutputStream(String name, boolean append) | 傳入文件的路徑,append爲true時,追加文件內容,爲false時,重寫文件內容 |
實際上,常用這兩種方式,一種是傳入文件,另外一種是傳入文件路徑。
如果傳入的文件不存在的話,那麼構造時會創建該文件。
老蝴蝶建議, 傳入文件。
一.二.一.二 演示
@Test
public void conTest() throws Exception{
//有兩種,一種是傳入文件File, 一種是傳入路徑字符串
File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
//傳入File,重寫文件
OutputStream outputStream=new FileOutputStream(file);
//傳入File, 追加文件
// OutputStream outputStream=new FileOutputStream(file,true);
String path="E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello.txt";
//傳入路徑, 重寫文件
OutputStream outputStream1=new FileOutputStream(path);
//傳入路徑,追加文件
// OutputStream outputStream1=new FileOutputStream(path,true);
}
一.二.二 寫入和關閉方法
重寫父類的方法, 主要是 write() 方法。
一.三 OutputStream 寫入文件
一.三.一 write(int b) 單個寫入
寫入int 類型,如果寫入的是字符串的話,需要將字符串轉換成字節數組,然後遍歷字節數組,一個個寫入。
@Test
public void writeContentTest() throws Exception{
File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
//這個文件不存在
OutputStream outputStream=new FileOutputStream(file);
//輸入單個內容
outputStream.write(100);
//輸入單個內容
outputStream.write((int)'a');
//輸入數組
String str="Hello,My Name is TwoButterfly\r\n";
//一次次寫入
byte[] bytes=str.getBytes("UTF-8");
//一個一個寫入
for(byte b:bytes){
//byte 類型會自動轉換成 int 類型
outputStream.write(b);
}
System.out.println("寫入內容成功");
outputStream.close();
}
運行程序,查看文件內容
會將100 轉換成字節, 轉換後爲 d. (97+3, 97爲小寫字母a)
會發現,當寫入字符串時,需要一個一個的寫,效率太低。 能不能直接寫入字符串呢?
一.三.二 write(byte[] bytes) 字節數組形式寫入
當寫入字符串時,因爲是字節輸出,所以是不能直接寫入字符串的, 但卻可以直接寫入字節數組。 就是將一個個字節先封裝起來,封裝成一個字節數組,然後以字節數組爲單位,進行寫入。 建議都採用寫字節數組的形式。
@Test
public void writeContent2Test() throws Exception{
File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
//不是追加,是重寫。
OutputStream outputStream=new FileOutputStream(file);
//輸入字符串
String str="Hello,My Name is TwoButterfly";
//將字符串轉換成字節數組
byte[] bytes=str.getBytes("UTF-8");
//寫入字節數組, 也可以 後面添加 0, len 形式的。
outputStream.write(bytes);
System.out.println("寫入文件成功");
outputStream.close();
}
運行程序,查看內容
發現,將以前的內容,先刪除,再進行寫入。 那麼,能不能保留以前的內容呢?
一.三.三 追加內容,構造方法時令append 參數爲true
在構造方法 FileOutputStream 時,令參數 append爲true 即可。
@Test
public void writeContent3Test() throws Exception{
File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
//令參數爲true,追加內容
OutputStream outputStream=new FileOutputStream(file,true);
//輸入字符串, 追加內容
String str="Thank you";
//將字符串轉換成字節數組
byte[] bytes=str.getBytes("UTF-8");
//寫入字節數組
outputStream.write(bytes);
System.out.println("寫入文件成功");
outputStream.close();
}
運行程序:
會發現,格式非常不好,都連在一起了, 能不能換行呢?
一.三.四 換行,寫入 \r\n
換行,只需要寫入換行符就可以了。 換行符是 \r\n
@Test
public void writeContent4Test() throws Exception{
File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
//令參數爲true,追加內容
OutputStream outputStream=new FileOutputStream(file,true);
//輸入字符串, 追加內容,並且換行
String str="\r\n I change Line"+"\r\n I change Line too";
//將字符串轉換成字節數組
byte[] bytes=str.getBytes("UTF-8");
//寫入字節數組
outputStream.write(bytes);
System.out.println("寫入文件成功");
outputStream.close();
}
運行程序:
現在寫入的都是英文字符,能不能寫入我們偉大的中國漢字呢?
一.三.五 寫入漢字
@Test
public void writeContent5Test() throws Exception{
File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
//這個文件不存在
OutputStream outputStream=new FileOutputStream(file,true);
//輸入漢字
String str="\r\n你好,我是兩個蝴蝶飛";
//寫入數組
byte[] bytes=str.getBytes("UTF-8");
//換一種形式
outputStream.write(bytes,0,bytes.length);
outputStream.close();
}
運行程序:
正常寫入漢字。 能夠將內容寫入到文件裏面,那麼就可以將文件內容讀取出來。
二. InputStream
InputStream 是字節輸入流, 用於讀取文件中的內容。常常使用其子類 FileInputStream.
二.一 InputStream 接口方法
方法 | 作用 |
---|---|
abstract int read() | 只讀取一個內容, 並且返回該內容 |
int read(byte[] b) | 一次讀取多個內容,並且將內容放置到 b 字節數組裏面。返回讀取的長度 |
int read(byte[] b, int off, int len) | 一次讀取多個內容,讀的內容是從 off 到 off+len, 然後將內容放置到b 字節數組裏面 |
void close() | 關閉此輸入流並釋放與流相關聯的任何系統資源。 |
二.二 FileInputStream
二.二.一 構造方法
二.二.一.一 方法
方法 | 作用 |
---|---|
FileInputStream(File file) | 放置進去文件 |
FileInputStream(String name) | 放置進去文件的路徑 |
可以傳入文件,也可以傳入文件的路徑, 注意,文件必須要存在,否則會拋出 FileNotFoundException 異常。
老蝴蝶建議,傳入文件的形式。
二.二.一.二 演示
@Test
public void conTest() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello.txt");
//傳入文件
InputStream inputStream=new FileInputStream(file);
String path="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello.txt";
//傳入文件的路徑
InputStream inputStream1=new FileInputStream(path);
}
二.三 InputStream 讀取文件
二.三.一 read() 一個一個讀取
@Test
public void read1Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
InputStream inputStream=new FileInputStream(file);
//讀一個,需要轉換成 char 字符進行展示
System.out.println("第一個:"+(char)inputStream.read());
System.out.println("第二個:"+(char)inputStream.read());
System.out.println("第三個:"+(char)inputStream.read());
int r=-1;
byte[] b=new byte[(int)file.length()];
int i=0;
//需要一個個放置到字節數組裏面
while((r=inputStream.read())!=-1){
b[i]=(byte)r;
i++;
}
System.out.println("讀出文件內容:"+new String(b,0,i));
inputStream.close();
}
運行程序,控制檯打印輸出:
發現,中文目前可以正常的打印出來。
注意: read() 讀取之後,無法再重新返回去讀。
讀內容是一個一個的讀,那麼能不能讀完之後,封裝到一個數組裏面呢, 這樣便於獲取?
二.三.二 read(byte[] b ) 讀取內容後封裝到數組裏面
@Test
public void read2Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
InputStream inputStream=new FileInputStream(file);
//根據文件大小,設置字節數組的大小
byte[] bytes=new byte[(int)file.length()];
//讀取之後,封裝到數組裏面
inputStream.read(bytes);
System.out.println("讀出內容:"+new String(bytes));
inputStream.close();
}
控制檯打印輸出:
中文也可以正常的顯示出來,換行也能顯示。
但發現有一個問題,創建字節數組時,用的是 new byte[(int)file.length()]; 也就是先統計一下文件的大小,然後根據文件大小,去構建數組,需要先查詢一下。 這樣效率會低一些。
二.三.三 read(byte[] b) 數組長度設置爲 1024
@Test
public void read3Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
InputStream inputStream=new FileInputStream(file);
//設置長度 爲1024
byte[] bytes=new byte[1024];
inputStream.read(bytes);
System.out.println("讀出內容:"+new String(bytes));
inputStream.close();
}
運行程序:
會發現,後面有很多很多的空格, 浪費了 bytes 數組的空間。 可以利用 read(bytes) 方法的返回值進行控制。
二.三.四 read(byte[] b) 返回值
@Test
public void read4Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
InputStream inputStream=new FileInputStream(file);
//設置長度 爲1024
byte[] bytes=new byte[1024];
//返回的內容,就是讀取的長度
int len=inputStream.read(bytes);
System.out.println("讀出內容:"+new String(bytes,0,len));
inputStream.close();
}
運行程序,控制檯打印輸出:
你以爲這樣就成功了嗎? 不,還沒有。 現在我們固定長度爲 1024, 也就是可以讀取 1KB的內容, 但是如果長度超過了 1KB, bytes字節數組是放不下的,會造成文件的內容讀取不全的, 那該怎麼辦呢?
老蝴蝶可以演示一下,這種效果。 我們可以設置長度爲 10, 這個長度肯定小於文件的大小
@Test
public void read5Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
InputStream inputStream=new FileInputStream(file);
//設置長度 爲10
byte[] bytes=new byte[10];
//返回的內容,就是讀取的長度
int len=inputStream.read(bytes);
System.out.println("讀出內容:"+new String(bytes,0,len));
inputStream.close();
}
這個時候,運行程序:
會發現,文件內容沒有讀全,只讀取了前10個字節。
要想全部讀完,我們可以設置循環讀取。
二.三.五 read(byte[]b ,int offset, int len) 循環讀取內容
@Test
public void read6Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
InputStream inputStream=new FileInputStream(file);
StringBuilder sb=new StringBuilder();
byte[] bytes=new byte[10];
int len=-1;
while((len=inputStream.read(bytes))!=-1){
//依次讀取的內容
String temp=new String(bytes,0,len);
sb.append(temp);
}
System.out.println("輸出讀取的內容:"+sb.toString());
inputStream.close();
}
控制檯打印輸出:
可以讀取全部的內容信息,但是發現,產生了中文亂碼的問題。
這是由於在讀取中文時,準確地說在讀取到 漢字’個’時, 正好將個這個詞的字節給分開了, 一半在 bytes[9], 另一半在下一次bytes數組的 的bytes[0]位置。
如果將字節數組bytes的長度擴大,如擴大到 1024, 就不會產生亂碼問題了。
二.四 字節讀取時,中文亂碼問題
在fileSrc目錄下,新創建一個 rz.txt 文件, 填充內容爲:
你好我是兩個蝴蝶飛你好我是兩個蝴蝶飛
我們讀取這個文件,來演示中文亂碼問題。
二.四.一 字節數組長度爲奇數時
字節數組爲奇數時,很容易造成中文亂碼。
@Test
public void read7Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"rz.txt");
InputStream inputStream=new FileInputStream(file);
StringBuilder sb=new StringBuilder();
byte[] bytes=new byte[5];
int len=-1;
while((len=inputStream.read(bytes))!=-1){
//依次讀取的內容
String temp=new String(bytes,0,len);
sb.append(temp);
}
System.out.println("輸出讀取的內容:"+sb.toString());
inputStream.close();
}
運行程序:
有很大概率,會將中文進行拆分。
這個例子時,將 byte[5] 轉換成 byte[6],換成偶數時:
這個時候,就可能不亂碼了。
二.四.二 字節數組長度不被3整除時
上面的字節長度爲6時,不亂碼,爲10時就亂碼了。
爲12時,就不亂碼了,爲 14時亂碼了,爲18時不亂碼了。
老蝴蝶建議,接收的字節數組的長度最好是能被3整除的偶數。 讀取時,爲了方便,常常是1024的整數被。
所以,綜合起來就是: 能同時被1024和3整除的偶數。
爲了方便,老蝴蝶還是以 1024爲主。
二.五 讀取文件內容綜合
@Test
public void read8Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");
InputStream inputStream=new FileInputStream(file);
//長度設置成1024
byte[] bytes=new byte[1024];
int len=-1;
while((len=inputStream.read(bytes))!=-1){
//依次讀取的內容
System.out.println("讀取的內容爲:"+new String(bytes,0,len));
}
inputStream.close();
}
三. OutputStream和InputStream的應用: 文件複製
文件複製,實際上就是將 InputStream和 OutputStream 結合起來使用, 兩者共同使用同一個 字節數組, InputStream將讀出的內容批量寫入到字節數組裏面, OutputStream將字節數組批量寫入到文件裏面。
三.一 文件複製方法
/**
* 複製字節文件
* @param src 源文件
* @param desc 目標文件
* @return
*/
public static boolean copyBin(String src,String desc) throws Exception{
//定義兩個文件
File srcFile=new File(src);
File descFile=new File(desc);
if(!srcFile.exists()||!srcFile.isFile()){
throw new RuntimeException("源文件不存在或者源文件是目錄");
}
//定義InputStream 和 OutputStream
InputStream inputStream=new FileInputStream(srcFile);
OutputStream outputStream=new FileOutputStream(descFile);
//1M 1M的讀取
byte[] bytes=new byte[1024];
int len=-1;
//將內容寫入到 bytes字節數組裏面
while((len=inputStream.read(bytes))!=-1){
//將bytes字節數組寫入到文件裏面
outputStream.write(bytes,0,len);
}
outputStream.flush();
//關閉流
outputStream.close();
inputStream.close();
return true;
}
三.二 測試文件複製
複製文件和複製圖片類型都可以。
@Test
public void copyTest(){
//複製普通文件
/* String src="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"rz.txt";
String desc="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"rzcopy.txt";*/
//複製圖片
String src="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"129.png";
String desc="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"129copy.png";
try {
CopyUtils.copyBin(src,desc);
System.out.println("文件複製成功");
} catch (Exception e) {
e.printStackTrace();
}
}
運行程序,查看文件系統
謝謝您的觀看,如果喜歡,請關注我,再次感謝 !!!