Java學習筆記——IO流與文件

Java學習筆記——IO流與文件

標籤: IOStreamFile



一、文件(File類

1. File對象創建方法

  • Java文件類以抽象的方式代表文件名和目錄路徑名。該類主要用於文件和目錄的創建、文件的查找和文件的刪除等。
  • File類提供管理文件或目錄的方法。File實例表示真實文件系統中的一個文件或者目錄,可以通過以下四種構造方法創建一個File對象。

構造器

(1). File(File parent, String child):參數parent表示根路徑,參數child表示子路徑。例如:

    File file = new File(new File("src"), "test.txt");

(2). File(String pathname):參數pathname表示文件路徑或者目錄路徑。例如:

    File file = new File("src/test.txt"); //等價於(1)中例子

(3). File(String parent, String child):參數parent表示根路徑,參數child表示子路徑。例如:

    File file = new File("src", "test.txt"); //等價於(2)中例子

(4). File(URI uri):參數uri表示路徑。例如:

    File file = new File(new URI("src/test.txt")); //等價於(3)中例子

只處理一個文件,使用第二個或者第四個構造方法更方便;如處理一個公共目錄的若干子目錄或文件,那麼使用第一個或者第三個更方便。

2. File類常用方法

假設現在在/com/hzj/目錄下有個test.txt文件,內容如下:

    qwertyuiopasdfghjklzxcvbnm

(1). boolean exists():判斷該文件或者目錄是否存在。

(2). long length():返回該文件的長度。

(3). String getName():返回該文件名字。

(4). boolean createNewFile():創建指定文件。

(5). boolean delete():刪除指定文件或者空目錄。

(6). boolean mkdir():創建一個目錄(不連續創建),即如果該目錄的父目錄不存在話,那麼就會創建失敗。

(7). boolean mkdirs():創建一個目錄(連續創建),如果該目錄的父目錄不存在話,那麼還會創建所有的父目錄。

(8). String[] list():返回指定目錄下所有文件名或者子目錄名所組成的字符串數組。

(9). long lastModified():返回指定文件最後一次被修改的時間(從1970年1月1日凌晨12點到這個文件的修改時間
之間所經歷的毫秒數)。

(10). String getPath():返回指定文件或者目錄的路徑。

(11). String getAbsolutePath():返回指定文件或者目錄的絕對路徑。

(12). boolean renameTo(File dest):修改該目錄或文件名字爲指定的值。

    File parent = new File("/com/hzj");
    File child = new File(parent, "test.txt");
    File file = new File("/com/hzj/test2.txt");
    File mkdir_test = new File("/com/hzj/aaa/bbb");

    System.out.println(child.exists()); // true
    System.out.println(child.length()); // 26
    System.out.println(child.getName()); // test.txt
    System.out.println(file.createNewFile()); // true(創建file文件)
    System.out.println(file.delete()); // true(刪除file文件)
    System.out.println(mkdir_test.mkdir()); // false(沒有aaa目錄)
    System.out.println(mkdir_test.mkdirs()); // true(連續創建)

    System.out.println(parent.list()); // test.txt   aaa

    System.out.println(parent.renameTo(new File("/com/jzh"))); // true

二、IO流(Stream

  • 在Java中,把一組有序的數據序列稱爲流。程序的主要任務就是操縱數據。
  • 根據流中最小的數據單元,可以把流分爲字節流和字符流。
  • 根據操作的方向,可以把流分爲輸入流和輸出流兩種。程序從輸入流讀取數據,向輸出流寫出數據。示例圖如下:
輸入流
輸出流
文件,內存,鍵盤
Java程序
文件,內存,控制檯

一定要搞清楚什麼時候用輸入流,什麼時候用輸出流。通俗的講,就是把Java程序看爲主體,如果要讀文件,就是文件系統 --> Java程序,看箭頭方法可知,從文件系統出,進入Java程序,即相對於Java程序就是input,如果要寫文件,就是Java程序 --> 文件系統,看箭頭方法可知,從Java程序出,進入文件系統,即相對於Java程序就是output

下面是各個流之間的層次關係(放大了看):

IO流
字符流
字節流
Reader
Writer
InputStream
OutputStream
BufferedReader
InputStreamReader
StringReader
PipedReader
CharArrayReader
FilterReader
BufferedWriter
OutputStreamWriter
StringWriter
PrinterWriter
PipedWriter
CharArrayWriter
FilterWriter
FileInputStream
FilterInputStream
ObjectInputStream
PipedInputStream
SequenceInputStream
StringBufferInputStream
ByteArrayInputStream
FileOutputStream
FilterOutputStream
ObjectOutputStream
PipedOutputStream
ByteArrayOutputStream
FileReader
PushbackReader
FileWriter
BufferedInputStream
DataInputStream
PushbackInputStream
BufferedOutputStream
DataOutputStream
PrintStream

1. 字節流InputStream && OutputStream

InputStreamOutputStream類處理的是字節流,也就是說,數據流中的最小單元爲一個字節,它包括8個二進制位。

(1)輸入流(InputStream

主要方法:

(1). int available():返回可以從輸入流中讀取的字節數目。

(2). abstract int read():從輸入流讀取一個8位的字節,把它轉換爲0-255之間的整數,並返回這一整數。如果遇到輸入流的結尾,則返回-1

(3). int read(byte[] b):從輸入流讀取若干個字節,把它們保存到參數b指定的字節數組中。返回的整數表示讀取的字節數。如果遇到輸入流的結尾,則返回-1。使用byte[]數組可以降低物理讀取次數。byte[]長度最大可以取到整個要讀取的數據的長度。

(4). int read(byte[] b, int off, int len):從輸入流讀取若干個字節,把它們保存到參數b指定的字節數組中。返回的整數表示讀取的字節數。參數off指定在字節數組中開始保存數據的起始下標(默認爲0),參數len指定讀取的字節數目。返回的整數表示實現讀取的字節數。如果遇到輸入流的結尾,則返回-1

(5). void close():關閉輸入流,InputStream類本身的close()方法不執行任何操作。它的一些子類覆蓋了close()方法,在close()方法中釋放和流有關的系統資源。

(6). long skip(long n):從輸入流中跳過參數n指定數目的字節。

(7). void mark(int readlimit):標記此輸入流中的當前位置。

(8). boolean markSupported():返回此輸入流是否支持標記(mark())和重置(reset())方法。

常用子類:

InputStream是個抽象類,常用的子類有:ByteArrayInputStreamDataInputStreamBufferedInputStreamPipedInputStreamFileInputStreamObjectInputStream

(2)輸出流(OutputStream

主要方法:

(1). void close():關閉輸出流。OutputStream類本身的close()方法不執行任何操作。它的一些子類覆蓋了close()方法,在close()方法中釋放和流有關的系統資源。

(2). void flush()OutputStream類本身的flush()方法不執行任何操作,它的一些帶有緩衝區的子類(比如BufferedOutputStreamPrintStream類)覆蓋了flush()方法。通過帶緩衝區的輸出流寫數據時,數據先保存在緩衝區中,積累到一定程度纔會真正寫到輸出流中。緩衝區通常用字節數組實現,實際上是指一塊內存空間。flush:即使內容沒有達到緩衝的標準大小,仍然輸出到輸出流中。只要使用到buffer的流,就一定要進行flush,否則最後很可能有數據沒有完全達到要求而不能輸出。

(3). void write(byte[] b):把參數b指定的字節數組中的所有字節寫到輸出流。

(4). void write(byte[] b, int off, int len):把參數b指定的字節數組中的所有字節寫到輸出流,參數off指定字節數組的起始下標,從這個位置開始輸出由參數len指定數目的字節。

(5). abstract void write(int b):向輸出流寫入一個字節。在向文件或控制檯寫數據時,採用上面兩個write方法可以減少進行物理讀文件或鍵盤的次數,因此能提高I/O操作的效率。

常用子類:

OutputStream是個抽象類,常用的子類有:ByteArrayOutputStreamDataOutputStreamBufferedOutputStreamPipedOutputStreamFileOutputStreamObjectOutputStream

(3)各輸入流、輸出流用法詳解

(1). ByteArrayInputStream && ByteArrayOutputStream

ByteArrayInputStream:把字節數組轉換爲輸入流。ByteArrayInputStream類有兩個默認的構造方法:

  • ByteArrayInputStream(byte[] b): 使用一個字節數組當中所有的數據做爲數據源,程序可以像輸入流方式一樣讀取字節,可以看做一個虛擬的文件,用文件的方式去讀取它裏面的數據。
  • ByteArrayInputStream(byte[] b,int offset,int length): 從數組當中的第offset(offset0開始)開始,一直取出length個這個字節做爲數據源。

ByteArrayOutputStream:字節數組輸出流在內存中創建一個字節數組緩衝區,所有發送到輸出流的數據保存在該字節數組緩衝區中。

主要方法:

  • byte[] toByteArray():創建一個新分配的字節數組。數組的大小和當前輸出流的大小,內容是當前輸出流的拷貝。即把輸出流轉換爲字節數組。
  • void writeTo(OutputStream out):將此字節數組輸出流的全部內容寫入到指定的輸出流參數中。

示例(將字節數組轉換爲輸入流,將輸出流轉換成字節數組):

    System.out.println("-----使用ByteArrayInputStream(byte[] b)構建對象------");
    byte[] b1 = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
    InputStream bis1 = new ByteArrayInputStream(b1);
    System.out.println(bis1.available()); // 8
    int n1;
    bis1.skip(2); // 跳過兩個字符
    while ((n1 = bis1.read()) != -1) {
        System.out.print((char) n1 + " "); // c d e f g h
    }
    bis1.close();

    System.out.println();
    System.out.println("----使用ByteArrayInputStream(byte[] b,int offset,int length)構建對象-----");

    byte[] b2 = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
    ByteArrayInputStream bis2 = new ByteArrayInputStream(b2, 2, 3);
    System.out.println(bis2.available()); // 3
    int n2;
    while ((n2 = bis2.read()) != -1) {
        System.out.print((char) n2 + " "); // c d e
    }
    bis2.close();

    System.out.println();
    System.out.println("----利用read(byte[] b)讀取數據-----");

    byte[] b3 = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
    byte[] b4 = new byte[3];
    ByteArrayInputStream bis3 = new ByteArrayInputStream(b3);
    System.out.println(bis3.available()); // 8
    int n3;
    while ((n3 = bis3.read(b4)) != -1) { // 利用read(byte[] b)方法時,返回值是當前讀的字符個數
        System.out.print(n3 + " "); // 3 3 2
    }
    bis3.close();

    System.out.println();
    System.out.println("----ByteArrayOutputStream用法-----");
    byte[] b5 = new byte[10];
    ByteArrayOutputStream bos5 = new ByteArrayOutputStream();
    bos5.write('a');
    bos5.write('b');
    bos5.write('c');
    bos5.write('d');
    bos5.write('e');
    bos5.write('f');
    bos5.flush();
    b5 = bos5.toByteArray(); // 將輸出流轉換成字節數組
    for (byte b : b5) {
        System.out.print((char) b); // abcdef
    }
    
    ByteArrayOutputStream bos6 = new ByteArrayOutputStream();
    bos5.writeTo(bos6); // 將bos5字節數組輸出流的全部內容寫入到指定的輸出流參數(bos6)中。
    b5 = bos6.toByteArray(); // 將輸出流轉換成字節數組
    for (byte b : b5) {
        System.out.print((char) b); // abcdef
    }
(2). FileInputStream && FileOutputStream

FileInputStream:從文件中讀取數據。FileInputStream類常用的兩個構造方法:

  • FileInputStream(File file):使用一個文件對象來創建一個輸入流對象來讀取文件。首先得使用 File()方法來創建一個文件對象。
  • FileInputStream(String name):使用字符串類型的文件名來創建一個輸入流對象來讀取文件。

FileOutputStream:向文件中寫入數據。如果該流在打開文件進行輸出前,目標文件不存在,那麼該流會創建該文件。FileOutputStream類的常用構造方法:

  • FileOutputStream(File file):使用一個文件對象來創建一個輸出流來寫文件。首先得使用 File()方法來創建一個文件對象。
  • FileOutputStream(String name):使用字符串類型的文件名來創建一個輸出流對象來寫文件。
  • FileOutputStream(File file, boolean append):使用一個文件對象來創建一個輸出流來寫文件,同時給定一個append參數(默認false),用來指定是追加還是覆蓋。
  • FileOutputStream(String name, boolean append):使用字符串類型的文件名來創建一個輸出流對象來寫文件,同時給定一個append參數(默認false),用來指定是追加還是覆蓋。

示例(向文件中寫入文件,並讀取出來):

    File file = new File("src/note/file_test.txt");
    OutputStream out = new FileOutputStream(file); // 寫文件
    out.write('h');
    out.write('e');
    out.write('l');
    out.write('l');
    out.write('o');

    InputStream in = new FileInputStream(file); // 讀文件
    System.out.println("可讀取" + in.available() + "字節數"); // 可讀取5字節數
    System.out.println(in.markSupported()); // false (不支持標記)
    int n;
    while ((n = in.read()) != -1) {
        System.out.print((char) n); // hello
    }

    in.close(); // 關閉資源
    out.close(); // 關閉資源
(3). DataInputStream && DataOutputStream

DataInputStream:用於讀取基本類型數據,如int, float, long, doubleboolean等。構造方法爲:

  • DataInputStream(InputStream in):需要傳入一個InputStream對象,通常傳入FileInputStream對象。

常用方法:

  • int skipBytes(int n):從輸入流中跳過n個字節的數據。
  • boolean readBoolean():從輸入流中讀取1個字節,將它轉換爲boolean類型的數據。
  • byte readByte():從輸入流中讀取1個字節,將它轉換爲byte類型的數據。
  • char readChar():從輸入流中讀取2個字節,將它轉換爲char類型的數據。
  • double readDouble():從輸入流中讀取8個字節,將它轉換爲double類型的數據。
  • float readFloat():從輸入流中讀取4個字節,將它轉換爲float類型的數據。
  • int readInt():從輸入流中讀取4個字節,將它轉換爲int類型的數據。
  • long readLong():從輸入流中讀取8個字節,將它轉換爲long類型的數據。
  • short readShort():從輸入流中讀取2個字節,將它轉換爲short類型的數據。
  • String readUTF():從輸入流中讀取13個字節的串,將它轉換爲UTF-8字符編碼的字符串。

DataOutputStream:用於寫入基本數據類型。如int, float, long, doubleboolean等。構造方法爲:

  • DataOutputStream(OutputStream out):需要傳入一個OutputStream對象,通常傳入FileOutputStream對象。

常用方法如下(這些方法都是將指定的基本數據類型以字節的方式寫入到輸出流):

  • void writeBoolean(boolean v)
  • void writeByte(int v)
  • void writeChar(int v)
  • void writeDouble(double v)
  • void writeFloat(float v)
  • void writeInt(int v)
  • void writeLong(long v)
  • void writeShort(int v)
  • void writeUTF(String str):寫入的時候,首先會寫入兩個字節,代表字符串的長度,然後再以字節的方式把字符串寫入。可查看writeUTF()源代碼理解。

示例(向文件中寫入指定類型的數據,並按照指定數據類型讀取出來):

    // -----------寫入DataOutputStream-----------
    OutputStream fos = new FileOutputStream("src/note/data_test.txt");
    DataOutputStream dos = new DataOutputStream(fos);
    dos.writeInt(1);
    dos.writeLong(100);
    dos.writeUTF("hello world!");
    dos.writeDouble(1.23);
    dos.writeBoolean(false);
    dos.writeByte('a');
    dos.writeChar('b');
    dos.writeFloat(1.1f);

    // -----------從DataOutputStream讀出-----------
    InputStream fis = new FileInputStream("src/note/data_test.txt");
    DataInputStream dis = new DataInputStream(fis);
    System.out.println(dis.readInt());
    System.out.println(dis.readLong());
    System.out.println(dis.readUTF());
    System.out.println(dis.readDouble());
    System.out.println(dis.readBoolean());
    System.out.println(dis.readByte());
    System.out.println(dis.readChar());
    System.out.println(dis.readFloat());
    /* 結果:
            1
            100
            hello world!
            1.23
            false
            97
            b
            1.1
        */

如果需要跳過某些類型數據,只需利用skipBytes(n)方法跳過該類型所佔字節數即可。其中String類型比較特殊,需要使用skipBytes(out.readShort()),因爲writeUTF()會先寫入一個short類型的數據,代表String的長度。示例:

    // -----------寫入DataOutputStream-----------
    OutputStream fos = new FileOutputStream("src/note/data_test.txt");
    DataOutputStream dos = new DataOutputStream(fos);
    dos.writeInt(1);
    dos.writeLong(100);
    dos.writeUTF("hello world!");
    dos.writeDouble(1.23);

    // -----------從DataOutputStream讀出-----------
    InputStream fis = new FileInputStream("src/note/data_test.txt");
    DataInputStream dis = new DataInputStream(fis);
//		System.out.println(dis.readInt());
    dis.skipBytes(4); // 跳過int類型,int佔4個字節
    System.out.println(dis.readLong());
//		System.out.println(dis.readUTF());
    dis.skipBytes(dis.readShort()); // 跳過String類型的數據
    System.out.println(dis.readDouble());
    /* 結果:
            100
            1.23
        */
(4). ObjectInputStream && ObjectOutputStream

ObjectInputStream:對象的反序列化(對象需要實現Serializable序列化接口)。構造方法:

ObjectInputStream(InputStream in):需要傳入一個InputStream對象,可以傳入FileInputStream對象。

常用方法:

  • Object readObject():讀取一個對象。

ObjectOutputStream:對象的序列化(對象需要實現Serializable序列化接口)。構造方法:

ObjectOutputStream(OutputStream out):需要傳入一個OutputStream對象,可以傳入FileInputStream對象。

常用方法:

  • void writeObject(Object obj):寫入一個對象。

示例(對象的序列化與反序列化,利用文件流來實現數據持久化):

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

public class Test {
	public static void main(String[] args) throws IOException {
        // 藉助FileOutputStream來實現對象的持久化存儲,即存入文件

		// -----------寫入ObjectOutputStream-----------
		OutputStream fos = new FileOutputStream("src/note/data_test.txt");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(new Student("張三"));
		oos.close();
		fos.close();

		// -----------從DataOutputStream讀出-----------
		InputStream fis = new FileInputStream("src/note/data_test.txt");
		ObjectInputStream ois = new ObjectInputStream(fis);
		try {
			Student s = (Student) ois.readObject();
			System.out.println(s); // Student [name=張三]
			fis.close();
			ois.close();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

class Student implements Serializable { // Stduent類,實現Serializable接口
	private String name;

	public Student() {
	}

	public Student(String name) {
		super();
		this.name = name;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + "]";
	}

}
(5). PipedInputStream && PipedOutputStream

管道流,用於線程間的通信。一個線程的PipedInputStream對象從另外一個線程的PipedOutputStream對象讀取輸入。要使管道流有用,必須同時構造管道輸入流和管道輸出流。

PipedInputStream:管道輸入流從一個管道輸出流中讀取數據。

PipedOutputStream:管道輸出流給管道輸入流傳輸數據。

示例(利用多線程實現管道傳輸Student對象):

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.Serializable;

/*
 * 開啓兩個線程,一個用於管道輸出流用於寫入Student對象,另一個用於管道輸入流讀取Student對象
 */
public class Test {
	public static void main(String[] args) throws IOException {
		PipedOutputStream pos = new PipedOutputStream();
		PipedInputStream pis = new PipedInputStream(pos);
		PipedOutputStreamThread thread1 = new PipedOutputStreamThread(pos);
		PipedInputStreamThread thread2 = new PipedInputStreamThread(pis);
		thread1.start();
		thread2.start();
	}
}

class PipedOutputStreamThread extends Thread { // 管道輸出流線程
	PipedOutputStream pos;

	public PipedOutputStreamThread(PipedOutputStream pos) {
		this.pos = pos;
	}

	@Override
	public void run() {
		try {
            // 用ObjectOutputStream包裝管道輸出流,使之有序列化對象功能
			ObjectOutputStream oos = new ObjectOutputStream(pos); 
			oos.writeObject(new Student("李四"));
			oos.close();
			pos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

class PipedInputStreamThread extends Thread { // 管道輸入線程
	PipedInputStream pis;

	public PipedInputStreamThread(PipedInputStream pis) {
		this.pis = pis;
	}

	@Override
	public void run() {
		try {
            // 用ObjectInputStream包裝管道輸入流,使之有反序列化對象功能
			ObjectInputStream ois = new ObjectInputStream(pis);
			Student s = (Student) ois.readObject();
			System.out.println(s); // Student [name=李四]
			pis.close();
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

class Student implements Serializable {
	private String name;

	public Student() {
	}

	public Student(String name) {
		super();
		this.name = name;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + "]";
	}

}
(6). BufferedInputStream && BufferedOutputStream

BufferedInputStream:字節緩衝輸入流。BufferedInputStream類覆蓋了被過濾的輸入流的讀數據行爲,利用緩衝區來提高讀數據的效率。BufferedInputStream類先把一批數據讀入到緩衝區,接下來read()方法只需要從緩衝區內獲取數據,就能減少物理性讀取數據的次數。構造方法:

  • BufferedInputStream(InputStream in):參數in指定需要被過濾的輸入流。
  • BufferedInputStream(InputStream in, int size):參數in指定需要被過濾的輸入流。參數size指定緩衝區的大小,以字節爲單位。

BufferedOutputStream:字節緩衝輸出流。BufferedOutputStream類覆蓋了被過濾的輸出流的寫數據行爲,利用緩衝區來提高寫數據的效率。BufferedOutputStream類先把一批數據寫入到緩衝區,接下來只需要flush()將所有緩衝的輸出字節寫出到底層輸出流中,就能減少物理性存數據的次數。構造方法:

  • public BufferedOutputStream(OutputStream out):採用的默認的緩衝區大小 ,來構造一個字節緩衝輸出流對象。
  • public BufferedOutputStream(OutputStream out,int size):指定size緩衝區大小構造緩衝輸出流對象。

主要方法:

(1). void flush():刷新此緩衝的輸出流,讓所有緩衝的輸出字節被寫出到底層輸出流中。
(2). void close():關閉此輸出流並釋放與此流有關的所有系統資源。FilterOutputStreamclose方法先調用其flush方法,然後調用其基礎輸出流的close方法。

示例:

    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.txt"));
    bos.write("qwertyuiopasdfghjklzxcvbnm".getBytes());
    bos.flush();
    bos.close();

    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.txt"));
    int n;
    while ((n = bis.read()) != -1) {
        System.out.print((char) n); // qwertyuiopasdfghjklzxcvbnm
    }
    bis.close();

2. 字符流Reader && Writer

在許多應用場合,Java應用程序需要讀寫文本文件。在文本文件中存放了採用特定字符編碼的字符,爲了便於讀於各種字符編碼的字符,java.io包中提供了Reader/Writer類,它們分別表示字符輸入流和字符輸出流。

  • 在不同的操作系統有着不同的字符編碼,windows默認是gbkgbk2312unixutf-8maceuc_cn
  • 在處理字符流時,最主要的問題是進行字符編碼的轉換。Java語言採用Unicode字符編碼。對於每一個字符,Java虛擬機會爲其分配兩個字節的內存。而在文本文件中,字符有可能採用其他類型的編碼,比如GBKUTF-8字符編碼等。

在默認情況下,ReaderWriter會在本地平臺的字符編碼和Unicode字符編碼之間進行編碼轉換。

編碼轉換1

如果要輸入或輸出採用特定類型編碼的字符串,可以使用InputStreamReader類和OutputStreamWriter類。在它們的構造方法中可以指定輸入流或輸出流的字符編碼。

編碼轉換2

(1)輸入流(Reader

  • Reader類能夠將輸入流中採用其他編碼類型的字符轉換爲Unicode字符,然後在內存中爲這些Unicode字符分配內存。

主要方法:

  • abstract void close():關閉流並釋放與其關聯的所有系統資源。
  • void mark(int readAheadLimit):標記流中的當前位置。
  • boolean markSupported():判斷此流是否支持mark()操作。
  • int read():讀一個字符。
  • int read(char[] cbuf):將字符讀入數組。
  • abstract int read(char[] cbuf, int off, int len):將字符讀入數組的一部分。
  • int read(CharBuffer target):嘗試將字符讀入指定的字符緩衝區。
  • boolean ready():判斷此流是否可以讀取。
  • void reset():重置流。
  • long skip(long n):跳過指定長度字符。

常用子類:

Reader是個抽象類,常見子類有:

  • CharArrayReader:把字符數組轉換爲Reader,從字符數組中讀取字符。
  • BufferedReader:過濾器,爲其他Reader提供讀緩衝區,此外,它的readLine()方法能夠讀入一行字符串。
  • StringReader:把字符串轉換爲Reader,從字符串中讀取字符。
  • PipedReader:連接一個PipedWriter
  • PushBackReader:能把讀到的字符壓回到緩衝區中,通常用做編譯器的掃描器,在程序中一般很少使用它。
  • InputStreamReader:過濾器,把InputStream轉換爲Reader,可以指定字符編碼。
  • FileReader:從文件中讀取字符。

(2)輸出流(Writer

  • Writer類能夠把內存中的Unicode字符轉換爲其他編碼類型的字符,再寫到輸出流中。

主要方法:

  • Writer append(char c):將指定的字符追加到此writer
  • Writer append(CharSequence csq):將指定的字符序列追加到此writer
  • Writer append(CharSequence csq, int start, int end):將指定字符序列的子序列追加到此writer
  • abstract void close():關閉流,關閉前先flush()
  • abstract void flush():刷新流。
  • void write(char[] cbuf):向Writer中寫一個字符數組。
  • abstract void write(char[] cbuf, int off, int len):向Writer中寫一個字符數組的一部分。
  • void write(int c):向Writer中寫一個字符。
  • void write(String str):向Writer中寫一個字符串。
  • void write(String str, int off, int len):向Writer中寫一個字符串的一部分。

常用子類:

Writer是個抽象類,常用的子類有:

  • BufferedWriter:帶有緩衝的Writer
  • OutputStreamWriter:過濾器,把Writer轉換爲OutputStream,可以指定字符編碼。
  • PrinterWriter:打印流。
  • StringWriter:將Writer轉化爲字符串。
  • PipedWriter:連接一個PipedReader
  • CharArrayWriter:將Writer寫入字符數組。
  • FileWriter:將字符寫入文件。

(3)各輸入流、輸出流用法詳解

(1). InputStreamReader && OutputStreamWriter

InputStreamReader:字符輸入流。InputStreamReader類把InputStream類型轉換爲Reader類型,即把字節輸入流轉換成字符輸入流。構造方法:

  • InputStreamReader(InputStream in):按照本地平臺的字符編碼讀取輸入流中的字符。
  • InputStreamReader(InputStream in, String charsetName):按照指定的字符編碼讀取輸入流中的字符。

示例(兩種方式創建InputStreamReader對象,將FileInputStream字節流轉換爲InputStreamReader字符流):

    //--------------用InputStreamReader(InputStream in)構建對象--------------
    // 創建FileInputStream對象(字節輸入流)
    InputStream fis = new FileInputStream("src/note/test.txt");
    // 創建InputStreamReader對象,把FileInputStream字節流轉換爲字符流
    Reader isr = new InputStreamReader(fis);
    int n;
    while ((n = isr.read()) != -1) {
        System.out.print((char) n); // hello,我是張三。
    }
    isr.close();
    fis.close();
    
    
    //---------------InputStreamReader(InputStream in, String charsetName)構件對象-------------
    // 創建FileInputStream對象(字節輸入流)
    InputStream fis1 = new FileInputStream("src/note/test.txt");
    // 創建InputStreamReader對象,把FileInputStream字節流轉換爲字符流,並且制定字符編碼爲utf-8
    Reader isr1 = new InputStreamReader(fis1, "utf-8");
    int n1;
    while ((n1 = isr1.read()) != -1) {
        System.out.print((char) n1); // hello,我是張三。
    }
    isr1.close();
    fis1.close();

OutputStreamWriter:字符輸出流。OutputStreamWriter類把OutputStream類型轉換爲Writer類型,即把字節輸出流轉換成字符輸出流。構造方法:

  • OutputStreamWriter(OutputStream out):創建使用默認字符編碼的OutputStreamWriter
  • OutputStreamWriter(OutputStream out, Charset cs):創建一個使用給定字符集的OutputStreamWriter
  • OutputStreamWriter(OutputStream out, CharsetEncoder enc):創建使用給定charset編碼器的OutputStreamWriter
  • OutputStreamWriter(OutputStream out, String charsetName):創建一個使用指定charsetOutputStreamWriter

示例(用OutputStreamWriter向文件中寫入字符、字符串):

    // 創建文件輸出字節流
    OutputStream fos = new FileOutputStream("src/note/test.txt");
    // 將文件輸出字節流轉換爲字符流
    OutputStreamWriter osw = new OutputStreamWriter(fos);
    // 向流中添加字符
    osw.append('胡');
    // 向流中寫入字符串
    osw.write("hello world");
    osw.flush();
(2). FileReader && FileWriter

FileReaderInputStreamReader的一個子類,用於從文件中讀取字符數據。該類只能按照本地平臺的字符編碼來讀取數據,用戶不能指定其他字符編碼類型。構造方法:

  • FileReader(File file):參數file指定需要讀取的文件。
  • FileReader(String name):參數name指定需要讀取的文件的路徑。

FileWriterOutputStreamWriter的一個子類,用於向文件中寫入字符數據。該類只能按照本地平臺的字符編碼來寫入數據,用戶不能指定其他字符編碼類型。構造方法:

  • FileWriter(File file):參數file指定需要寫入的文件。
  • FileWriter(File file, boolean append):參數file指定需要寫入的文件,參數append代表是追加還是覆蓋。
  • FileWriter(FileDescriptor fd):構造與文件描述符關聯的FileWriter對象。
  • FileWriter(String fileName):參數fileName指定需要寫入的文件的路徑。
  • FileWriter(String fileName, boolean append):指定需要寫入的文件的路徑,參數append代表是追加還是覆蓋。

示例(用FileWriter向文件寫入字符,並用FileReader讀取出來):

    FileWriter fw = new FileWriter("src/note/test.txt");
    fw.write("hello world!");
    fw.flush();
    fw.close();
    FileReader fr = new FileReader("src/note/test.txt");
    int n;
    while((n = fr.read()) != -1) {
        System.out.print((char)n); // hello world!
    }
    fr.close();
(3). BufferedReader && BufferedWriter

帶有緩存的字符輸入輸出流,常用於讀寫文件,效率高。BufferedWriter提供了newLine()方法,BufferedReader提供了readLine()方法。

BufferedReader:帶有緩存的字符輸出流。構造方法:

  • BufferedReader(Reader in):創建使用默認大小的輸入緩衝區的緩衝字符輸入流。需要傳入一個Reader類型的參數,可以傳入FileReader類型,可以理解爲給FileReader添加新功能(讀一行)。
  • BufferedReader(Reader in, int sz):創建使用指定大小的輸入緩衝區的緩衝字符輸入流。需要傳入一個Reader類型的參數,可以傳入FileReader類型,可以理解爲給FileReader添加新功能(讀一行)。

BufferedWriter:帶有緩存的字符輸入流。構造方法:

  • BufferedWriter(Writer out):創建使用默認大小的輸入緩衝區的緩衝字符輸出流。需要傳入一個Writer類型的參數,可以傳入FileWriter類型,可以理解爲給FileWriter添加新功能(寫一行)。
  • BufferedWriter(Writer out, int sz):創建使用指定大小的輸入緩衝區的緩衝字符輸出流。需要傳入一個Writer類型的參數,可以傳入FileWriter類型,可以理解爲給FileWriter添加新功能(寫一行)。

示例(利用BufferedReaderBufferedWriter實現文件的讀寫,寫一行、讀一行):

    FileWriter fw = new FileWriter("src/note/test.txt");
    BufferedWriter bw = new BufferedWriter(fw);
    bw.write("hello world!");
    bw.newLine(); // 另起一行
    bw.write("第二行");
    bw.flush();
    bw.close();
    fw.close();
    FileReader fr = new FileReader("src/note/test.txt");
    BufferedReader br = new BufferedReader(fr);
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
        // hello world!
        // 第二行
    }
    br.close();
    fr.close();

三、 實例

1. 本地文件傳輸(網絡傳輸文件基礎)

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * @author HZJ
 * @date 2019年6月14日 下午2:42:15
 * @description 實現文件的複製功能,inPath代表輸入的文件路徑,outPath代表輸出的文件路徑。
 */
public class Test {
	private static String inPath = "src/note/in_test.jpg"; // 輸入圖片路徑
	private static String outPath = "src/note/out_test.jpg"; // 輸出圖片路徑

	public static void main(String[] args) throws Exception {

		// 創建輸入文件對象
		File in_file = new File(inPath);
		if(!in_file.exists()) {
			throw new Exception("文件不存在!");
		}
		// 創建文件輸入流(字節流)
		FileInputStream in = new FileInputStream(in_file);

		// 創建輸出文件對象
		File out_file = new File(outPath);
		// 創建文件輸出流(字節流)
		FileOutputStream out = new FileOutputStream(out_file);

		byte[] b = new byte[1024]; // 利用一個數組緩存,提高讀取效率
		int n;
		while ((n = in.read(b)) != -1) { // n = -1 說明已經讀到流的末尾了
			// 注意這裏第三個參數不能是數組b的長度(1024),否則最後一次循環可能會多寫數據到out流中,
			// 因爲文件的可讀字節不一定是b的長度(1024)的整數倍。
			out.write(b, 0, n);
		}
		out.flush();
		out.close();
		in.close();
	}
}

2. 網絡傳輸文件

要先啓動Server.java,再啓動Client.java

Client.java

package note;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class Client {
	// 服務器ip地址
	private static String ip = "127.0.0.1";
	// 服務器端口
	private static int port = 10000;
	//本地文件,即需要發送的文件
	private static String client_file = "src/note/client.jpg";

	public static void main(String[] args) {
		try {
			System.out.println("正在連接服務器。。。");
			// 連接服務器
			Socket socket = new Socket(ip, port);
			System.out.println("與服務器連接成功!準備發送文件。。。");
			// 獲取套接字輸出流
			OutputStream out = socket.getOutputStream();
			// 獲取本地文件輸入流
			FileInputStream in = new FileInputStream(client_file);
			// 緩存數組
			byte[] b = new byte[1024];
			// 每次讀出的字節長度
			int n;
			while ((n = in.read(b)) != -1) {
				// 將字節數組b中長度爲n的子數組寫入out
				out.write(b, 0, n);
			}
			out.flush();
			out.close();
			in.close();
			socket.close();
			System.out.println("文件發送成功!");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

Server.java

package note;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
	// 監聽10000端口號
	private static int port = 10000;
	// 本地文件,即接收到的文件存放的地址
	private static String server_file = "src/note/server.jpg";

	public static void main(String[] args) {
		try {
			System.out.println("服務器啓動中。。。");
			// 監聽10000端口號,等待客戶端連接
			ServerSocket serverSocket = new ServerSocket(port);
			System.out.println("服務器啓動成功,監聽" + port + "端口,等待客戶端連接。。。");
			// 與客戶端建立連接
			Socket server = serverSocket.accept();
			System.out.println("與客戶端成功建立連接,接收文件中。。。");
			// 獲取套接字輸入流
			InputStream in = server.getInputStream();
			// 獲取本地文件輸出流
			FileOutputStream out = new FileOutputStream(server_file);
			// 緩存數組
			byte[] b = new byte[1024];
			// 每次讀出的字節長度
			int n;
			while ((n = in.read(b)) != -1) {
				// 將字節數組b中長度爲n的子數組寫入out
				out.write(b, 0, n);
			}
			out.flush();
			out.close();
			in.close();
			server.close();
			serverSocket.close();
			System.out.println("服務器接收文件成功!文件路徑:" + server_file);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章