io 包提供了對I/O
原語的基本接口,其基本任務是包裝這些原語已有的實現(如 os 包里的原語),使之成為共享的公共接口,這些公共接口抽象出了泛用的函數並附加一些相關的原語的操作。
io 包常用接口
io.Reader 和 io.Writer 接口
io.Reader 接口
// io.Reader 接口用於包裝基本的讀取方法
type Reader interface {
Read(p []byte) (n int, err error)
}
從底層輸入流讀取最多len(p)
個字節數據寫入到 p 中,返回讀取的字節數 n 和遇到的任何錯誤 err。即使 Read 方法返回的n < len(p)
,在調用過程也會占用len(p)
個字節作為暫存空間。若可讀取的數據不足len(p)
個字節,Read 方法會返回可用數據,而不是等待更多數據。
當 Read 方法讀取n > 0
個字節后遇到一個錯誤或 EOF (end-of-file)
,會返回讀取的字節數,同時在該次調用返回一個非 nil 錯誤或者在下一次的調用時返回 0 和該錯誤。一般情況下,io.Reader
接口會在輸入流的結尾返回非 0 的字節數,返回值 err 為 nil 或者io.EOF
,但下次調用必然返回(0, io.EOF)
。調用者在考慮錯誤之前應當首先處理n > 0
字節的返回數據,這樣做可以正確地處理在讀取一些字節后產生的I/O
錯誤,同時允許 EOF 的出現。
☕️ 示例代碼
package main
import (
"fmt"
"io"
"os"
"strings"
)
func ReadFrom(reader io.Reader, num int) ([]byte, error) {
p := make([]byte, num)
n, err := reader.Read(p)
if n > 0 {
return p[:n], nil
}
return p, err
}
func main() {
// 例子 1:從標准輸入中讀取 11 個字節
// 控制台輸入:hello world!!!!
data, err := ReadFrom(os.Stdin, 11)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", data) // hello world
// 例子 2:從普通文件中讀取 9 個字節
// 文件內容:hello world!!!!
f, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer f.Close()
data, err = ReadFrom(f, 9)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", data) // hello wor
// 例子 3:從字符串中讀取 12 個字節
r := strings.NewReader("hello world!!!!")
data, err = ReadFrom(r, 12)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", data) // hello world!
}
io.Writer 接口
// io.Writer 接口用於包裝基本的寫入方法
type Writer interface {
Write(p []byte) (n int, err error)
}
將len(p)
個字節數據從 p 中寫入底層的輸出流,返回寫入的字節數 n 和遇到的任何錯誤 err。如果 Write 方法返回的n < len(p)
,它就必須返回一個 非 nil 的錯誤。Write 方法不能修改切片 p 中的數據,即使臨時修改也不行。
⭐️ 示例代碼
package main
import (
"fmt"
"io"
"os"
)
func WriteTo(write io.Writer, p []byte) (int, error) {
n, err := write.Write(p)
return n, err
}
func main() {
p := []byte("hello world!!!\n")
// 例子 1:將字符串寫入到標准輸出
n, err := WriteTo(os.Stdout, p)
if err != nil {
panic(err)
}
fmt.Printf("write %d bytes\n", n)
// 例子 2:將字符串寫入文件中
f, err := os.Create("./test.txt")
if err != nil {
panic(err)
}
defer f.Close()
n, err = WriteTo(f, p)
if err != nil {
panic(err)
}
fmt.Printf("write %d bytes\n", n)
}
// 控制台輸出:
// hello world!!!
// write 15 bytes
// write 15 bytes
// test.txt 文件內容:
// hello world!!!
io.Reader 和 io.Writer 接口實現類型
os.File
同時實現了io.Reader
和io.Writer
接口strings.Reader
實現了io.Reader
接口bufio.Reader
和bufio.Writer
分別實現了io.Reader
和io.Writer
接口bytes.Buffer
同時實現了io.Reader
和io.Writer
接口bytes.Reader
實現了io.Reader
接口compress/gzip.Reader
和compress/gzip.Writer
分別實現了io.Reader
和io.Writer
接口crypto/cipher.StreamReader
和crypto/cipher.StreamWriter
分別實現了io.Reader
和io.Writer
接口crypto/tls.Conn
同時實現了io.Reader
和io.Writer
接口encoding/csv.Reader
和encoding/csv.Writer
分別實現了io.Reader
和io.Writer
接口mime/multipart.Part
實現了io.Reader
接口net/conn
分別實現了io.Reader
和io.Writer
接口(Conn 接口定義了 Read 和 Write 方法)
除此之外,io 包本身也有這兩個接口的實現類型。如:
- 實現了
io.Reader
的類型:LimitedReader
、PipeReader
、SectionReader
- 實現了
io.Writer
的類型:PipeWriter
以上類型中,常用的類型有:os.File
、strings.Reader
、bufio.Reader/Writer
、bytes.Buffer
、bytes.Reader
。
io.ReaderAt 和 io.WriterAt 接口
io.ReaderAt 接口
// io.ReaderAt 接口包裝了基本的 ReadAt 方法
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}
從底層輸入流的偏移量 off 位置讀取最多len(p)
字節數據寫入 p,返回讀取的字節數 n 和遇到的任何錯誤 err。當ReadAt
方法返回的n < len(p)
時,它會返回一個非 nil 的錯誤來說明沒有讀取更多的字節的原因。在這方面,ReadAt
方法是比 Read 方法要嚴格的。
即使ReadAt
方法返回的n < len(p)
,在調用過程也會占用len(p)
個字節作為暫存空間。如果有部分可用數據,但不夠len(p)
字節,ReadAt 方法會阻塞直到獲取len(p)
個字節數據或者遇到錯誤。在這方面,ReadAt
方法和 Read 方法是不同的。如果 ReadAt 方法返回時到達輸入流的結尾,返回的n == len(p)
,返回的 err 為 nil 或者io.EOF
。
如果ReadAt
方法是從某個有偏移量的底層輸入流讀取,ReadAt
方法既不應影響底層的偏移量,也不被它所影響。ReadAt
方法的調用者可以對同一輸入流執行並行的ReadAt
調用。
✏️ 示例代碼
package main
import (
"fmt"
"strings"
)
func main() {
reader := strings.NewReader("Go語言中文網")
p := make([]byte, 6)
n, err := reader.ReadAt(p, 2)
if err != nil {
panic(err)
}
fmt.Printf("Read %d bytes: %s\n", n, p) // Read 6 bytes: 語言
}
io.WriterAt 接口
// io.WriterAt 接口包裝了基本的 WriteAt 方法
type WriterAt interface {
WriteAt(p []byte, off int64) (n int, err error)
}
將len(p)
個字節數據從 p 中寫入偏移量 off 位置的底層輸出流中,返回寫入的字節數 n 和遇到的任何錯誤 err。當WriteAt
方法返回的n < len(p)
時,它就必須返回一個非 nil 的錯誤。
如果WriteAt
方法寫入的目標是某個有偏移量的底層輸出流,WriteAt
方法既不應影響底層的偏移量,也不被它所影響。ReadAt
方法的調用者可以對同一輸入流執行並行的WriteAt
調用。(前提是寫入范圍不重疊)
📚 示例代碼
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Create("writeAt.txt")
if err != nil {
panic(err)
}
defer file.Close()
file.WriteString("Golang中文社區——這里是多余hh!!!")
n, err := file.WriteAt([]byte("Go語言中文網"), 24)
if err != nil {
panic(err)
}
fmt.Printf("write %d bytes", n) // write 17 bytes
file.WriteString("hello world!!!")
}
// test.txt 文件內容:
// Golang中文社區——Go語言中文網!!!hello world!!!
io.ReaderFrom 和 io.WriteTo 接口
io.ReaderFrom 接口
// io.ReaderFrom 接口包裝了基本的 ReadFrom 方法
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error)
}
從輸入流 r 中讀取數據,寫入底層輸出流,直到 EOF 或發生錯誤。其返回值 n 為讀取的字節數,除io.EOF
之外,在讀取過程中遇到的其它錯誤 err 也將被返回。
注意:ReadFrom
方法不會返回err == io.EOF
✌ 示例代碼
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// test.txt 文件內容:hello world!!!
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
// 調用 file 的讀操作從文件中讀取文件,並寫入標准輸出
writer := bufio.NewWriter(os.Stdout)
n, err := writer.ReadFrom(file)
if err != nil {
panic(err)
}
fmt.Printf("read %d bytes\n", n)
// 將緩存中的數據 flush 到控制台
writer.Flush()
}
// 控制台輸出:
// hello world!!!
// read 15 bytes
io.WriteTo 接口
// io.WriterTo 接口包裝了基本的 WriteTo 方法
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}
從底層輸入流中讀取數據,寫入輸出流 w 中,直到沒有數據可寫或發生錯誤。其返回值 n 為寫入的字節數,在寫入過程中遇到的任何錯誤也將被返回。
✌ 示例代碼
package main
import (
"bytes"
"fmt"
"os"
)
func main() {
// 調用 r 的讀操作讀取數據,並寫入到標准輸出
r := bytes.NewReader([]byte("Go語言中文網\n"))
n, err := r.WriteTo(os.Stdout)
if err != nil {
panic(err)
}
fmt.Printf("write %d bytes\n", n)
}
// 控制台輸出:
// Go語言中文網
// write 18 bytes
io.Seeker 接口
// io.Seeker 接口用於包裝基本的讀/寫偏移量移動方法
type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}
設定下一次讀/寫的位置。offset 為相對偏移量,而 whence 決定相對位置:0 為相對文件開頭,1 為相對當前位置,2 為相對文件結尾。返回新的偏移量(相對開頭)和可能的錯誤。
移動到一個絕對偏移量為負數的位置會導致錯誤,移動到任何偏移量為正數的位置都是合法的,但其下一次I/O
操作的具體行為則要看底層的實現。
// whence 的值,在 io 包中定義了相應的常量,推薦使用這些常量
const (
SeekStart = 0 // seek relative to the origin of the file
SeekCurrent = 1 // seek relative to the current offset
SeekEnd = 2 // seek relative to the end
)
✍ 示例代碼
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Go語言中文網")
fmt.Println(r.Len()) // 17
n, err := r.Seek(-6, io.SeekEnd)
if err != nil {
panic(err)
}
fmt.Println(n) // 11
c, _, _ := r.ReadRune()
fmt.Printf("%c\n", c) // 文
}
io.Closer 接口
// io.Closer 接口用於包裝基本的關閉方法
type Closer interface {
Close() error
}
該接口比較簡單,用於關閉數據流。文件 (os.File
)、歸檔(壓縮包)、數據庫連接、Socket 等需要手動關閉的資源都實現了io.Closer
接口。實際編程中,經常將 Close 方法的調用放在 defer 語句中。
💡 示例代碼
package main
import (
"os"
)
func main() {
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
}
io.ByteReader、io.ByteWriter 和 io.ByteScanner 接口
// io.ByteReader 接口是基本的 ReadByte 方法的包裝
type ByteReader interface {
ReadByte() (c byte, err error)
}
// io.RuneReader 接口是基本的 ReadRune 方法的包裝
type ByteWriter interface {
WriteByte(c byte) error
}
// io.ByteScanner 接口在基本的 ReadByte 方法之外還添加了 UnreadByte 方法
type ByteScanner interface {
ByteReader
UnreadByte() error
}
io.ByteReader
和io.ByteWriter
接口用於讀/寫一個字節。在標准庫中,有如下類型實現了io.ByteReader
或io.ByteWriter
接口:
bufio.Reader
和bufio.Writer
分別實現了io.ByteReader
和io.ByteWriter
接口bytes.Buffer
同時實現了io.ByteReader
和io.ByteWriter
接口bytes.Reader
實現了io.ByteReader
接口strings.Reader
實現了io.ByteReader
接口
io.ByteScanner
接口內嵌了io.ByteReader
接口之外還添加了UnreadByte
方法,該方法會還原最近一次讀取操作讀出的最后一個字節,相當於讓讀偏移量向前移動一個字節。注意,連續兩次UnreadByte
方法調用而中間沒有任何讀取操作,會返回錯誤。
☕️ 示例代碼
package main
import (
"bytes"
"fmt"
)
func main() {
var ch byte
fmt.Scanf("%c\n", &ch)
var b bytes.Buffer
// 寫入一個字節
err := b.WriteByte(ch)
if err != nil {
panic(err)
}
fmt.Println("寫入一個字節成功!准備讀取該字節……")
// 讀取一個字節
newCh, _ := b.ReadByte()
fmt.Printf("讀取的字節:%c\n", newCh)
// 還原最近一次讀取操作讀出的最后一個字節
b.UnreadByte()
newCh2, _ := b.ReadByte()
fmt.Printf("再次讀取的字節:%c\n", newCh2)
}
// 控制台輸入:
// A
// 控制台輸出:
// 寫入一個字節成功!准備讀取該字節……
// 讀取的字節:A
// 再次讀取的字節:A
io.RuneReader 和 io.RuneScanner 接口
// io.RuneReader 是基本的 ReadRune 方法的包裝
type RuneReader interface {
ReadRune() (r rune, size int, err error)
}
// io.RuneScanner 接口在基本的 ReadRune 方法之外還添加了 UnreadRune 方法
type RuneScanner interface {
RuneReader
UnreadRune() error
}
和io.ByteReader/io.ByteScanner
接口類似,不過io.RuneReader/io.RuneScanner
接口操作的是一個字符。ReadRune
方法讀取單個UTF-8
編碼的字符,返回其 rune 和該字符占用的字節數。如果沒有有效的字符,會返回錯誤。
UnreadRune
方法還原前一次ReadRune
操作讀取的 unicode 碼值,相當於讓讀偏移量前移一個碼值長度。注意,UnreadRune
方法調用前必須調用ReadRune
方法,UnreadRune
方法比UnreadByte
嚴格很多。
⭐️ 示例代碼
package main
import (
"bytes"
"fmt"
)
func main() {
var ch rune
fmt.Scanf("%c\n", &ch)
var b bytes.Buffer
// 寫入一個字符,WriteRune 方法是 bytes.Buffer 自定義的方法
b.WriteRune(ch)
fmt.Println("寫入一個字符成功!准備讀取該字符……")
// 讀取一個字符
newCh, _, _ := b.ReadRune()
fmt.Printf("讀取的字符:%c\n", newCh)
// 還原前一次 ReadRune 操作讀取的字符
b.UnreadRune()
newCh2, _, _ := b.ReadRune()
fmt.Printf("再次讀取的字符:%c\n", newCh2)
}
// 控制台輸入:
// 您
// 控制台輸出:
// 寫入一個字符成功!准備讀取該字符……
// 讀取的字符:您
// 再次讀取的字符:您
其它聚合接口
// io.ReadWriter 接口聚合了基本的讀寫操作
type ReadWriter interface {
Reader
Writer
}
// io.ReadCloser 接口聚合了基本的讀取和關閉操作
type ReadCloser interface {
Reader
Closer
}
// io.ReadSeeker 接口聚合了基本的讀取和移位操作
type ReadSeeker interface {
Reader
Seeker
}
// io.ReadWriteCloser 接口聚合了基本的讀寫和關閉操作
type ReadWriteCloser interface {
Reader
Writer
Closer
}
// io.ReadWriteSeeker 接口聚合了基本的讀寫和移位操作
type ReadWriteSeeker interface {
Reader
Writer
Seeker
}
// io.WriteSeeker 接口聚合了基本的寫入和移位操作
type WriteSeeker interface {
Writer
Seeker
}
// io.WriteCloser 接口聚合了基本的寫入和關閉操作
type WriteCloser interface {
Writer
Closer
}
io 包常用函數
文件復制 io.Copy/io.CopyN
// 將 src 的數據拷貝到 dst,直到在 src 上到達 EOF 或發生錯誤。返回拷貝的字節數和遇到的第一個錯誤
// 對成功的調用,返回值 err 為 nil 而非 EOF,因為 Copy 定義為從 src 讀取直到 EOF,它不會將讀取到 EOF視為應報告的錯誤
// 如果 src 實現了 WriterTo 接口,會調用 src.WriteTo(dst) 進行拷貝;如果 dst 實現了 ReaderFrom 接口,會調用 dst.ReadFrom(src) 進行拷貝
func Copy(dst Writer, src Reader) (written int64, err error)
// 從 src 拷貝 n 個字節數據到 dst,直到在 src 上到達 EOF 或發生錯誤。返回復制的字節數和遇到的第一個錯誤
// 只有 err 為 nil 時,written 才會等於 n。如果 dst 實現了 ReaderFrom 接口,本函數會調用它實現拷貝
func CopyN(dst Writer, src Reader, n int64) (written int64, err error)
✏️ 示例代碼
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 打開原始文件
originalFile, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer originalFile.Close()
// 創建新的文件作為目標文件
newFile, err := os.Create("./test_copy.txt")
if err != nil {
panic(err)
}
defer newFile.Close()
// 從源中復制字節到目標文件
writtenNum, err := io.Copy(newFile, originalFile)
if err != nil {
panic(err)
}
fmt.Printf("Copied %d bytes.", writtenNum)
// 將文件內容 flush 到硬盤中
err = newFile.Sync()
if err != nil {
panic(err)
}
}
讀取至少 N 個字節 io.ReadAtLeast
// 從 r 至少讀取 min 字節數據填充進 buf。函數返回寫入的字節數和錯誤(如果沒有讀取足夠的字節)
// 只有沒有讀取到字節時才可能返回 io.EOF;如果讀取了有但不夠的字節時遇到了 EOF,函數會返回ErrUnexpectedEOF
// 如果 min 比 buf 的長度還大,函數會返回 ErrShortBuffer。只有返回值 err 為 nil 時,返回值 n 才會不小於 min
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)
📚 示例代碼
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 打開文件,只讀
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
byteSlice := make([]byte, 512)
minBytes := 8
// io.ReadAtLeast() 在不能得到最小的字節的時候會返回錯誤,但已讀的文件數據仍保留在 byteSlice 中
numBytesRead, err := io.ReadAtLeast(file, byteSlice, minBytes)
if err != nil {
panic(err)
}
fmt.Printf("Number of bytes read: %d\n", numBytesRead)
fmt.Printf("Data read: %s\n", byteSlice)
}
讀取正好 N 個字節 io.ReadFull
// 從 r 精確地讀取 len(buf) 字節數據填充進 buf。函數返回寫入的字節數和錯誤(如果沒有讀取足夠的字節)
// 只有沒有讀取到字節時才可能返回 io.EOF;如果讀取了有但不夠字節數時遇到了 EOF,函數會返回ErrUnexpectedEOF
// 只有返回值 err 為 nil 時,返回值 n 才會等於 len(buf)
func ReadFull(r Reader, buf []byte) (n int, err error)
✌ 示例代碼
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 打開文件,只讀
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
// file.Read() 可以讀取一個小文件到大的 byte slice 中,
// 但是 io.ReadFull() 在文件的字節數小於 byte slice 字節數的時候會返回錯誤
byteSlice := make([]byte, 2)
numBytesRead, err := io.ReadFull(file, byteSlice)
if err != nil {
panic(err)
}
fmt.Printf("Number of bytes read: %d\n", numBytesRead)
fmt.Printf("Data read: %s\n", byteSlice)
}
字符串寫入 io.WriteString
// 將字符串 s 的內容寫入 w 中,返回寫入的字節數和遇到的任何錯誤。如果 w 已經實現了 WriteString 方法,函數會直接調用該方法
func WriteString(w Writer, s string) (n int, err error)
✍ 示例代碼
package main
import (
"fmt"
"io"
"os"
)
func main() {
file, err := os.Create("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
// 寫入字符串
n, err := io.WriteString(file, "hello world!!!!")
if err != nil {
panic(err)
}
fmt.Printf("Write %d btyes\n", n)
}
合並多個輸入流 io.MultiReader
// 返回一個將提供的 Readers 在邏輯上串聯起來的 Reader 接口,它們依次被讀取
// 當所有的輸入流都讀取完畢,Read 才會返回 EOF。如果 readers 中任一個返回了非 nil 非 EOF 的錯誤,Read 方法會返回該錯誤
func MultiReader(readers ...Reader) Reader
✍ 示例代碼
package main
import (
"bytes"
"fmt"
"io"
"strings"
)
func main() {
readers := []io.Reader{
strings.NewReader("from strings reader!!!"),
bytes.NewBufferString("from bytes buffer!!!"),
}
reader := io.MultiReader(readers...)
data := make([]byte, 0, 128)
buf := make([]byte, 10)
for n, err := reader.Read(buf); err != io.EOF; n, err = reader.Read(buf) {
if err != nil {
panic(err)
}
data = append(data, buf[:n]...)
}
fmt.Printf("%s\n", data) // from strings reader!!!from bytes buffer!!!
}
合並多個輸出流 io.MultiWriter
// 創建一個 Writer 接口,會將提供給其的數據同時寫入 Writers 中的所有輸出流
func MultiWriter(writers ...Writer) Writer
💡 示例代碼
package main
import (
"io"
"os"
)
func main() {
file, err := os.Create("./text.txt")
if err != nil {
panic(err)
}
defer file.Close()
writers := []io.Writer{
file,
os.Stdout,
}
writer := io.MultiWriter(writers...)
// 會同時在 text.txt 文件和控制台中輸出:Go語言中文網
writer.Write([]byte("Go語言中文網"))
}
讀取並自動寫入 io.TeeReader
// 返回一個將其從 r 讀取的數據寫入 w 的 Reader 接口,所有通過該接口對 r 的讀取都會執行對應的對 w 的寫入
// 沒有內部的緩沖,寫入必須在讀取完成前完成,寫入時遇到的任何錯誤都會作為讀取錯誤返回
func TeeReader(r Reader, w Writer) Reader
☕️ 示例代碼
package main
import (
"io"
"os"
)
func main() {
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
r := io.TeeReader(file, os.Stdout)
// r 讀取內容后,會自動寫入到 os.Stdout,所以控制台會顯示文件中內容
r.Read(make([]byte, 100))
}
io/ioutil 包常用函數
雖然 io 包提供了不少類型、方法和函數,但有時候使用起來不是那么方便,比如讀取一個文件中的所有內容。為此,Go 語言在io/ioutil
包提供了一些常用、方便的I/O
操作函數。
接口類型轉換 ioutil.NopCloser
// 用一個無操作的 Close 方法包裝 r,返回一個 io.ReadCloser 接口。Close 方法什么也不做,只是返回 nil
func NopCloser(r io.Reader) io.ReadCloser
⭐️ 示例代碼
package main
import (
"fmt"
"io/ioutil"
"strings"
)
func main() {
r := strings.NewReader("hello world")
nc := ioutil.NopCloser(r)
// Close 方法什么也不做,只是返回 nil
nc.Close()
p := make([]byte, 20)
n, err := nc.Read(p)
if err != nil {
panic(err)
}
fmt.Printf("read %d bytes:%s", n, p) // read 11 bytes:hello world
}
讀取全部字節 ioutil.ReadAll
// 從 r 讀取數據直到 EOF 或遇到 error,返回讀取的數據和遇到的錯誤
// 成功的調用返回的 err 為 nil 而非 EOF。因為本函數定義為讀取 r 直到 EOF,它不會將讀取返回的 EOF 視為應報告的錯誤
func ReadAll(r io.Reader) ([]byte, error)
✏️ 示例代碼
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
// os.File.Read(), io.ReadFull() 和 io.ReadAtLeast() 在讀取之前都需要一個固定大小的 byte slice
// 但 ioutil.ReadAll() 會讀取 reader (這個例子中是file) 的每一個字節,然后把字節 slice 返回
data, err := ioutil.ReadAll(file)
if err != nil {
panic(err)
}
fmt.Printf("Data as hex: %x\n", data)
fmt.Printf("Data as string: %s\n", data)
fmt.Println("Number of bytes read:", len(data))
}
讀取目錄 ioutil.ReadDir
// 讀取 dirname 目錄,並返回排好序的文件和子目錄名
func ReadDir(dirname string) ([]os.FileInfo, error)
📚 示例代碼
package main
import (
"fmt"
"io/ioutil"
)
func main() {
// 輸出當前目錄下的所有文件(包括子目錄)
listAll(".", 0)
}
func listAll(path string, curHeight int) {
fileInfos, err := ioutil.ReadDir(path)
if err != nil {
fmt.Println(err)
return
}
for _, info := range fileInfos {
if info.IsDir() {
for tmpHeight := curHeight; tmpHeight > 0; tmpHeight-- {
fmt.Printf("|\t")
}
fmt.Println(info.Name(), "\\")
listAll(path+"/"+info.Name(), curHeight+1)
} else {
for tmpHeight := curHeight; tmpHeight > 0; tmpHeight-- {
fmt.Printf("|\t")
}
fmt.Println(info.Name())
}
}
}
快讀文件 ioutil.ReadFile
io/ioutil
包提供ReadFile
函數可以處理打開文件、讀取文件和關閉文件一系列的操作。如果需要簡潔快速地讀取文件到字節切片中,可以使用它。該方法定義如下:
// 從 filename 指定的文件中讀取數據並返回文件的內容
// 成功的調用返回的 err 為 nil 而非 EOF。因為本函數定義為讀取整個文件,它不會將讀取返回的 EOF 視為應報告的錯誤
func ReadFile(filename string) ([]byte, error)
✌ 示例代碼
package main
import (
"fmt"
"io/ioutil"
)
func main() {
// 讀取文件到字節 slice 中
data, err := ioutil.ReadFile("./test.txt")
if err != nil {
panic(err)
}
fmt.Printf("Data read: %s\n", data)
}
快寫文件 ioutil.WriteFile
和ReadFile
函數對應,io/ioutil
包提供WriteFile
函數可以處理創建或者打開文件、寫入字節切片和關閉文件一系列的操作。如果需要簡潔快速地寫字節切片到文件中,可以使用它。該方法定義如下:
// 向 filename 指定的文件中寫入數據。如果文件不存在將按給出的權限創建文件,否則在寫入數據之前清空文件
func WriteFile(filename string, data []byte, perm os.FileMode) error
✍ 示例代碼
package main
import (
"io/ioutil"
)
func main() {
// 向文件寫入 "hello world!!!"
err := ioutil.WriteFile("./test.txt", []byte("hello world!!!"), 0666)
if err != nil {
panic(err)
}
}
創建臨時目錄 ioutil.TempDir
// 在 dir 目錄里創建一個新的、使用 prfix 作為前綴的臨時文件夾,並返回文件夾的路徑
// 如果 dir 是空字符串,表明使用系統默認的臨時目錄(參見 os.TempDir 函數)中創建臨時目錄
// 不同程序同時調用該函數會創建不同的臨時目錄,調用本函數的程序有責任在不需要臨時文件夾時摧毀它
func TempDir(dir, prefix string) (name string, err error)
💡 示例代碼
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
// 創建臨時目錄
name, err := ioutil.TempDir("F:", "temp")
if err != nil {
panic(err)
}
// 刪除臨時目錄
defer os.Remove(name)
fmt.Println(name) // F:\temp2305919538
}
創建臨時文件 ioutil.TempFile
// 在 dir 目錄下創建一個新的、使用 prefix 為前綴的臨時文件,以讀寫模式打開該文件並返回 os.File 指針
// 如果 dir 是空字符串,表明使用系統默認的臨時目錄(參見 os.TempDir 函數)中創建臨時文件
// 不同程序同時調用該函數會創建不同的臨時文件,調用本函數的程序有責任在不需要臨時文件時摧毀它
func TempFile(dir, prefix string) (f *os.File, err error)
💡 示例代碼
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
// 創建並打開臨時文件
f, err := ioutil.TempFile("F:", "temp")
if err != nil {
panic(err)
}
// 刪除臨時文件
defer func() {
f.Close()
os.Remove(f.Name())
}()
fmt.Println(f.Name()) // F:\temp2379571275
}