首頁 > 軟體

Golang檔案讀寫操作詳情

2022-07-25 14:03:09

一、概念

檔案是資料來源(儲存資料的地方)的一種,檔案最主要的作用就是儲存資料。

檔案在程式中是以流的形式來操作的。

  • 輸入流和輸出流

  • :資料在資料來源(檔案)和程式(記憶體)之間經歷的路徑
  • 輸入流:資料從資料來源(檔案)到程式(記憶體)的路徑
  • 輸出流:資料從程式(記憶體)到資料來源(檔案)的路徑

二、讀取檔案操作

2.1 開啟和關閉檔案

開啟檔案:

func Open(filename string) (file *File, err error)

Open開啟一個檔案用於讀取。如果操作成功,返回的檔案物件的方法可用於讀取資料;對應的檔案描述符具有O_RDONLY模式。如果出錯,錯誤底層型別是*PathError

關閉檔案:

func (f *File) Close() error

Close關閉檔案f,使檔案不能用於讀寫。它返回可能出現的錯誤。

範例:

package main
import (
	"fmt"
	"os"
)
func main() {
	//唯讀方式開啟當前目錄下的test.txt
	file, err := os.Open("test.txt")
	if err != nil {
		fmt.Println("open file failed!,err:",err)
	}
	//返回的是一個指標
	fmt.Println(&file)

	//關閉檔案
	//err = file.Close()
	//if err != nil{
	//	fmt.Println("close file failed!,err:",err)
	//}
	//為了防止檔案忘記關閉,通常使用defer註冊檔案關閉語句。
	defer file.Close() // 關閉檔案
}

執行結果:

0xc0000ce018

defer 語句

  • defer—般用於資源的釋放和異常的捕捉。
  • defer語句會將其後面跟隨的語句進行延遲處理;跟在defer後面的語言將會在程式進行最後的return之後再執行。
  • defer歸屬的函數即將返回時,將延遲處理的語句按defer的逆序進行執行,也就是說,先被defer的語句最後被執行,最後被 defer的語句,最先被執行。

2.2 file.Read() 讀取檔案

Read 方法定義

func (f *File) Read(b []byte) (n int, err error)

從檔案物件中讀取長度為b的位元組,返回當前讀到的位元組數以及錯誤資訊。因此使用該方法需要先初始化一個符合內容大小的空的位元組列表。讀取到檔案的末尾時,該方法返回0,io.EOF

ReadAt方法定義

func (file *File) ReadAt(b []byte, off int64) (n int, err Error)

從檔案的off偏移量開始讀取長度為b的位元組。返回讀取到位元組數以及錯誤資訊。當讀取到的位元組數n小於想要讀取位元組的長度len(b)的時候,該方法將返回非空的error。當讀到檔案末尾時,err返回io.EOF。

  • b:是指定位元組長度的緩衝區
  • off:int64型別的偏移量,從此位置開始讀取。

注意:ReadAt 絕對不允許出現,沒有讀滿 buffer,又非 EOF,又沒有 err 的情況發生,這個是介面語意明確規定的,這是一個非常細節的區別。

一次性讀取

適用於讀取較小檔案使用:

package main
import (
	"fmt"
	"io"
	"os"
)
func main() {
	//1、唯讀方式開啟當前目錄下的test2.txt
	file, err := os.Open("test2.txt")
	if err != nil {
		fmt.Println("open file failed!,err:",err)
		return
	}
	//3、當函數退出時,及時關閉file
	//使用 defer 內建函數 當函數退出時才會呼叫,要及時關閉否則會記憶體洩露
	defer file.Close()
	//2、使用Read方法讀取資料,注意一次只會讀取128個位元組
	tmp := make([]byte, 128)
	n, err := file.Read(tmp)
	//使用ReadAt方法讀取資料,注意一次只會讀取6個位元組
	//tmp := make([]byte, 6)
	//n, err := file.ReadAt(tmp,6)

	//io.EOF 表示檔案的末尾
	if err == io.EOF {
		fmt.Println("檔案讀取完畢")
		return
	}
	if err != nil {
		fmt.Println("read file failed,err:",err)
		return
	}
	fmt.Printf("讀取了 %d 位元組資料n", n)
	fmt.Println(string(tmp[:n]))
}

執行結果:

讀取了 13 位元組資料
Hello Golang!

迴圈讀取

使用 for 迴圈讀取檔案中的所有資料:

