摘要:文件流,系統(tǒng)標(biāo)準(zhǔn)輸入輸出流,標(biāo)準(zhǔn)錯(cuò)誤流,還有一開始提到的流,還有一些后臺(tái)技術(shù)如對(duì)請(qǐng)求響應(yīng)流的抽象,都可以見到流的概念。語(yǔ)言的庫(kù)中定義了打開文件流時(shí)必須指定的集中打開方式,表示用于讀取,用于寫入,用于讀寫。
—— 對(duì)這個(gè)問題的思考來源于前幾天對(duì) Java Socket 編程的嘗試,TCP 協(xié)議要求建立一個(gè) Socket 連接(著名的三次握手)之后才能進(jìn)行通信,而連接雙方進(jìn)行數(shù)據(jù)的發(fā)送與接受,都是通過對(duì)輸入輸出流的機(jī)制來完成的。
流的概念流作為概念應(yīng)該是語(yǔ)言無關(guān)的。文件IO流,Unix系統(tǒng)標(biāo)準(zhǔn)輸入輸出流,標(biāo)準(zhǔn)錯(cuò)誤流(stdin, stdout, stderr),還有一開始提到的 TCP 流,還有一些 Web 后臺(tái)技術(shù)(如Nodejs)對(duì)HTTP請(qǐng)求/響應(yīng)流的抽象,都可以見到流的概念。
K&R 在 C Programming Language 書中提到流是這樣定義的:
流 (stream) 是與磁盤或其它外圍設(shè)備關(guān)聯(lián)的數(shù)據(jù)的源或目的地。
可以把流理解成是對(duì)程序與外界交換數(shù)據(jù)的一種抽象,這里的外界限定是有必要的,通常不會(huì)把程序內(nèi)部的數(shù)據(jù)流動(dòng)抽象為流,畢竟在程序內(nèi)部,數(shù)據(jù)流動(dòng)是由函數(shù)調(diào)用、返回來完成的。而當(dāng)我們使用三個(gè)標(biāo)準(zhǔn)IO流時(shí),我們關(guān)心的是怎樣通過它們與外界交互;當(dāng)我們使用文件流時(shí),我們關(guān)心的是將內(nèi)存中的數(shù)據(jù)持久化到磁盤文件中(或從磁盤中讀數(shù)據(jù)導(dǎo)內(nèi)存)。
于是數(shù)據(jù)從 A 處“流”向 B 處,可以類比像水流一樣從高處流向低處。在水流動(dòng)的過程中,作為最基本物理組成單位的水分子是不變的,相應(yīng)的數(shù)據(jù)流也有它最小的組成單位。在不同的編程語(yǔ)言中,這個(gè)最小單位通常是字節(jié)流(二進(jìn)制流)中的字節(jié),或者字符流(文本流)中的字符。
——但不會(huì)是其他數(shù)據(jù)類型,就像我們從來沒聽說過數(shù)字流?,或者浮點(diǎn)數(shù)流,甚至數(shù)組流?
因?yàn)樽止?jié)是計(jì)算機(jī)保存數(shù)據(jù)的最終形式,而字符是其它數(shù)據(jù)結(jié)構(gòu)序列化后的表現(xiàn)形式,也是人可以閱讀的形式。與外界的交互需要這些通用的格式。不關(guān)心數(shù)據(jù)的內(nèi)容,只需要完整地傳輸原始數(shù)據(jù)時(shí),考慮字節(jié)流即可;關(guān)心傳輸字符和字符串時(shí),就需要對(duì)字符流進(jìn)行操作,stdio.h頭文件里那一大坨輸入輸出函數(shù)就是干這個(gè)的。比如fgetc(FILE *stream)從文本流中讀入一個(gè)字符。
另一方面,根據(jù)數(shù)據(jù)流動(dòng)的方向,可以再抽象出輸出流和輸入流的概念。從程序內(nèi)部到外部的流向是輸出流,從程序外部到內(nèi)部的流向是輸入流。
C 語(yǔ)言的stdio.h庫(kù)中定義了打開文件流時(shí)必須指定的集中打開方式,"r"表示用于讀取,"w"用于寫入,"r+"用于讀寫。類似地,Java 語(yǔ)言的java.io包中包含了InputStream, OutputStream 明確區(qū)分的輸入流類和輸出流類,并且二者都是抽象類,意味著必須根據(jù)需要使用它們各自的子類進(jìn)行實(shí)例化。
通過流操作實(shí)現(xiàn)(最)簡(jiǎn)單的文件拷貝根據(jù)實(shí)際的代碼可以幫助理解stream,下面是一段用C語(yǔ)言標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)的最簡(jiǎn)單的文件拷貝功能。
出于學(xué)習(xí)目的,這段代碼偷懶沒有任何容錯(cuò)功能,是典型的反面教材, 不過 whatever 了,不信你真拿去編譯一下,是真的可以完整拷貝文件!除了不能拷貝目錄,不能拷貝不存在的文件,不能拷貝文件權(quán)限,不能漏掉目的文件名或者路徑,不能靈活處理文件軟鏈接硬鏈接。等等等等blahblah(所以其實(shí)連看上去很簡(jiǎn)單的cp程序也是要有一大坨因素要考慮和支持的(啊跑題了
// mini_cp.c #include#define BUFFER_SIZE 512 int main(int argc, char *argv[]) { // 從命令行參數(shù)中獲得 SOURCE 和 DES 文件流 FILE *src = fopen(argv[1], "rb"); FILE *des = fopen(argv[2], "wb"); long int num; // buffer 是讀寫的緩沖數(shù)組 char buffer[BUFFER_SIZE]; while(!feof(src)) { num = fread(buffer, sizeof(char), BUFFER_SIZE, src); fwrite(buffer, sizeof(char), num, des); } fclose(src); fclose(des); return 0; }
這個(gè)自制的mini_cp程序不難理解,核心的邏輯可以分解為三個(gè)步驟:
打開源文件流FILE *src和目的文件流FILE *des
循環(huán)執(zhí)行 { 每次從src流讀取最多512字節(jié)的數(shù)據(jù) => 并寫入des流 } 直到源文件讀取結(jié)束
關(guān)閉文件流
核心邏輯是非常清晰明了的,這樣的邏輯也是流操作的普遍原理,嘗試其他語(yǔ)言的實(shí)現(xiàn),其實(shí)都已經(jīng)大同小異,往往都少不了一個(gè)緩沖區(qū)的概念(或?qū)ο螅?/p>
來看一下 Java 版本的同等實(shí)現(xiàn):
import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Copy { private static final int BUFFER_SIZE = 512; public static void main(String[] args) throws IOException { File srcFile = new File(args[0]); File desFile = new File(args[1]); int recvBytesSize; byte[] buffer = new byte[BUFFER_SIZE]; FileInputStream in = new FileInputStream(srcFile); FileOutputStream out = new FileOutputStream(desFile); while((recvBytesSize = in.read(buffer)) != -1) { out.write(buffer, 0, recvBytesSize); } in.close(); out.close(); } }
面向?qū)ο笪陡鼭?del>(代碼更冗長(zhǎng))了有木有?但也正是因?yàn)槊嫦驅(qū)ο螅琂ava 把理論上的 stream 抽象為類,讓我們直接獲得類的實(shí)例(即對(duì)象),從而對(duì)對(duì)象進(jìn)行操作。還是挺不賴的是吧,雖然代碼更長(zhǎng)了沒錯(cuò),但是更 OO 啊~
寫到這里已經(jīng)能回答流基本是怎么一回事了,那么最后順便再來放一段拷貝程序的ruby實(shí)現(xiàn);
require "fileutils" FileUtils.cp("SOURCE.txt", "DEST.txt")
哈?
嗯。
... That"s why we love Ruby...(逃。。。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/64758.html
摘要:收集器用作高級(jí)歸約剛剛的結(jié)論又引出了優(yōu)秀的函數(shù)式設(shè)計(jì)的另一個(gè)好處更易復(fù)合和重用。更具體地說,對(duì)流調(diào)用方法將對(duì)流中的元素觸發(fā)一個(gè)歸約操作由來參數(shù)化。另一個(gè)常見的返回單個(gè)值的歸約操作是對(duì)流中對(duì)象的一個(gè)數(shù)值字段求和。 用流收集數(shù)據(jù) 我們?cè)谇耙徽轮袑W(xué)到,流可以用類似于數(shù)據(jù)庫(kù)的操作幫助你處理集合。你可以把Java 8的流看作花哨又懶惰的數(shù)據(jù)集迭代器。它們支持兩種類型的操作:中間操作(如 filt...
摘要:由于配置流是從關(guān)系型數(shù)據(jù)庫(kù)中讀取,速度較慢,導(dǎo)致實(shí)時(shí)數(shù)據(jù)流流入數(shù)據(jù)的時(shí)候,配置信息還未發(fā)送,這樣會(huì)導(dǎo)致有些實(shí)時(shí)數(shù)據(jù)讀取不到配置信息。從數(shù)據(jù)庫(kù)中解析出來,再去統(tǒng)計(jì)近兩周占比。 Flink 學(xué)習(xí) https://github.com/zhisheng17/flink-learning 麻煩路過的各位親給這個(gè)項(xiàng)目點(diǎn)個(gè) star,太不易了,寫了這么多,算是對(duì)我堅(jiān)持下來的一種鼓勵(lì)吧! showI...
摘要:但有一個(gè)限制它們不能修改定義的方法的局部變量的內(nèi)容。如前所述,這種限制存在的原因在于局部變量保存在棧上,并且隱式表示它們僅限于其所在線程。 2014年,Oracle發(fā)布了Java8新版本。對(duì)于Java來說,這顯然是一個(gè)具有里程碑意義的版本。尤其是那函數(shù)式編程的功能,避開了Java那煩瑣的語(yǔ)法所帶來的麻煩。 這可以算是一篇Java8的學(xué)習(xí)筆記。將Java8一些常見的一些特性作了一個(gè)概要的...
閱讀 555·2021-11-25 09:44
閱讀 2645·2021-11-24 09:39
閱讀 2315·2021-11-22 15:29
閱讀 3528·2021-11-15 11:37
閱讀 3395·2021-09-24 10:36
閱讀 2523·2021-09-04 16:41
閱讀 1004·2021-09-03 10:28
閱讀 1858·2019-08-30 15:55