Java 標準 IO 流編程一覽筆錄( 下 )

八、回推流:PushbackInputStream與PushbackReader

PushbackInputStream/PushbackReader 用於解析InputStream/Reader內的數據,容許你讀取字節/字符後,回推(pushback)到流中,而不破壞流。java

PushbackInputStream類具備如下構造函數:git

PushbackInputStream(InputStream inputStream)
PushbackInputStream(InputStream inputStream,int numBytes)

第一種形式建立的流對象容許將一個字節返回到輸入流; 第二種形式建立的流對象具備一個長度爲numBytes的回推緩存,從而容許將多個字節回推到輸入流中。編程

提供了unread()方法,以下所示:緩存

void unread(int b)
void unread(byte[] buffer)
void unread(byte[] buffer,int offset,int numBytes)

第一種形式回推b的低字節,這會使得後續的read()調用會把這個字節再次讀取出來。第二種形式回推buffer中的字節。第三種形式回推buffer中從offset開始的numBytes個字節。當回推緩存已滿時,若是試圖回推字節,就會拋出IOException異常。函數

示例:spa

public static void main(String[] args) throws IOException {
		String filepath = "file.bin";
		java.io.OutputStream os = null;
		try {
			os = new FileOutputStream(filepath);
			os.write('#');
			os.write(new byte[]{'a', 'b', 'c', 'd'});
			os.flush();// 把緩衝區內的數據刷新到磁盤
		} finally {
			if (os != null) {
				os.close();// 關閉流
			}
		}
		/**
		 * 回推(pushback)
		 */
		PushbackInputStream pis = null;
		try {
			//pis = new PushbackInputStream(new FileInputStream(filepath));
			pis = new PushbackInputStream(new FileInputStream(filepath), 3);
			int len = -1;
			byte[] bytes = new byte[2];
			while ((len = pis.read(bytes)) != -1) {
				if ('b' == bytes[0]) {
					//pis.unread('U');
					//pis.unread(bytes);
					pis.unread(new byte[]{'1', '2', '3'});
				}
				for (int i = 0; i < len; i++) {
					System.out.print(((char) bytes[i]));
				}
			}
			System.out.println();
		} finally {
			if (pis != null)
				pis.close();
		}
		/**
		 * 會發現PushbackInputStream並無改變目標介質的數據,不破壞流
		 */
		try {
			pis = new PushbackInputStream(new FileInputStream(filepath));
			int len = -1;
			byte[] bytes = new byte[2];
			while ((len = pis.read(bytes)) != -1) {
				for (int i = 0; i < len; i++) {
					System.out.print(((char) bytes[i]));
				}
			}
		} finally {
			if (pis != null)
				pis.close();
		}
	}

注:PushbackInputStream對象會使得InputStream對象(用於建立PushbackInputStream對象)的mark()或reset()方法無效。對於準備使用mark()或reset()方法的任何流來講,都應當使用markSupported()方法進行檢查。.net

 

九、行數記錄:LineNumberInputStream與LineNumberReader

LineNumberInputStream與LineNumberReader提供跟蹤行號的附加功能。行是以回車符 ('\r')、換行符 ('\n') 或回車符後面緊跟換行符結尾的字節序列。在全部這三種狀況下,都以單個換行符形式返回行終止字符。 行號以 0 開頭,並在 read 返回換行符時遞增 1。 對象

使用getLineNumber()能夠獲取當前讀取所在行數。blog

示例:token

public static void main(String[] args) throws IOException {
		String filepath = "file.txt";
		java.io.Writer w = null;
		try {
			w = new FileWriter(filepath);
			w.write("百世山河任凋換,一輩子意氣未改遷。願從劫火投身去,重自寒灰飛赤鸞。\r\n");
			w.write("滄海桑田新幾度,月明還照舊容顏。琴心劍魄今何在,留見星虹貫九天。 \n");
			w.write("冰輪騰轉下西樓,永夜初晗凝碧天。長路尋仙三山外,道心自在紅塵間。 \n");
			w.write("何來慧劍破心繭,再把貂裘換酒錢。回望天涯攜手處,踏歌重訪白雲間。\n");
			w.write("何以飄零去,何以少團欒,何以別離久,何以不得安? \n");
			w.flush();// 把緩衝區內的數據刷新到磁盤
		} finally {
			if (w != null) {
				w.close();// 關閉流
			}
		}
		/**
		 * LineNumberReader
		 */
		LineNumberReader lnr = null;
		try {
			lnr = new LineNumberReader(new FileReader(filepath));
			int len = -1;
			char[] chars = new char[2];
			//int lastLineNumber = -1;
			while ((len = lnr.read(chars)) != -1) {
				for (int i = 0; i < len; i++) {
					System.out.print(((char) chars[i]));
				}
				/*int lineNumber = lnr.getLineNumber();
				if (lineNumber != lastLineNumber) {
					System.out.println("---------------行數:" + lineNumber);
					lastLineNumber = lineNumber;
				}*/
			}
			int lineNumber = lnr.getLineNumber();
			System.out.println("行數:" + lineNumber);
			System.out.println();
		} finally {
			if (lnr != null)
				lnr.close();
		}
	}

