Go 终端读写 && 文件读写、copy

终端读写

操作终端相关文件句柄常量

  • 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:

  1. ioutil.ReadFile,   ioutil.ReadDir,  ioutil.ReadAll       和       ioutil.WriteFile
  2. os.Open      和      os.OpenFile
  3. bufio.Reader      和      bufio.Writer

 其中也有穿插使用,比如:os.Open 和 ioutil.ReadAll,os.Open 和 bufio.Reader,os.OpenFile 和 bufio.Writer

ending ~

每天都要遇到更好的自己.
原文地址:https://www.cnblogs.com/kaichenkai/p/11117574.html