package main
import (
	"fmt"
	"io"
	"os"
)
//迴圈讀取檔案
func main() {
	//唯讀方式開啟當前目錄下的test.txt
	file, err := os.Open("test.txt")
	if err != nil {
		fmt.Println("open file failed!,err:",err)
		return
	}
	//關閉檔案
	defer file.Close()
	//迴圈讀取檔案
	var content []byte
	//使用Read方法讀取資料,注意一次只會讀取128個位元組
	tmp := make([]byte, 128)

	for {
		n, err := file.Read(tmp)	//每次讀取128個位元組
		if err == io.EOF {
			fmt.Println("檔案讀取完畢")
			break
		}
		if err != nil {
			fmt.Println("read file failed,err:",err)
			return
		}
		//每次讀取的內容都追加到已知的byte切片中
		content = append(content,tmp[:n]...)
	}
	//將byte型別轉換結果,列印結果
	fmt.Println(string(content))
}

執行結果:

檔案讀取完畢
水陸草木之花,可愛者甚蕃。晉陶淵明獨愛菊。自李唐來,世人甚愛牡丹。
予獨愛蓮之出淤泥而不染,濯清漣而不妖,中通外直,不蔓不枝,香遠益清,亭亭淨植,可遠觀而不可褻玩焉。

予謂菊,花之隱逸者也;牡丹,花之富貴者也;蓮,花之君子者也。
噫!菊之愛,陶後鮮有聞。蓮之愛,同予者何人?牡丹之愛,宜乎眾矣!

說明:

這裡的迴圈讀取檔案其實就是一個不斷追加的過程,將每次讀取的128個位元組追加到預先定義好的content切片中,最後將切片轉換成string型別,進行列印顯示。

2.3 bufio 讀取檔案

語法:

//bufio.NewReader(rd io.Reader) *Reader
r := bufio.NewReader(file)

//func (b *Reader) ReadString(delim byte) (string, error)
n, err := r.Read(buf)

引數

返回值:

使用 NewReader 讀取檔案時,首先,需要開啟檔案,接著, 使用開啟的檔案返回的檔案控制程式碼當作 函數引數 傳入 NewReader。

最後,使用 NewReader 返回的 reader 物件呼叫 Read 來讀取檔案。檔案讀取結束的標誌是返回的 n 等於 0,因此,如果需要讀取整個檔案內容,那麼我們需要使用 for 迴圈 不停的讀取檔案,直到 n 等於 0。

  • file:要讀取的檔案控制程式碼;
  • buf:讀取的資料存放的緩衝區。
  • n:讀取到的長度
  • err:讀取失敗,則返回錯誤資訊。

範例:

package main
import (
	"bufio"
	"fmt"
	"io"
	"os"
)
//bufio讀取檔案
func main() {

	//唯讀方式開啟當前目錄下的test.txt
	file, err := os.Open("test.txt")
	if err != nil {
		fmt.Println("open file failed!,err:",err)
		return
	}
	//關閉檔案,避免記憶體洩露
	defer file.Close()
	//通過bufio緩衝區讀取檔案
	reader := bufio.NewReader(file)	//建立緩衝區,將檔案內容放入到緩衝區
	//迴圈讀取檔案資訊
	for {
		line, err := reader.ReadString('n')	//讀到一個換行就結束

		if err == io.EOF {	 //io.EOF 表示檔案的末尾
			//輸出最後的內容
			if len(line) != 0 {
				fmt.Println(line)
			}
			fmt.Println("檔案讀取完畢")
			break
		}
		if err != nil {
			fmt.Println("read file failed,err:",err)
			return
		}
		fmt.Println(line)
	}
}

執行結果:

水陸草木之花,可愛者甚蕃。晉陶淵明獨愛菊。自李唐來,世人甚愛牡丹。
予獨愛蓮之出淤泥而不染,濯清漣而不妖,中通外直,不蔓不枝,香遠益清,亭亭淨植,可遠觀而不可褻玩焉。
予謂菊,花之隱逸者也;牡丹,花之富貴者也;蓮,花之君子者也。
噫!菊之愛,陶後鮮有聞。蓮之愛,同予者何人?牡丹之愛,宜乎眾矣!
檔案讀取完畢

2.4 ioutil 讀取檔案

語法:

func ReadFile(name string) ([]byte, error)
  • name:檔案路徑地址

使用 io/ioutil.ReadFile 方法一次性將檔案讀取到記憶體中,只需要將檔名作為引數傳入。

範例:

package main
import (
	"fmt"
	"io/ioutil"
)
//ioutil 讀取整個檔案
func main() {
	content, err := ioutil.ReadFile("test.txt")
	if err != nil {
		fmt.Println("read failed,err:",err)
		return
	}
	fmt.Println(string(content))
}

執行結果:

