Java - IO - 字節流

概述

什麼是IO?

IO就是輸入流輸出流的意思。之內存爲基準,分爲輸入input輸出output ,即流向內存是輸入流,流出內存的輸出流。java

  • 輸入流:把硬盤中的數據讀取到內存中使用
  • 輸出流:把內存中的數據寫入到硬盤中保存

image-20200329113022378

上方的表格是頂級父類數組

字節流

一切皆字節

一切文件數據(文本、圖片、視頻等)在存儲時,都是以二進制數字的形式保存,都一個一個的字節,那麼傳輸時同樣如此。app

因此,字節流能夠傳輸任意文件數據。在操做流的時候,咱們要時刻明確,不管使用什麼樣的流對象,底層傳輸的始終爲二進制數據。優化

字節輸入流(InputStream)

java.io.InputStream ``抽象類是表示字節輸入流的全部類的超類,能夠讀取字節信息到內存中。編碼

它定義了字節輸入流的基本共性功能方法。操作系統

  • public abstract int read(): 從輸入流讀取數據的下一個字節。
  • public int read(byte[] b): 從輸入流中讀取一些字節數,並將它們存儲到字節數組 b中 。
  • public void close() :關閉此輸入流並釋放與此流相關聯的任何系統資源。

文件字節輸入流(FileInputStream)

java.io.FileInputStream 類是文件輸入流,繼承InputStream抽象類。publicclass FileInputStream extends InputStream3d

做用是把硬盤文件中的數據,讀取到內存中使用。code

讀取數據的原理(硬盤->內存):Java程序-->JVM-->操做系統-->調用本地方法-->讀取文件內容視頻

構造方法
Constructor Description
FileInputStream(File file) * 經過打開與實際文件的鏈接建立一個 FileInputStream ,該文件由文件系統中的 File對象 file命名。
FileInputStream(String name) 經過打開與實際文件的鏈接來建立一個 FileInputStream ,該文件由文件系統中的路徑名 name命名。
FileInputStream(FileDescriptor fdObj) 建立 FileInputStream經過使用文件描述符 fdObj ,其表示在文件系統中的現有鏈接到一個實際的文件。

當你建立一個流對象時,必須傳入一個文件路徑。該路徑下,若是沒有該文件,會拋出FileNotFoundException對象

經常使用方法
方法 描述
int read() 今後輸入流中讀取一個數據字節。
int read(byte[] b) 今後輸入流中將最多 b.length 個字節的數據讀入一個 byte 數組中。
int read(byte[] b, int off, int len) 今後輸入流中將最多 len 個字節的數據讀入一個 byte 數組中。
void close() 關閉此文件輸入流並釋放與此流有關的全部系統資源。
long skip(long n) 從輸入流中跳過並丟棄 n 個字節的數據。
protected void finalize() 確保在再也不引用文件輸入流時調用其 close 方法。
int available() 返回下一次對此輸入流調用的方法能夠不受阻塞地今後輸入流讀取(或跳過)的估計剩餘字節數。
FileChannel getChannel() 返回與此文件輸入流有關的惟一 FileChannel 對象。
FileDescriptor getFD() 返回表示到文件系統中實際文件的鏈接的 FileDescriptor 對象,該文件系統正被此 FileInputStream 使用。

*在文件字節輸入流中,read()底層調用本地方法read0()

其餘兩個read方法調用本地方法readBytes(byte b[], int off, int len)


使用步驟
  1. 建立FileInputStream對象,構造方法中綁定數據源
  2. 使用FileInputStream對象中的read方法,讀取數據
  3. 釋放資源
package IO;

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStream_in {
	public static void main(String args[]) throws IOException{
		
		//read("1.txt");
		//readMore("1.txt");
		readAll("1.txt");
	}
	public static void read(String path) throws IOException{
		FileInputStream fis = new FileInputStream(path);
		int len =0;
		while((len = fis.read())!=-1)
			System.out.print((char)len);
		
		fis.close();
		
	}
	
	public static void readMore(String path) throws IOException{
		FileInputStream fis = new FileInputStream(path);
		int len =0;
		byte[] bytes = new byte[2];
		while((len = fis.read(bytes))!=-1)
			System.out.println(new String(bytes));
		
		fis.close();
		
	}
	//優化上一個方法
	public static void readAll(String path) throws IOException{
		FileInputStream fis = new FileInputStream(path);
		int len =0;
		byte[] bytes = new byte[1024];
		while((len = fis.read(bytes))!=-1)
			System.out.print(new String(bytes,0,len));//只取有效部分
		
		fis.close();
		
	}
}

