雖然 io 包提供了不少型別、方法和函式,但有時候使用起來不是那麼方便。比如讀取乙個檔案中的所有內容。為此,標準庫中提供了一些常用、方便的io操作函式。
說明:這些函式使用都相對簡單,一般就不舉例子了。
有時候我們需要傳遞乙個io.readcloser的例項,而我們現在有乙個io.reader的例項,比如:strings.reader,這個時候nopcloser就派上用場了。它包裝乙個io.reader,返回乙個io.readcloser,而相應的close方法啥也不做,只是返回nil。
比如,在標準庫net/http包中的newrequest,接收乙個io.reader的body,而實際上,request的body的型別是io.readcloser,因此,**內部進行了判斷,如果傳遞的io.reader也實現了io.readcloser介面,則轉換,否則通過ioutil.nopcloser包裝轉換一下。相關**如下:
rc, ok := body.(io.readcloser)
if !ok && body != nil
如果沒有這個函式,我們得自己實現乙個。當然,實現起來很簡單,讀者可以看看nopcloser的實現。
很多時候,我們需要一次性讀取io.reader中的資料,通過上一節的講解,我們知道有很多種實現方式。考慮到讀取所有資料的需求比較多,go提供了readall這個函式,用來從io.reader中一次讀取所有資料。
func readall(r io.reader) (byte, error)
閱讀該函式的原始碼發現,它是通過bytes.buffer中的readfrom來實現讀取所有資料的。
筆試題:編寫程式輸出某目錄下的所有檔案(包括子目錄)
是否見過這樣的筆試題?
在go中如何輸出目錄下的所有檔案呢?首先,我們會想到查os包,看file型別是否提供了相關方法(關於os包,後面會講解)。
其實在ioutil中提供了乙個方便的函式:readdir,它讀取目錄並返回排好序的檔案和子目錄名(os.fileinfo)。通過這個方法,我們可以很容易的實現「面試題」。
下面的例子實現了類似unix中的tree命令,不過它在windows下也執行的很好哦。
// 未實現-l引數功能
func main() )
}}// 列出dirname目錄中的目錄樹,實現類似unix中的tree命令
// curhier 當前層級(dirname為第一層)
// hiermap 當前層級的上幾層是否需要'|'的對映
func tree(dirname string, curhier int, hiermap map[int]bool) error
fileinfos, err := ioutil.readdir(dirabs)
if err != nil
filenum := len(fileinfos)
for i, fileinfo := range fileinfos else
fmt.print(strings.repeat(" ", 3))
}// map是引用型別,所以新建乙個map
tmpmap := map[int]bool{}
for k, v := range hiermap
if i+1 == filenum else
fmt.print("-- ")
fmt.println(fileinfo.name())
if fileinfo.isdir()
}return nil
}
readfile 讀取整個檔案的內容,在上一節我們自己實現了乙個函式讀取檔案整個內容,由於這種需求很常見,因此go提供了readfile函式,方便使用。readfile的是實現和readall類似,不過,readfile會先判斷檔案的大小,給bytes.buffer乙個預定義容量,避免額外分配記憶體。
writefile 函式的簽名如下:
func writefile(filename string, data byte, perm os.filemode) error
它將data寫入filename檔案中,當檔案不存在時會建立乙個(檔案許可權由perm指定);否則會先清空檔案內容。對於perm引數,我們一般可以指定為:0666,具體含義os包中講解。
小提示readfile 原始碼中先獲取了檔案的大小,當大小 < 1e9時,才會用到檔案的大小。按原始碼中注釋的說法是fileinfo不會很精確地得到檔案大小。
作業系統中一般都會提供臨時目錄,比如linux下的/tmp目錄(通過os.tempdir()可以獲取到)。有時候,我們自己需要建立臨時目錄,比如go工具鏈原始碼中(src/cmd/go/build.go),通過tempdir建立乙個臨時目錄,用於存放編譯過程的臨時檔案:
b.work, err = ioutil.tempdir("", "go-build")
第乙個引數如果為空,表明在系統預設的臨時目錄(os.tempdir)中建立臨時目錄;第二個引數指定臨時目錄名的字首,該函式返回臨時目錄的路徑。
相應的,tempfile用於建立臨時檔案。如gofmt命令的原始碼中建立臨時檔案:
f1, err := ioutil.tempfile("", "gofmt")
引數和ioutil.tempdir引數含義類似。
這裡需要注意:建立者建立的臨時檔案和臨時目錄要負責刪除這些臨時目錄和檔案。如刪除臨時檔案:
defer func() ()
discard 對應的型別(type devnull int
)實現了io.writer介面,同時,為了優化io.copy到discard,避免不必要的工作,實現了io.readerfrom介面。
devnull 在實現io.writer介面時,只是簡單的返回(標準庫檔案:src/pkg/io/ioutil.go)。
func (devnull) write(p byte) (int, error)
而readfrom的實現是讀取內容到乙個buf中,最大也就8192位元組,其他的會丟棄(當然,這個也不會讀取)。 輸入輸出流
c 通過以下幾個類支援檔案的輸入和輸出 ofstream寫操作的檔案類由ostream引申而來 ifstream讀操作的檔案類由istream引申而來 fstream可同時讀寫操作的檔案類由iostream引申而來 ifstream in tian.txt 開啟乙個檔案 ifstream in in...
輸入輸出流
預定義流類的物件與通用的流運算子 1 cin 2 cout 3 cerr是ostream類物件,在標準輸出裝置上顯示錯誤資訊 不帶緩衝,立即顯示 輸入輸出流 ostream 和 ofstream istream 和 ifstream fstream 定義檔案輸出流物件 fstream outfile...
輸入輸出流
流 按照方向分為 輸入流和輸出流。以記憶體為參照物將資料從資料來源中讀取到記憶體,為輸入流,也叫讀取流。將資料從記憶體中寫入資料來源,為輸出流,也稱為寫入流 流按照型別分 分為位元組流,字元流和物件流。由於計算機採用二進位制,所有資料的傳輸都是以位元組為單位傳輸。所以無論是那種流,其本質都是位元組流...