水陸草木之花,可愛者甚蕃。晉陶淵明獨愛菊。自李唐來,世人甚愛牡丹。
予獨愛蓮之出淤泥而不染,濯清漣而不妖,中通外直,不蔓不枝,香遠益清,亭亭淨植,可遠觀而不可褻玩焉。
予謂菊,花之隱逸者也;牡丹,花之富貴者也;蓮,花之君子者也。
噫!菊之愛,陶後鮮有聞。蓮之愛,同予者何人?牡丹之愛,宜乎眾矣!

注意:如果檔案比較大,一次性讀取整個檔案會佔用很大的記憶體,影響執行效率。

建議讀取小檔案時使用,不太適用於大檔案的讀取。

效率比較

  • 當檔案較小(KB 級別)時,ioutil > bufio > file.Read()。
  • 當檔案大小比較常規(MB 級別)時,三者差別不大,但 bufio 優勢已經顯現出來。
  • 當檔案較大(GB 級別)時,bufio > file.Read()> ioutil。

當讀取小檔案時,使用ioutil效率明顯優於file.Read()bufio,但如果是大檔案,bufio讀取會更快,效率更高。

三、寫入檔案操作

3.1 os.OpenFile()函數

語法:

func OpenFile(name string, flag int, perm uint32) (file *File, err Error)

引數

os.OpenFile()函數能夠以指定模式開啟檔案,從而實現檔案寫入相關功能。

  • name:要檔案路徑+檔名;
  • flag:開啟檔案的模式,唯讀、讀寫等;
  • perm:檔案許可權,一個八進位制數。r(讀)04,W(寫)02,x(執行)01。

模式flag種類:

模式含義
os.O_WRONLY只寫
os.O_CREATE如果不存在檔案,建立檔案
os.O_RDONLY唯讀
os.O_RDWR可讀可寫
os.O_TRUNC開啟時清空檔案原先內容
os.O_APPEND追加

若同時想用多種可用|拼接不同模式。

檔案許可權perm:

使用4位元8進位制數來表示三種型別使用者的許可權,首位取0,形式即0XXX

  • 第一個X表示的是檔案所有者的許可權;
  • 第二個X表示的是組使用者的許可權;
  • 第三個X表示的是其他使用者的許可權。

每位數位所代表的許可權:讀r=4,寫w=2,可執行x=1

數位rwx許可權
0---所有許可權均無
1--x可執行
2-w-可寫
3-wx可寫,可執行
4r--可讀
5r-x可讀,可執行
6rw-可讀,可寫
7rwx可讀,可寫,可執行

常使用的0644-rw-r--r--),表示檔案所有者可讀寫,同組使用者及其他使用者只可讀。

3.2 Write和WriteString 方式寫入

Write語法:

func (file *File) Write(b []byte) (n int, err Error)

引數

返回值:

使用 Write 方法寫檔案,接受的 引數 是一個要寫入的檔案內容的 位元組 陣列。如果寫入成功,返回成功寫入的位元組數,如果寫入失敗,返回 error 資訊。

WriteString語法:

func (f *File) WriteString(s string) (n int, err error)

引數

返回值:

使用 WriteString 方法寫檔案,接受的引數是一個要寫入的檔案內容的 字串。如果寫入成功,返回成功寫入的位元組數,如果寫入失敗,返回 error 資訊。

  • file:檔案物件
  • b:要寫入的檔案內容
  • n: 成功寫入的位元組數
  • err:寫入失敗,則返回錯誤資訊
  • f:檔案物件
  • s:要寫入的檔案內容
  • n:成功寫入的位元組數
  • err:寫入失敗,則返回錯誤資訊

範例:

package main
import (
	"fmt"
	"os"
)
//建立並寫入資料
//Write 和 WriteString
func main() {
	//os.O_CREATE|os.O_RDWR:如果不存在檔案,建立檔案,可讀可寫
	//0666對應:-rw-rw-rw-
	file, err := os.OpenFile("D:/bb.txt", os.O_CREATE|os.O_RDWR, 0666)
	if err != nil {
		fmt.Println("open file failed,err:",err)
		return
	}
	defer file.Close()
	str := "Hello Golangrn"
	file.Write([]byte(str))	//寫入位元組切片資料
	file.WriteString("直接寫入的字串資料") //直接寫入字串資料
}

3.3 bufio.NewWriter

語法:

func NewWriter(w io.Writer) *Writer
func (b *Writer) WriteString(s string) (int, error)
func (b *Writer) Flush() error

將要寫入的內容寫入快取中,在執行flush的時候才會被寫到磁碟。

  • 建立writer範例
  • 將資訊寫入快取
  • 將緩衝寫入檔案