字節輸出流(OutputStream)

java.io.OutputStream 抽象類是表示字節輸出流的全部類的超類,將指定的字節信息寫出到目的地。

它定義了字節輸出流的基本共性功能方法。

  • public void close() :關閉此輸出流並釋放與此流相關聯的任何系統資源。
  • public void flush() :刷新此輸出流並強制任何緩衝的輸出字節被寫出。
  • public void write(byte[] b):將 b.length字節從指定的字節數組寫入此輸出流。
  • public void write(byte[] b, int off, int len) :從指定的字節數組寫入 len字節,從偏移量 off開始輸出到此輸出流。
  • public abstract void write(int b) :將指定的字節輸出流。

image-20200329113346675

方法 描述
void close() 關閉此輸出流並釋放與此流有關的全部系統資源。
void flush() 刷新此輸出流並強制寫出全部緩衝的輸出字節。
void write(byte[] b) 將 b.length 個字節從指定的 byte 數組寫入此輸出流。
void write(byte[] b, int off, int len) 將指定 byte 數組中從偏移量 off 開始的 len 個字節寫入此輸出流。
abstract void write(int b) 將指定的字節寫入此輸出流。

*在字節輸出流中,write的重載方法都調用write(int b) 方法

文件字節輸出流(FileOutputStream)

java.io.FileInputStream 類是文件輸入流,從文件中讀取字節。

寫入數據的原理(內存->硬盤):Java程序-->JVM-->操做系統-->調用本地方法-->把數據寫入文件

構造方法
  • FileInputStream(File file): 經過打開與實際文件的鏈接來建立一個 FileInputStream ,該文件由文件系統中的 File對象 file命名。
  • FileInputStream(String name): 經過打開與實際文件的鏈接來建立一個 FileInputStream ,該文件由文件系統中的路徑名 name命名。

當你建立一個流對象時,必須傳入一個文件路徑。該路徑下,若是沒有該文件,會拋出FileNotFoundException

Constructor Description
FileOutputStream(File file, boolean append) ** 建立文件輸出流以寫入由指定的 File對象表示的文件。
FileOutputStream(String name, boolean append) 建立文件輸出流以指定的名稱寫入文件。append是追加開關,默認false(關)。
FileOutputStream(String name) 建立文件輸出流以指定的名稱寫入文件。
FileOutputStream(File file) 建立文件輸出流以寫入由指定的 File對象表示的文件。
FileOutputStream(FileDescriptor fdObj) * 建立文件輸出流以寫入指定的文件描述符,表示與文件系統中實際文件的現有鏈接。

其實無論哪一個構造器都轉成FileOutputStream(File file, boolean append)形式

經常使用方法
方法 描述
void write(int b) **核心 將指定字節寫入此文件輸出流。
void write(byte[] b) 將 b.length 個字節從指定 byte 數組寫入此文件輸出流中。
void write(byte[] b, int off, int len) 將指定 byte 數組中從偏移量 off 開始的 len 個字節寫入此文件輸出流。
void close() 關閉此文件輸出流並釋放與此流有關的全部系統資源。
protected void finalize() 清理到文件的鏈接,並確保在再也不引用此文件輸出流時調用此流的 close 方法。
FileChannel getChannel() 返回與此文件輸出流有關的惟一FileChannel 對象。
FileDescriptor getFD() 返回與此流有關的文件描述符。

底層調用本地(native)方法write(int b, boolean append)writeBytes(byte b[], int off, int len, boolean append)

在文件字節輸出流中,write重載方法調用不一樣的本地方法。

實例
//用文件字節輸出流向文件寫入一個字節
import java.io.*;
public class fileIO {

		public static void main(String args[]) throws IOException{
			FileOutputStream fos = new FileOutputStream("1.txt");
			fos.write(97);//輸入一個字節  97的二進制1100001
			fos.close();//打開記事本默認讀字符97對應的ASCII表是a
		}
}

image-20200329120329926

package IO;
import java.io.*;
import java.util.Arrays;


public class FileOutputStream_ {

