终端读写
操作终端相关文件句柄常量
- os.Stdin(standard):标准输入
- os.Stdout:标准输出
- os.Stderr:标准错误输出
标准输出
demo:直接输出和 判断之后输出的结果不一致
func main(){ //标准输出接口, 格式, 需要格式化的内容 fmt.Fprintf(os.Stdout, "%s ", "hello world ~") fmt.Fprintln(os.Stdout, "hello, world ~") } 运行结果: hello world ~ hello, world ~
标准输入
package main import ( "fmt" ) var ( firstName, lastName string age, salary int ) func main(){ //从键盘获取string类型赋值给fistName fmt.Scanf("%s", &firstName) //将字符串赋值给lastName, 中间不能空格 fmt.Sscan("helloworld", &lastName) //将一个字符串按照指定格式赋值给变量 fmt.Sscanf("18-3000", "%d-%d", &age, &salary) fmt.Printf("%s %s %d %d", firstName, lastName, age, salary) } 运行结果: "holle go" //手动输入,有空格不行 "holle helloworld 18 3000
带缓存的输出
package main import ( "bufio" "fmt" "os" ) func main(){ //先得到*Write缓存 buf := bufio.NewWriter(os.Stdout) //标准输出接口, 格式, 需要格式化的内容 fmt.Fprintf(buf, "%s ", "hello world ~") fmt.Fprintln(buf, "hello, world ~") buf.Flush() } 运行结果: hello world ~ hello, world ~
带缓存的输入
package main import ( "bufio" "fmt" "os" ) func main(){ buf := bufio.NewReader(os.Stdin) input, err := buf.ReadString(' ') //delim 定界符 if err == nil { fmt.Println(input) } } 运行结果: hello world //手动输入 hello world
读文件
Go 中读取一个文件有多种方式,下面介绍主要的几种:
使用 ioutil 包直接读取
先需要引入 io/ioutil 包,该包默认拥有以下函数供使用者调用
func NopCloser(r io.Reader) io.ReadCloser func ReadAll(r io.Reader) ([]byte, error) func ReadDir(dirname string) ([]os.FileInfo, error) func ReadFile(filename string) ([]byte, error) func TempDir(dir, prefix string) (name string, err error) func TempFile(dir, prefix string) (f *os.File, err error) func WriteFile(filename string, data []byte, perm os.FileMode) error
1)通过指定路径,读取一个文件,并返回 []byte 数据和错误信息
func ReadFile(filename string) ([]byte, error)
demo:通过文件路径读取一个文件,输出文件中的内容
package main import ( "fmt" "io/ioutil" ) func readFileByName(fileName string){ contentSlice, err := ioutil.ReadFile(fileName) if err == nil { fmt.Print(string(contentSlice)) } } func main(){ var fileName string = "D:\code\go_dev\projects\src\part_10_终端输入输出 && 文件操作\01_使用os.osutil读取文件\test.txt" readFileByName(fileName) } 运行结果: my name is johny i'm 12 years old i like speaking english i like to eat cake
2)通过指定路径,读取一个目录,并返回目录下的文件类型列表和错误信息
func ReadDir(dirname string) ([]os.FileInfo, error)
demo:打开一个目录,输出目录下文件的名称
package main import ( "fmt" "io/ioutil" ) func readDiractory(dirName string) { fileInfoSlice, err := ioutil.ReadDir(dirName) if err != nil { fmt.Println(err) return } for _, fileInfo := range fileInfoSlice { fileName := fileInfo.Name() fmt.Println(fileName) } } func main() { var dirName string = "D:\code\go_dev\file_test" readDiractory(dirName) } 运行结果: test1.txt test2.txt test3.txt
使用 os.Open() 和 os.OpenFile() 函数读取
不论是 os.Open() 还是 os.OpenFile,都返回一个文件类型指针(*File),File 文件类型是一个结构体,封装了关于文件相关操作的方法
关闭一个文件:
File.Close()
1)os.Open()
打开一个将被读取的文件,如果读取成功,返回文件类型,默认的权限是 O_RDONLY,也就是只读权限,否则返回错误
func os.Open(name string) (*File, error)
2)os.OpenFile()
大部分用户会选择这个函数来代替 os.Open() 函数
主要原因是这个函数打开文件时可以指定参数(os.O_APPEND | os.O_CREATE | os.O_WRONLY),以及文件权限(0666)
func os.OpenFile(name string, flag int, perm FileMode) (*File, error)
flag 相关参数:
const ( O_RDONLY int = syscall.O_RDONLY // 只读打开文件和os.Open()同义 O_WRONLY int = syscall.O_WRONLY // 只写打开文件 O_RDWR int = syscall.O_RDWR // 读写方式打开文件 O_APPEND int = syscall.O_APPEND // 当写的时候使用追加模式到文件末尾 O_CREATE int = syscall.O_CREAT // 如果文件不存在,此案创建 O_EXCL int = syscall.O_EXCL // 和O_CREATE一起使用, 只有当文件不存在时才创建 O_SYNC int = syscall.O_SYNC // 以同步I/O方式打开文件,直接写入硬盘. O_TRUNC int = syscall.O_TRUNC // 如果可以的话,当打开文件时先清空文件 )
3)使用 os.Open() 函数和 ioutil.ReadAll() 函数来读取文件
由于 os.Open 是打开一个文件并返回文件指针类型(*File),因此其实可以结合 ioutil 包中的 ReadAll(r io.Reader) ([]byte, error) 函数来进行读取的(这里还有点疑问)
io.Reader 其实是一个包含 Read() 方法的接口类型,而文件类型(File)是实现了 Read() 方法的
ioutil.ReadAll() 函数从一个文件类型( io.Reader )中读取内容直到返回错误或者结束(EOF)时返回读取的数据,当 err == nil 时,数据成功读取到 []byte 中
func ReadAll(r io.Reader) ([]byte, error)
demo:打开一个绝对路径下的文件
package main import ( "fmt" "io" "io/ioutil" "os" ) func readByFielType(fileType io.Reader){ contentSlice, err := ioutil.ReadAll(fileType) if err == nil { fmt.Print(string(contentSlice)) } } func main(){ fileType, err := os.Open("D:\code\go_dev\projects\src\part_10_终端输入输出 && 文件操作\01_使用os.osutil读取文件\test.txt") if err != nil { fmt.Printf("file open failed, detail is %v", err.Error()) } else { readByFielType(fileType) } defer fileType.Close() } 运行结果: my name is johny i'm 12 years old i like speaking english i like to eat cake
这种方式会比较繁琐一些,因为使用了 os 的同时借助了 ioutil,但是在读取大文件的时候还是比较有优势的
带缓存的读取(bufio)
bufio 包实现了缓存 IO,创建了 Reader 和 Writer 类型结构体,这种带有缓存的方式,对于文本 I/O 来说,提供了一些便利
bufio 包中对 Reader 类型结构体有很多相关的函数及方法:
//首先定义了一个用来缓冲io.Reader对象的结构体,同时该结构体拥有以下相关的方法 type Reader struct { } //NewReader函数用来返回一个默认大小buffer的Reader对象(默认大小好像是4096) 等同于NewReaderSize(rd,4096) func NewReader(rd io.Reader) *Reader //该函数返回一个指定大小buffer(size最小为16)的Reader对象,如果 io.Reader参数已经是一个足够大的Reader,它将返回该Reader func NewReaderSize(rd io.Reader, size int) *Reader //该方法返回从当前buffer中能被读到的字节数 func (b *Reader) Buffered() int //Discard方法跳过后续的 n 个字节的数据,返回跳过的字节数。如果0 <= n <= b.Buffered(),该方法将不会从io.Reader中成功读取数据。 func (b *Reader) Discard(n int) (discarded int, err error) //Peekf方法返回缓存的一个切片,该切片只包含缓存中的前n个字节的数据 func (b *Reader) Peek(n int) ([]byte, error) //把Reader缓存对象中的数据读入到[]byte类型的p中,并返回读取的字节数。读取成功,err将返回空值 func (b *Reader) Read(p []byte) (n int, err error) //返回单个字节,如果没有数据返回err func (b *Reader) ReadByte() (byte, error) //该方法在b中读取delimz之前的所有数据,返回的切片是已读出的数据的引用,切片中的数据在下一次的读取操作之前是有效的。如果未找到delim,将返回查找结果并返回nil空值。因为缓存的数据可能被下一次的读写操作修改,因此一般使用ReadBytes或者ReadString,他们返回的都是数据拷贝 func (b *Reader) ReadSlice(delim byte) (line []byte, err error) //功能同ReadSlice,返回数据的拷贝 func (b *Reader) ReadBytes(delim byte) ([]byte, error) //功能同ReadBytes,返回字符串 func (b *Reader) ReadString(delim byte) (string, error) //该方法是一个低水平的读取方式,一般建议使用ReadBytes(' ') 或 ReadString(' '),或者使用一个 Scanner来代替。ReadLine 通过调用 ReadSlice 方法实现,返回的也是缓存的切片,用于读取一行数据,不包括行尾标记( 或 ) func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) //读取单个UTF-8字符并返回一个rune和字节大小 func (b *Reader) ReadRune() (r rune, size int, err error)
demo:使用 NewReader() 函数返回一个 *Reader 类型,然后使用 ReadString() 方法读取文件内容,使用 for 循环,一直读到文件结尾
package main import ( "bufio" "fmt" "io" "os" ) func bufioRead(){ fileType, err := os.Open("D:\golang_workspace\file_test\test.txt") if err != nil { fmt.Printf("file open failed, detail is %v", err.Error()) os.Exit(2) } defer fileType.Close() var bufReader *bufio.Reader = bufio.NewReader(fileType) for { contents, err := bufReader.ReadString(' ') if err != io.EOF { fmt.Print(contents) } else { break } } } func main(){ bufioRead() } 运行结果: 想带你逃学看一场演唱会 想你难过时在我怀中流泪 想和你背包走遍天南地北 想你共我尝尽人生的滋味 想下雨时小伞中有你依偎 想把酒言欢时也有你作陪
写文件
上面的读操作的方式同样也支持写操作
使用 ioutil 包进行文件写入
写入 []byte 类型的数据到 filename 文件中,文件的权限为 perm
func WriteFile(filename string, data []byte, perm os.FileMode) error
demo:使用 WriteFile() 函数写入内容到文件
package main import ( "fmt" "io/ioutil" ) func ioutilWrite(){ var contents string = "hello world ~" var data []byte = []byte(contents) err := ioutil.WriteFile("D:\golang_workspace\file_test\test.txt", data, 0644) if err != nil { fmt.Printf("open file failed, detail is %v", err) } else { fmt.Println("write success ~") } } func main(){ ioutilWrite() } 运行结果: write success ~ //覆盖写入
使用 os.OpenFile() 函数进行文件写入
因为 os.OpenFile() 函数会打开文件,并返回文件类型结构体指针,而该文件类型是一个结构体,拥有一些写入相关的方法
func os.OpenFile(name string, flag int, perm FileMode) (*File, error)
flag 相关参数及含义:
const ( O_RDONLY int = syscall.O_RDONLY // 只读打开文件和os.Open()同义 O_WRONLY int = syscall.O_WRONLY // 只写打开文件 O_RDWR int = syscall.O_RDWR // 读写方式打开文件 O_APPEND int = syscall.O_APPEND // 当写的时候使用追加模式到文件末尾 O_CREATE int = syscall.O_CREAT // 如果文件不存在,此案创建 O_EXCL int = syscall.O_EXCL // 和O_CREATE一起使用, 只有当文件不存在时才创建 O_SYNC int = syscall.O_SYNC // 以同步I/O方式打开文件,直接写入硬盘. O_TRUNC int = syscall.O_TRUNC // 如果可以的话,当打开文件时先清空文件 )
文件类型结构体以及相关写入文件的方法:
1)写入长度为b字节切片到文件f中,返回写入字节号和错误信息。当n不等于len(b)时,将返回非空的err
func (f *File) Write(b []byte) (n int, err error)
2)在off偏移量出向文件f写入长度为b的字节
func (f *File) WriteAt(b []byte, off int64) (n int, err error)
3)类似于Write方法,但是写入内容是字符串而不是字节切片
func (f *File) WriteString(s string) (n int, err error)
demo:使用 os.OpenFile() 函数打开文件,使用 Write() 和 WriteString() 方法将内容写入到文件
package main import ( "fmt" "os" ) func osOpenWrite(){ fileType, errOpen := os.OpenFile("D:\golang_workspace\file_test\test.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if errOpen != nil { fmt.Printf("openfile failed, detail is %v", errOpen.Error()) return } defer fileType.Close() //写入字符串 _, errWrite1 := fileType.WriteString("hello world") if errWrite1 != nil { fmt.Printf("write file error, detail is %v", errWrite1.Error()) } else { fmt.Println("write string success ~") } //写入字符切片 _, errWrite2 := fileType.Write([]byte("hello world")) if errWrite2 != nil { fmt.Printf("write file error, detail is %v", errWrite2.Error()) } else { fmt.Println("write []byte success ~") } } func main(){ osOpenWrite() } 运行结果: write string success ~ write []byte success ~
带缓存的写入(bufio)
bufio 和 io 包中很多操作都是相似的,唯一不同的是 bufio 提供了一些缓冲的操作,如果对文件操作比较频繁的,使用 bufio 还是能提高程序的性能和效率的
在 bufio 中,有一个 Writer 类型结构体,与其相关的方法支持一系列的写入操作:
//Writer是一个空的结构体,一般需要使用NewWriter或者NewWriterSize来初始化一个结构体对象 type Writer struct { // contains filtered or unexported fields } //NewWriterSize和NewWriter函数 //返回默认缓冲大小的Writer对象(默认是4096) func NewWriter(w io.Writer) *Writer //指定缓冲大小创建一个Writer对象 func NewWriterSize(w io.Writer, size int) *Writer //Writer对象相关的写入数据的方法 //把p中的内容写入buffer,返回写入的字节数和错误信息。如果nn<len(p),返回错误信息中会包含为什么写入的数据比较短 func (b *Writer) Write(p []byte) (nn int, err error) //将buffer中的数据写入 io.Writer func (b *Writer) Flush() error //以下三个方法可以直接写入到文件中 //写入单个字节 func (b *Writer) WriteByte(c byte) error //写入单个Unicode指针返回写入字节数错误信息 func (b *Writer) WriteRune(r rune) (size int, err error) //写入字符串并返回写入字节数和错误信息 func (b *Writer) WriteString(s string) (int, error)
demo:使用 NewWriter() 函数得到一个 *Writer(指针类型结构体),然后调用 Write() 方法将内容写入到文件,最后调用 Flush() 方法把数据刷新到磁盘
package main import ( "bufio" "fmt" "os" ) func bufioWrite(){ fileType, errOpen := os.OpenFile("D:\golang_workspace\file_test\test.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if errOpen != nil { fmt.Printf("openfile failed, detail is %v", errOpen.Error()) return } defer fileType.Close() //生成 *Writer 指针结构体 var writer *bufio.Writer = bufio.NewWriter(fileType) _, errWrite := writer.Write([]byte("hello world ~")) if errWrite != nil { fmt.Printf("file write error, detail is %v", errWrite.Error()) } //刷新数据到磁盘 errFlush := writer.Flush() if errFlush != nil { fmt.Println("file flush error") panic(errFlush) } else { fmt.Println("write file success") } } func main(){ bufioWrite() } 运行结果: write file success
文件 copy
demo:把一个文件的内容,拷贝到另一个文件中
package main
import (
"fmt"
"io"
"os"
)
func main(){
var srcFileName string = "D:\golang_workspace\file_test\source.txt"
var targetFileName string = "D:\golang_workspace\file_test\target.txt"
CopyFile(srcFileName, targetFileName)
}
func CopyFile(srcFileName, targetFileName string){
srcFile, err := os.Open(srcFileName)
if err != nil {
fmt.Printf("open srcfile failed, err:[%v]", err)
}
defer srcFile.Close()
targetFile, err := os.OpenFile(targetFileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
fmt.Printf("open targetfile failed, err:[%v]", err)
}
defer targetFile.Close()
written, err := io.Copy(targetFile, srcFile)
if err != nil {
fmt.Printf("file copy failed, err[%v]", err)
}
fmt.Printf("copy complete, writen[%v]", written)
}
运行结果:
copy complete, writen[14]
总结:
Go 语言中,文件操作读和写用到了三个包,分别是 ioutil os bufio:
- ioutil.ReadFile, ioutil.ReadDir, ioutil.ReadAll 和 ioutil.WriteFile
- os.Open 和 os.OpenFile
- bufio.Reader 和 bufio.Writer
其中也有穿插使用,比如:os.Open 和 ioutil.ReadAll,os.Open 和 bufio.Reader,os.OpenFile 和 bufio.Writer
ending ~