十、StreamTokenizer的使用

 StreamTokenizer定義了幾種基本的常量用於標識解析過程:TT_EOF(流結尾)、TT_EOL(行結尾)、TT_NUMBER(數字符號, 0 1 2 3 4 5 6 7 8 9 . -都屬於數字語法)、TT_WORD(一個單詞)。
ttype 在調用 nextToken 方法以後,此字段將包含剛讀取的標記的類型。
nval 若是當前標記是一個數字,則此字段將包含該數字的值。
sval 若是當前標記是一個文字標記,則此字段包含一個給出該文字標記的字符的字符串。

public static void main(String[] args) throws IOException {
	StreamTokenizer tokenizer = new StreamTokenizer(new StringReader("Sven had 7 shining ring..."));
	while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {// 流末尾
		if (tokenizer.ttype == StreamTokenizer.TT_WORD) {
			System.out.println(tokenizer.sval);
		} else if (tokenizer.ttype == StreamTokenizer.TT_NUMBER) {
			System.out.println(tokenizer.nval);
		} else if (tokenizer.ttype == StreamTokenizer.TT_EOL) {// 行末尾
			System.out.println();
		}
	}
	//System.out.println(tokenizer.lineno());
}

基本方法介紹一下:

 nextToken()  - 今後標記生成器的輸入流中解析下一個標記。

(1)標記註釋

commenChar(int ch) - 指定某個字符爲註釋字符,此字符以後直到行結尾都被stream tokenizer忽略。

slashSlashComments(boolean flag) - 若是爲true,則/*與*/之間的都被認爲是註釋,反之,不是。

slashStartComments(boolean flag) - 若是爲true,則//以後到行結尾的全部都被認爲是註釋,反之,不是。 

(2)基本語義

eolIsSignificant(boolean flag) - 決定一個行結束符是否被看成一個基本的符號處理,若是是true,則被看成一個基本符號,不看成普通的分隔符,若是是false,則保持原義,即看成普通的分隔符。

lowerCaseMode(boolean flag) - 決定是否讀取一個單詞時是否轉變成小寫。

parseNumbers() - 當stream tokenizer遭遇到一個單詞爲雙精度的浮點數時,會把它看成一個數字,而不是一個單詞。

resetSyntax() - 重置語法表使全部的字符都被認爲是「ordinary」。

(3)指定字符語義

ordinaryChar(int ch) - 指定字符在這個tokenizer中保持原義,即只會把當前字符認爲普通的字符,不會有其餘的語義。
ordinaryChars(int low, int hi) - 指定範圍內的字符保持語義,同上

whitespaceChars(int low, int hi) - 字符low與hi之間的全部字符都被看成爲空格符,即被認識爲tokenzier的分隔符。
wordChars(int low, int hi) - 字符low與hi之間的全部字符都被看成爲單詞的要素。一個單詞是由一個單詞要素後面跟着0個或者更多個單詞要素或者數字要素。

 

十一、合併流SequenceInputStream

SequenceInputStream會將與之相鏈接的流集組合成一個輸入流並從第一個輸入流開始讀取,直到到達文件末尾,接着從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的末尾爲止。 合併流的做用是將多個源合併合一個源。

public static void main(String[] args) throws IOException {
		String filepath1 = "file1.txt";
		String filepath2 = "file2.txt";
		java.io.Writer w = null;
		try {
			w = new FileWriter(filepath1);
			w.write("百世山河任凋換,一輩子意氣未改遷。願從劫火投身去,重自寒灰飛赤鸞。\r\n");
			w.write("滄海桑田新幾度,月明還照舊容顏。琴心劍魄今何在,留見星虹貫九天。 \n");
			w.write("冰輪騰轉下西樓,永夜初晗凝碧天。長路尋仙三山外,道心自在紅塵間。 \n");
			w.write("何來慧劍破心繭,再把貂裘換酒錢。回望天涯攜手處,踏歌重訪白雲間。\n");
			w.flush();// 把緩衝區內的數據刷新到磁盤
		} finally {
			if (w != null) {
				w.close();// 關閉流
			}
		}
		try {
			w = new FileWriter(filepath2);
			w.write("何以飄零去,何以少團欒,何以別離久,何以不得安? ");
			w.flush();// 把緩衝區內的數據刷新到磁盤
		} finally {
			if (w != null) {
				w.close();// 關閉流
			}
		}
		java.io.Reader r = null;
		try {
			Vector<InputStream> v = new Vector<InputStream>(2);
			InputStream s1 = new FileInputStream(filepath1);
			InputStream s2 = new FileInputStream(filepath2);
			v.addElement(s1);
			v.addElement(s2);
			r = new BufferedReader(new InputStreamReader(new SequenceInputStream(v.elements())));
			
			char[] data = new char[256];
			int len = -1;
			while ((len = r.read(data)) != -1) {// -1 表示讀取到達文件結尾
				//操做數據
				for (int i = 0; i < len; i++) {
					System.out.print(data[i]);
				}
			}
		} finally {
			if (r != null) {
				r.close();// 關閉流
			}
		}
	}

更多Demo:https://git.oschina.net/svenaugustus/MyJavaIOLab

本文只針對標準IO的知識總結,其餘IO總結姊妹篇(NIO)請參見:
+ JavaNIO編程一覽筆錄: https://my.oschina.net/langxSpirit/blog/899954

相關文章
相關標籤/搜索