Hike News
Hike News

Golang物件導向初識-day21-讀寫文件

終端讀寫

  • 之前常使用的fmt包,就是操作終端的代表
  • 操作終端相關文件句柄常量
    • os.Stdin:標準輸入
    • os.Stdout:標準輸出
    • os.Stderr:標準錯誤輸出

Fprintln(Fprintf)搭配os實現終端寫入

fmt.Println(io.Writer, a ...interface{})

  • 第一個參數為一個實現io.Writer接口的實例
  • 第二個參數為內容
1
2
3
4
5
6
7
8
9
10
package main

import (
"fmt"
"os"
)

func main(){
fmt.Fprintln(os.Stdout,"Hello World")
}
  • 其中的os.Stdout實現了io.Writer這個接口,因此可作為參數被傳入

result

1
Hello World
  • 可以發現其功能與fmt.Println()直接輸出的功能是一樣的

Scanln(Scanf)實現終端讀入

可使用Scanln()實現單行從終端讀入,其等價於Scanf()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func testScanln(){
var a int
var b string
fmt.Printf("input int & string:")
fmt.Scanln(&a,&b)
fmt.Fprintf(os.Stdout,"int:%d, string:%s",a,b)
}

func main(){
testScanln()
}
````
* 其`fmt.Scanln(&a,&b)`等同於`fmt.Scanf(%d %s,a,b)`

