golang中處理文件有很多種方式,下面我們來看看。
(1)使用os模塊
先來看看如何查看文件屬性
package main
import (
"fmt"
"os"
)
func main() {
//打開文件使用os.Open函數,會返回一個文件句柄和一個error
file, err := os.Open(`D:\komeijisatori\src\day3\whiteblum.txt`)
if err != nil {
fmt.Println("文件打開失敗:",err)
}
//調用file.Stat()可以查看文件的信息,這是一個os.FileInfo對象
info, err := file.Stat()
//屬性如下
/*
type FileInfo interface {
Name() string // 文件名
Size() int64 // 文件的大小,按字節計算
Mode() FileMode // 文件的模式
ModTime() time.Time // 修改時間
IsDir() bool // 是否是目錄
Sys() interface{} // 數據源,一般不用管
}
*/
fmt.Println(info.Name()) // whiteblum.txt
fmt.Println(info.Size()) // 183
//有點類似於linux里面的,第一個-表示文本文件,后面的三個rw-表示可讀可寫不可執行。
//分別是用戶、用戶所屬組、其他組的權限
fmt.Println(info.Mode()) // -rw-rw-rw-
fmt.Println(info.ModTime()) // 2019-08-31 19:52:44.3146692 +0800 CST
fmt.Println(info.IsDir()) // false
fmt.Println(info.Sys()) // &{32 {2579124676 30760946} {2579124676 30760946} {2579124676 30760946} 0 183}
}
讀取文件里面的內容
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open(`D:\komeijisatori\src\day3\whiteblum.txt`)
if err != nil {
fmt.Println("文件打開失敗:",err)
}
//忘記說了,文件打開是要關閉的,調用defer函數即可
defer file.Close()
//此外file.Name()還可以拿到文件的所在路徑,file.Fd()則是拿到文件的描述符
fmt.Println(file.Name()) //D:\komeijisatori\src\day3\whiteblum.txt
fmt.Println(file.Fd()) // 216
//讀取文件內容可以調用file.Read()方法,接收一個字節數組,返回一個int和error
buf := make([]byte, 12)
//此時文件的內容都會讀到buf里面,n則是寫入了多少個字節,n不會超過字節數組buf的長度
n, err := file.Read(buf)
//將寫入的內容讀取出來
fmt.Println(string(buf[:n])) //白色相簿
}
我們注意到:當前只是讀取了12個字節,並沒有全部讀取完。這是因為我們的buf長度只有12,我們可以將容量變得更大一些,但是到底要弄多大呢?這是一個未知數。弄小了一次讀不完,要是弄大了,會浪費。因此最好的辦法,不要一次就讀完,而是循環讀取,這樣不就好了嗎?一般的話,我們都將buf的長度設置為1024個字節,但由於我的文件比較小,所以就設置為12個字節
package main
import (
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open(`D:\komeijisatori\src\day3\whiteblum.txt`)
if err != nil {
fmt.Println("文件打開失敗:",err)
}
defer file.Close()
buf := make([]byte, 12) // 存放文件內容的緩存,相當於中轉站
data := make([]byte, 0) // 用來存放文件內容,buf讀取的內容都會寫到data里面去
for {
//無限循環,不斷讀取
n, err := file.Read(buf)
// 什么時候文件讀完呢?如果文件讀完的話,那么err不為nil,而是io.EOF
// 所以我們可以進行判斷
if err != nil {
//如果err != nil說明出錯了,但如果還等於io.EOF的話,說明讀完了,因為文件讀完,err也不為nil。直接break
if err == io.EOF{
break
} else {
//如果錯誤不是io.EOF的話,說明就真的在讀取中出現了錯誤,直接panic出來
panic(err)
}
}
//此時文件內容寫到buf里面去了,寫了多少個呢?寫了n個,那么我們再寫到data里面去
data = append(data, buf[: n]...)
//我們來打印一下,每次寫了多少個字節
fmt.Printf("寫入%d個字節\n", n)
/*
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入12個字節
寫入6個字節
*/
}
//寫完之后,我們來打印一下
fmt.Println(string(data))
/*
白色相簿什么的,已經無所謂了。
因為已經不再有歌,值得去唱了。
傳達不了的戀情,已經不需要了。
因為已經不再有人,值得去愛了。
*/
}
那么如何使用os模塊來寫入文件呢?
package main
import (
"fmt"
"os"
)
func main() {
//既然要寫,那么就不能使用Open了,而是使用Create
//Create會創建一個文件,如果存在會清空原有的內容
file, err := os.Create(`D:\komeijisatori\src\day3\whiteblum.txt`)
if err != nil {
fmt.Println("文件創建失敗:",err)
}
defer file.Close()
//寫入文件可以調用file.WriteString,或者file.Write方法
//前者是寫入字符,后者是寫入字節。
// 其實WriteString底層是將我們傳入的字符串變成字節數組,然后還是調用Write方法
//會有兩個返回值,一個是我們寫入的個數,一個是error
n, err := file.WriteString("這是新寫入的內容,原來的內容會丟失")
//golang是按照字節算的,一個漢字占三個字節
fmt.Println(n) // 51
//再來讀取一下看看,有沒有寫入
file, err = os.Open(`D:\komeijisatori\src\day3\whiteblum.txt`)
//直接將數組設置的大一些,一次讀取完畢
new_buf := make([]byte, 1024)
n, _ = file.Read(new_buf)
fmt.Println(string(new_buf[: n])) // 這是新寫入的內容,原來的內容會丟失
}
其實無論是Open還是Create,底層都是調用了OpenFile
package main
import (
"fmt"
"os"
)
func main() {
// OpenFile接收三個參數
// 1.文件名
// 2.文件的模式,支持如下
/*
os.O_RDONLY: 以只讀的方式打開
os.O_WRONLY: 以只寫的方式打開
os.O_RDWR : 以讀寫的方式打開
os.O_NONBLOCK: 打開時不阻塞
os.O_APPEND: 以追加的方式打開
os.O_CREAT: 創建並打開一個新文件
os.O_TRUNC: 打開一個文件並截斷它的長度為零(必須有寫權限)
os.O_EXCL: 如果指定的文件存在,返回錯誤
os.O_SHLOCK: 自動獲取共享鎖
os.O_EXLOCK: 自動獲取獨立鎖
os.O_DIRECT: 消除或減少緩存效果
os.O_FSYNC : 同步寫入
os.O_NOFOLLOW: 不追蹤軟鏈接
*/
// 3.權限,一般設置為0666,這在linux下有用,Windows下面沒太大卵用
// 以只寫的方式打開,並且寫入的時候,是以追加的形式寫入
file, _ := os.OpenFile(`D:\komeijisatori\src\day3\whiteblum.txt`,os.O_WRONLY|os.O_APPEND, 0666)
_, _ = file.Write([]byte("\n這是新的內容,但是原來的內容還在"))
//寫入之后,我們再以只讀方式打開
file, _ = os.OpenFile(`D:\komeijisatori\src\day3\whiteblum.txt`,os.O_RDONLY, 0666)
buf := make([]byte, 1024)
n, _ := file.Read(buf)
fmt.Println(string(buf[: n]))
/*
這是新寫入的內容,原來的內容會丟失
這是新的內容,但是原來的內容還在
*/
}
但是通常我們直接使用Open和Create函數
(2)使用bufio
bufio相當於是在os.OpenFile得到的文件句柄之上進行一層封裝,bufio,從名字上也能看出來是帶緩存的io。
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
file, _ := os.Open(`D:\komeijisatori\src\day3\whiteblum.txt`)
//使用bufio.NewReader進行一層包裝
reader := bufio.NewReader(file)
//首先這個reader,可以像file一樣,使用Read方法
//然而reader還可是按照指定字符來讀,比如我想一行一行讀,就可以指定換行符來讀
for{
//返回string和error
s, err := reader.ReadString('\n') //表示每讀到\n就停止
//這里使用print,因為文件本身有換行符,println自帶換行,所以使用print
fmt.Print("讀取一行:", s)
/*
讀取一行:白色相簿什么的,已經無所謂了。
讀取一行:因為已經不再有歌,值得去唱了。
讀取一行:傳達不了的戀情,已經不需要了。
讀取一行:因為已經不再有人,值得去愛了。
*/
if err != nil {
if err == io.EOF{
break
} else {
panic(err)
}
}
}
}
package main
import (
"bufio"
"bytes"
"fmt"
"os"
)
func main() {
file, _ := os.Open(`D:\komeijisatori\src\day3\whiteblum.txt`)
//除了NewReader,還有一個NewScanner
scanner := bufio.NewScanner(file)
for scanner.Scan(){
//可以看到我們使用println打印,行與行之間沒有空隙,說明scanner.Text()沒有把換行符讀進去。
fmt.Println(scanner.Text())
/*
白色相簿什么的,已經無所謂了。
因為已經不再有歌,值得去唱了。
傳達不了的戀情,已經不需要了。
因為已經不再有人,值得去愛了。
*/
}
//但是這都是一行一行讀的,我們可以寫到緩存里面去
//這個緩存使用bytes.Buffer{}來創建
buf := bytes.Buffer{}
file, _ = os.Open(`D:\komeijisatori\src\day3\whiteblum.txt`)
scanner = bufio.NewScanner(file)
for scanner.Scan(){
//直接調用buf.WriteString即可
buf.WriteString(scanner.Text())
// 當然還可以使用buf.Write寫入字節,同時也可以使用scanner.Bytes()得到字節。buf.Write(scanner.Bytes())
}
//可以調用buf.Bytes()拿到字節
//也可以調用buf.String()拿到String
fmt.Println(buf.String()) // 白色相簿什么的,已經無所謂了。因為已經不再有歌,值得去唱了。傳達不了的戀情,已經不需要了。因為已經不再有人,值得去愛了。
//因為沒有讀取換行符,所以是連在一起的
//這里再提一下bytes.Buffer{},這是一個非常方便的字節緩存。用來組合字符串非常方便
buffer := bytes.Buffer{}
buffer.WriteString("哈哈")
buffer.WriteString("呱呱")
buffer.Write([]byte("嘻嘻"))
fmt.Println(buffer.String()) // 哈哈呱呱嘻嘻
fmt.Println(buffer.Bytes()) // [229 147 136 229 147 136 229 145 177 229 145 177 229 152 187 229 152 187]
//當然buffer也有ReadString,和Read方法
s, _ := buffer.ReadString('\n')
fmt.Println(s) // 哈哈呱呱嘻嘻
buffer.WriteString("你胸大,你先說")
b := make([]byte, 1024)
n, _ := buffer.Read(b)
fmt.Println(string(b[: n])) // 你胸大,你先說
}
(3)使用ioutil模塊
ioutil是一個非常強大模塊
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
file, _ := os.Open(`D:\komeijisatori\src\day3\whiteblum.txt`)
// 直接將file丟進去,可以讀取全部內容
s, _ := ioutil.ReadAll(file)
fmt.Println(string(s))
/*
白色相簿什么的,已經無所謂了。
因為已經不再有歌,值得去唱了。
傳達不了的戀情,已經不需要了。
因為已經不再有人,值得去愛了。
*/
// 是不是很方便呢?其實還有更方便的
//調用ReadFile,傳入文件名直接得到字節數組和error。當然底層是使用了os.Open和ioutil.ReadAll
s, _ = ioutil.ReadFile(`D:\komeijisatori\src\day3\whiteblum.txt`)
fmt.Println(string(s))
/*
白色相簿什么的,已經無所謂了。
因為已經不再有歌,值得去唱了。
傳達不了的戀情,已經不需要了。
因為已經不再有人,值得去愛了。
*/
}
當然ioutil開可以寫入文件
package main
import (
"bytes"
"fmt"
"io/ioutil"
)
func main() {
buf := bytes.Buffer{}
buf.WriteString("明明是我先來的\n")
buf.WriteString("為什么會變成這樣呢\n")
buf.WriteString("何でそんなに慣れてんだよ\n")
buf.WriteString("雪菜と何度もキースしたんだよ")
//傳入filename,content,權限
_ = ioutil.WriteFile(`D:\komeijisatori\src\day3\whiteblum.txt`, buf.Bytes(), 0666)
//讀取出來看看
s, _ := ioutil.ReadFile(`D:\komeijisatori\src\day3\whiteblum.txt`)
fmt.Println(string(s))
/*
明明是我先來的
為什么會變成這樣呢
何でそんなに慣れてんだよ
雪菜と何度もキースしたんだよ
*/
}
之前說ioutil很強大,是因為還有別的用處,比如讀取目錄
package main
import (
"fmt"
"io/ioutil"
)
func main() {
files, _ := ioutil.ReadDir(`C:\python37\Lib\site-packages\tornado`)
//返回一個[]os.FileInfo和error
for _, file := range files{
if !file.IsDir(){
fmt.Println(file.Name())
/*
__init__.py
_locale_data.py
auth.py
autoreload.py
concurrent.py
curl_httpclient.py
escape.py
gen.py
http1connection.py
httpclient.py
httpserver.py
httputil.py
ioloop.py
iostream.py
locale.py
locks.py
log.py
netutil.py
options.py
process.py
py.typed
queues.py
routing.py
simple_httpclient.py
speedups.cp37-win_amd64.pyd
tcpclient.py
tcpserver.py
template.py
testing.py
util.py
web.py
websocket.py
wsgi.py
*/
}
}
}