		public static void main(String args[]) throws IOException{
			fileOutOne("1.txt",97);//向1文件寫入1個字節(8位)01100001
			
			byte[] t = "你好".getBytes();
			System.out.println(Arrays.toString(t)); //gdk中英都佔兩個字節。 utf-8 英文一字節,中文三字節
			fileOutMore("2.txt",t);//向2文件寫入你好字符串轉化的字節數組。
			
			byte[] b = {97,98,49,48,48};
			fileOutMore1("3.txt",b);//向3文件寫入一個規定字節數組。
			
		}
		
		//使用字節輸出流向文件寫入一個字節
		public static void fileOutOne(String path,int num) throws IOException{
			FileOutputStream fos = new FileOutputStream(path);//指向路徑的文件
			fos.write(num);//輸入一個字節  97的二進制1100001
			fos.close();//打開記事本默認讀字符97對應的ASCII表是a
		}
		
		//使用字節輸出流向文件寫入多個字節
		public static void fileOutMore(String path,byte[] bytes) throws IOException{
			File f = new File(path);
			FileOutputStream fos = new FileOutputStream(f);//指向具體文件
			
			fos.write(bytes);//寫入一個字節數組
		
			fos.close();
		}
		
		//使用字節輸出流向文件寫入偏移的多個字節
		public static void fileOutMore1(String path,byte[] bytes) throws IOException{
			File f = new File(path);
			FileOutputStream fos = new FileOutputStream(f);//指向具體文件
			
			//byte[] b = {97,98,49,48,48}; 本應該是 ab100 偏移兩位,寫入三位得100
			fos.write(bytes,2,3);//輸入一個字節數組
		
			fos.close();
		}
}
追加 和 換行
package IO;

import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStrean_append {

	public static void main(String args[]) throws IOException{
		fileOutAppend("1.txt","append".getBytes());//向1文件追加換行和寫入一個單詞
	}
	
	public static void fileOutAppend(String path,byte[] bytes) throws IOException{
		FileOutputStream fos = new FileOutputStream(path,true);//true 標記追加
		
		fos.write("\r\n".getBytes());//window系統下文件的換行符號
		
		fos.write(bytes);//寫入一個字節數組
	
		fos.close();
	}
}

複製文件

步驟

  1. 建立一個字節輸入流對象,構造方法中綁定要讀取的數據源
  2. 建立一個字節輸出流對象,構造方法中綁定要寫入的目的地
  3. 使用字節輸入流對象的read方法讀取數據
  4. 使用直接輸出流對象的write方法將讀取到的數據寫入目的地文件
  5. 釋放資源(棧順序,先進後出)
package bytestream;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyFile {
    public static void main(String[] args) throws IOException {
        // 1. 建立一個字節輸入流對象,構造方法中綁定要讀取的數據源
        FileInputStream fis = new FileInputStream("test.txt");
        // 2. 建立一個字節輸出流對象,構造方法中綁定要寫入的目的地
        FileOutputStream fos = new FileOutputStream("test2.txt");
        // 3. 使用字節輸入流對象的read方法讀取數據
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = fis.read(bytes)) != -1) {

            // 4. 使用直接輸出流對象的write方法將讀取到的數據寫入目的地文件
            fos.write(bytes, 0, len);
        }
        // 5. 釋放資源(棧順序,先進後出)
        fos.close();
        fis.close();
    }
}

問題

中文字符的編碼

  • GBK 佔兩字節
  • UTF-8 佔三個字節
package bytestream;

import java.io.FileInputStream;
import java.io.IOException;

public class ByteStreamTest {
    public static void main(String[] args) throws IOException {
        // 當使用字節流讀取字符時,存在一些問題
        FileInputStream fis = new FileInputStream("test.txt");

        int len = 0;
        while ((len = fis.read()) != -1) {
            System.out.println((char)len);
        }
        fis.close();
    }
}

test.txt

123789456你好

運行結果

1
2
3
7
8
9
4
5
6
ä
½
 
å
¥
½

使用字節流讀取字符數據出現亂碼!

字節流存在的問題:當使用字節流讀取文本文件時,可能會有一個小問題。就是遇到中文字符時,可能不會顯示完整的字符,

那是由於一箇中文字符可能佔用多個字節存儲。因此Java提供一些字符流類,以字符爲單位讀寫數據,專門用於處理文本文件。

相關文章
相關標籤/搜索