### result
```command
> input int & string:1234 Hello!!
> int:1234, string:Hello!!


Sscanf實現字符串格式化讀入

將特定的字符串格式化讀入,並賦值給不同的變量

1
2
3
4
5
6
7
8
9
10
11
12
func testSscanf(){
var a int
var b string
var c float64
str := "18 Hello 12.3"
fmt.Sscanf(str,"%d %s %f", &a, &b, &c) //注意給變量賦值要加上&
fmt.Fprintf(os.Stdout,"int:%d, string:%s, float:%.2f", a, b, c)
}

func main(){
testSscanf()
}
  • fmt.Sscanf(string, format, ...variables):
  1. 第一個參數為要讀入的字符串(string)
  2. 第二個參數為要讀入的格式(format)
  3. 第三個參數為可變參數,將讀入的值賦給各個變量(…variables)

result

1
int:18, string:Hello, float:12.30

文件讀寫

套用終端讀寫使用的Fprintln()方法,只要將實現io.Writer接口的文件句柄傳參亦能實現讀寫文件的功能

建立(Write)文件句柄

使用os.OpenFile()方法來開啟文件,且其賦值的變量就為文件句柄
os.OpenFile()方法通常用作寫文件

1
file,err := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY, 0664)
  • 第一個參數要是沒有填寫路徑則會打開(創建)在GoPATH下的文件

  • 第二個參數為文件開啟的模式 (為一常量),標記之間可使用|(操作)加成

    • os.O_WRONLY: 只寫
    • os.O_CREATE: 創建文件(如果文件不存在)
    • os.O_RDONLY: 只讀
    • os.O_RDWR: 讀寫皆可
    • os.O_TRUNC: 清空(文件存在情況下)
  • 第三個參數設置權限控制

    • r —> 004
    • w —> 002
    • x —> 001

example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"os"
"fmt"
)

func main(){
file,err := os.OpenFile("/tmp/test.log",os.O_CREATE|os.O_WRONLY,0664)
if err != nil {
fmt.Println("OpenFile Failed !, Error:",err)
return
}
defer file.Close()
fmt.Fprintln(file,"Test WritingFile!!")
}

result

1
2
> cat /tmp/test.log test.log
> Test WritingFile!!
  • 從上述結果可以知道File這個變量亦實現了io.Writer接口,才能作為參數被傳入到Fprintln()函數中

建立(Read)文件句柄

使用os.Open()方法來開啟文件
os.Open()方法通常用作讀文件

1
file,err := os.Open("test.log")
  • 只有一個參數,要是沒有填寫路徑預設會打開在GoPATH底下的同名文件

ioutil文件讀寫

位於io/ioutil包中,可將整個文件直接讀取或是直接寫入

  • 應用於讀取或是寫入的文件大小不是很大的時候

讀操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func testIoutilRead(FileName string){

//ioutil.Readfile可直接讀取整份文件的內容至緩衝區
file,err := ioutil.ReadFile(FileName)
if err != nil {

//os.Stderr亦實現了io.writer的操作,可返回錯誤至終端
fmt.Fprintf(os.Stderr,"File Error: %s",err)
return
}

//讀出來的data為[]byte類型,需轉為string型
fmt.Println(string(file))
}

func main(){
testIoutilRead("Testlog.log")
}

result

1
Test Hello World

寫操作

1
2
3
4
5
6
7
8
9
10
11
12
13
func testIoutilWrite(FileName string){

//將data直接寫入至文件中
err := ioutil.WriteFile(FileName,[]byte("Writing..."),0664)
if err != nil {
fmt.Fprintf(os.Stderr,"File Write Error:",err)
return
}
}

func main(){
testIoutilWrite("/tmp/test.log")
}
  • ioutil.WriteFile只有一個錯誤的返回值
  • 請注意寫入多data為[]byte類型
  • 第三個參數為權限設置

result

1
2
> cat /tmp/test.log
> Writing...

帶緩衝區(bufio)的讀寫

不直接操作底層的io(包括文件或是終端),而是將數據先寫入緩衝區,再由Golang或是操作系統將緩衝區的數據寫入文件中

  • 一般會先寫入到緩衝區中主要是為了性能,因為直接操作存放在硬碟的文件比記憶體的效率差很多

從終端讀入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func testbufio_terminalRead(){

//NewReader的參數為一個實現io.Reader接口的實例
bufioRead := bufio.NewReader(os.Stdin)
fmt.Printf("input something I can read:")

//ReadBytes方法的參數填入分隔符 讀到何種字元為止(注意填入代表byte的單引號)
str,err := bufioRead.ReadBytes('\n')
if err != nil{
fmt.Println("Terminal Read FAILED!!:",err)
return
}

//讀到的bytes要將其轉為string
fmt.Println(string(str))
}

func main(){
testbufio_terminalRead()
}
  • 請注意bufio.NewReader()創造的實例有很多方法可以自己嘗試
  • 亦可將os.Stdin替換成其他實現io.Reader接口的實例,實現不同的讀操作(文件,網路等)

result

1
2
> input something I can read:You have to read this sentence
> You have to read this sentence

從文件讀入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func testbufio_FileRead(FileName string) {

//前面有介紹讀文件通常使用os.Open()
file, err := os.Open(FileName)
if err != nil {
fmt.Println("file open failed with Error:", err)
return
}

//記得關閉文件
defer file.Close()

//新建一個緩衝區讀取的實例,file文件句柄實現了io.Reader接口
bufioRead := bufio.NewReader(file)

for {
//一次讀入一行
line,err := bufioRead.ReadString('\n')

// io.EOF為讀到文件最後一行時會返回的錯誤,捕捉錯誤以 確定讀到最後一行
if err == io.EOF {
fmt.Println("The End")
return
}
if err != nil && err != io.EOF {
fmt.Println("ReadLine Error:",err)
return
}
fmt.Printf(line)
}
}

func main(){
testbufio_FileRead("Testlog.log")
}

result

1
2
Test Hello World
The End

os.File

os.File是一個結構體(struct)
之前提到的os.Stdin,os.Stdout,os.Stderr,file文件句柄都屬於os.File的指針類型


寫入文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func testbufioWrite(FileName string) {
file,err := os.OpenFile(FileName,os.O_WRONLY|os.O_CREATE,0664)
if err != nil {
fmt.Fprintf(os.Stderr,"File Open Error:%s",err)
return
}
defer file.Close()

//創建一個bufio.NewWriter對象
writer := bufio.NewWriter(file)

//將要寫的內容先存入緩衝區
writer.WriteString("test bufio writing...")

//將寫入緩衝區(內存)的數據全部刷入文件(硬碟)
writer.Flush()
}

func main(){
testbufioWrite("bufioWritingTest.log")
}

讀取壓縮文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func testReadgzip(FileName string){
fi, err := os.Open(FileName)
if err != nil{
fmt.Fprintf(os.Stderr,"Open file err : %s",err)
os.Exit(1)
return
}
defer fi.Close() //記得最後須關閉文件

//gzip.NewReader方法具解壓縮的功能
//gz變量接收解壓所過後所得到的文字訊息
gz,err := gzip.NewReader(fi)
if err != nil {
fmt.Fprintf(os.Stderr,"ungzip Failed: %s",err)
return
}

//將gz變量讀取到的文字讀入緩存區中,並打印出來
r := bufio.NewReader(gz)
for {
line,err := r.ReadString('\n')
if err != nil && err == io.EOF {
fmt.Fprintf(os.Stdout,"File Read Finish")
return
}
fmt.Print(line)
}
}

func main(){
testReadgzip("Testlog.log.gz")
}

result

1
2
Test Hello World
File Read Finish

拷貝文件

拷貝文件使用io.Copy(dst Writer, src Reader)(written int64, err error)方法

  • dst為原文件,實現了Writer接口
  • src為拷貝文件位置,實現了Reader接口
  • 翻閱Go文件源碼便可以知道,其實他是不斷的循環讀取src文件,並寫入到dst文件中
    https://golang.org/src/io/io.go line:359
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func TestCopy(dst_file,src_file string)(written int64,err error){
src,err := os.Open(src_file)
if err != nil {
fmt.Fprintf(os.Stderr,"dst File open Failed:",err)
return
}
defer src.Close()

dst,err := os.OpenFile(dst_file,os.O_CREATE|os.O_WRONLY,0666)
if err != nil{
fmt.Fprintf(os.Stderr,"src File open Failed:",err)
return
}
defer dst.Close()
return io.Copy(dst,src)
}

func main(){
_ ,err := TestCopy("new_dst_file.go","test2.go")
if err != nil {
fmt.Fprintf(os.Stderr,"Copy Failed:%s",err)
return
}
fmt.Println("Copy sucess")
}

result

1
Copy sucess