範例:

package main
import (
	"bufio"
	"fmt"
	"os"
)
//bufio.NewWriter
func main() {
	//1、開啟檔案
	file,err := os.OpenFile("D:/cc.txt",os.O_CREATE|os.O_TRUNC|os.O_WRONLY,0666)
	if err != nil {
		fmt.Println("open file failed,err:",err)
		return
	}
	//5、關閉檔案流
	defer file.Close()

	//2、建立writer物件
	writer := bufio.NewWriter(file)

	for i := 0; i < 10; i++ {
		writer.WriteString("Hello Golangrn")	//3、將資料先寫入快取
	}
	writer.Flush()	//4、將快取中的內容寫入檔案
}

3.4 ioutil.WriteFile

語法:

func WriteFile(filename string, data []byte, perm os.FileMode) error

引數

返回值

使用 WriteFile 方法寫檔案,接受的第一個 引數 是一個 string 型別 的檔名,第二個引數是一個要寫入的檔案內容的 byte 陣列,最後一個引數是檔案的許可權。如果寫入成功,返回空的 error 資訊,如果寫入失敗,返回 error 資訊。

  • filename:檔案路徑+檔名稱
  • data:要寫入的檔案內容
  • perm:檔案許可權
  • err:寫入失敗,則返回錯誤資訊

範例:

package main
import (
	"fmt"
	"io/ioutil"
)
//ioutil.WriteFile
func main() {
	str := "Hello Golang"
	err := ioutil.WriteFile("D:/dd.txt", []byte(str), 0666)
	if err != nil {
		fmt.Println("write file failed,err:",err)
		return
	}
}

四、複製檔案

4.1 通過ioutil進行復制

package main

import (
	"fmt"
	"io/ioutil"
)
//複製檔案
//ioutil 進行復制
//編寫一個函數,接收兩個檔案路徑 srcFileName dstFileName
func CopyFile(srcFileName string,dstFileName string)(err error){
	input, err := ioutil.ReadFile(srcFileName)
	if err != nil {
		fmt.Println(err)
		return err
	}
	err = ioutil.WriteFile(dstFileName, input, 0644)
	if err != nil {
		fmt.Println("Error creating",dstFileName)
		fmt.Println(err)
		return err
	}
	return nil
}
func main() {
	srcFile := "D:/aa.zip"
	dstFile := "D:/bb.zip"
	err := CopyFile(srcFile, dstFile)
	if err == nil {
		fmt.Printf("拷貝完成n")
	}else {
		fmt.Printf("拷貝錯誤 err=%vn",err)
	}
}

4.2 以檔案流的方式複製檔案

package main
import (
	"fmt"
	"io"
	"os"
)
//複製資料
func CopyFile(srcFileName string,dstFileName string)(err error){
	source, _ := os.Open(srcFileName)
	destination, _ := os.OpenFile(dstFileName, os.O_CREATE|os.O_WRONLY, 0666)
	buf := make([]byte, 128)
	for {
		n, err := source.Read(buf)
		if err != nil && err != io.EOF {
			return err
		}
		if n == 0 {
			break
		}
		if _,err := destination.Write(buf[:n]); err != nil {
			return err
		}
	}
	return nil
}
func main() {
	srcFile := "D:/aa.zip"
	dstFile := "D:/bb.zip"
	err := CopyFile(srcFile, dstFile)
	if err == nil {
		fmt.Printf("拷貝完成n")
	}else {
		fmt.Printf("拷貝錯誤 err=%vn",err)
	}
}

五、其他操作

package main
import (
	"fmt"
	"os"
)
func main() {
	//檔案重新命名
	err01 := os.Rename("D:/aa.txt","D:/ee.txt")	//只能同盤操作
	if err01 != nil {
		fmt.Println(err01)
	}
	//建立目錄
	err02 := os.Mkdir("D:/aa", 0666)
	if err02 != nil {
		fmt.Println(err02)
	}
	//一次建立多個目錄
	err03 := os.MkdirAll("D:/aa/bb/cc",0666)	//建立多級目錄
	if err03 != nil {
		fmt.Println(err03)
	}
	//刪除目錄和檔案
	err04 := os.Remove("D:/ee.txt")
	if err04 != nil {
		fmt.Println(err04)
	}
	//一次刪除多個目錄或者檔案
	err05 := os.RemoveAll("D:/aa")
	if err05 != nil {
		fmt.Println(err05)
	}
}

到此這篇關於Golang檔案讀寫操作詳情的文章就介紹到這了,更多相關Go檔案操作內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com