GO语言中的IO操作
格式化输出
| 输出格式 | 输出内容 |
|---|---|
| %t | 单词 true 或 false |
| %b | 表示为二进制 |
| %d | 表示为十进制 |
| %e | (=%.6e)有 6 位小数部分的科学计数法,如 -1234.456e+78 |
| %f | (=%.6f)有 6 位小数部分,如 123.456123 |
| %g | 根据实际情况采用 %e 或 %f 格式(获得更简洁、准确的输出) |
| %s | 直接输出字符串或者字节数组 |
| %v | 值的默认格式表示 |
| %+v | 类似 %v,但输出结构体时会添加字段名 |
| %#v | 值的 Go 语法表示 |
| %Т | 值的类型的 Go 语法表示 |
标准输入
fmt.Println("please input two word")
var word1 string
var word2 string
fmt.Scan(&word1, &word2) //读入多个单词,空格分隔。如果输入了更多单词会被缓存起来,丢给下一次scan
fmt.Println("please input an int")
var i int
fmt.Scanf("%d", &i) //类似于Scan,转为特定格式的数据 会返回数据和error
例子
package main
import (
"fmt"
"io"
"os"
"time"
)
func Ma() {
go func() {
for {
a := ""
n, err := fmt.Scanln(&a)
if err != nil {
if err != io.EOF {
fmt.Println(err, "====")
}
}
if n == 0 {
continue
}
fmt.Println("你输入了", a)
if a == "abc" {
os.Exit(111)
}
}
}()
time.Sleep(1 * time.Hour)
}
打开文件
func os.Open(name string) (*os.File, error)
fout, err := os.OpenFile("data/verse.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
-
os.O_WRONLY :以只写的方式打开文件
-
os.O_RDWR :读写方式打开
-
os.O_RDONLY :只读
-
os.O_TRUNC :把文件之前的内容先清空掉,需要配合
os.O_WRONLY -
os.O_CREATE :如果文件不存在则先创建
-
os.O_APPEND :追加方式写
-
os.O_SYNC :以同步I/O的方式打开
-
0666 :新建文件的权限设置。 注意:这里一定是四位数,3位数在linux中会出现权限不符合预期的情况
读文件
方法
cont := make([]byte, 10)
fin.Read(cont) //读出len(cont)个字节,返回成功读取的字节数
fin.ReadAt(cont, int64(n)) //从指定的位置开始读len(cont)个字节
fin.Seek(int64(n), 0) //重新定位。whence: 0从文件开头计算偏移量,1从当前位置计算偏移量,2到文件末尾的偏移量

1、整个文件读取入内存
直接将数据直接读取入内存,是效率最高的一种方式,但此种方式,仅适用于小文件,对于大文件,则不适合,因为比较浪费内存。
1.1 直接指定文件名读取
有两种方法
第一种:使用 os.ReadFile
package main
import (
"fmt"
"os"
)
func main() {
content, err := os.ReadFile("a.txt")
if err != nil {
panic(err)
}
fmt.Println(string(content))
}
第二种:使用 ioutil.ReadFile
package main
import (
"io/ioutil"
"fmt"
)
func main() {
content, err := ioutil.ReadFile("a.txt")
if err != nil {
panic(err)
}
fmt.Println(string(content))
}
其实在 Go 1.16 开始,ioutil.ReadFile 就等价于 os.ReadFile,二者是完全一致的
// ReadFile reads the file named by filename and returns the contents.
// A successful call returns err == nil, not err == EOF. Because ReadFile
// reads the whole file, it does not treat an EOF from Read as an error
// to be reported.
//
// As of Go 1.16, this function simply calls os.ReadFile.
func ReadFile(filename string) ([]byte, error) {
return os.ReadFile(filename)
}
1.2 先创建句柄再读取
如果仅是读取,可以使用高级函数 os.Open
package main
import (
"os"
"io/ioutil"
"fmt"
)
func main() {
file, err := os.Open("a.txt")
if err != nil {
panic(err)
}
defer file.Close()
content, err := ioutil.ReadAll(file)
fmt.Println(string(content))
}
之所以说它是高级函数,是因为它是只读模式的 os.OpenFile
// Open opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}
因此,你也可以直接使用 os.OpenFile,只是要多加两个参数
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
file, err := os.OpenFile("a.txt", os.O_RDONLY, 0)
if err != nil {
panic(err)
}
defer file.Close()
content, err := ioutil.ReadAll(file)
fmt.Println(string(content))
}
2、每次只读取一行
一次性读取所有的数据,太耗费内存,因此可以指定每次只读取一行数据。
方法有三种:
bufio.ReadLine()
bufio.ReadBytes('\n')
bufio.ReadString('\n')
在 bufio 的源码注释中,曾说道 bufio.ReadLine() 是低级库,不太适合普通用户使用,更推荐用户使用 bufio.ReadBytes 和 bufio.ReadString 去读取单行数据。
因此,这里不再介绍 bufio.ReadLine()
2.1 使用 bufio.ReadBytes
package main
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
func main() {
// 创建句柄
fi, err := os.Open("christmas_apple.py")
if err != nil {
panic(err)
}
defer fi.Close()
// 创建 Reader
r := bufio.NewReader(fi)
for {
lineBytes, err := r.ReadBytes('\n')
line := strings.TrimSpace(string(lineBytes))//去掉两边的空格
if err != nil && err != io.EOF {
panic(err)
}
if err == io.EOF {
break
}
fmt.Println(line)
}
}
2.2 使用 bufio.ReadString
package main
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
func main() {
// 创建句柄
fi, err := os.Open("a.txt")
if err != nil {
panic(err)
}
defer fi.Close()
// 创建 Reader
r := bufio.NewReader(fi)//读文件文件建议用bufio.Reader
for { //无限循环
line, err := r.ReadString('\n')//指定分隔符
line = strings.TrimSpace(line) //去除两边的空格
if err != nil && err != io.EOF {
panic(err)
}
if err == io.EOF {
if line>0{
fmt.Println(line)
}
break
}
fmt.Println(line)
}
}
3、每次只读取固定字节数
每次仅读取一行数据,可以解决内存占用过大的问题,但要注意的是,并不是所有的文件都有换行符 \n。
因此对于一些不换行的大文件来说,还得再想想其他办法。
3.1 使用 os 库
通用的做法是:
先创建一个文件句柄,可以使用 os.Open 或者 os.OpenFile
然后 bufio.NewReader 创建一个 Reader
然后在 for 循环里调用 Reader 的 Read 函数,每次仅读取固定字节数量的数据。
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// 创建句柄
fi, err := os.Open("a.txt")
if err != nil {
panic(err)
}
defer fi.Close()
// 创建 Reader
r := bufio.NewReader(fi)
// 每次读取 1024 个字节
buf := make([]byte, 1024)
for {
n, err := r.Read(buf)//用创建的切片来接收读取的数据 返回的n是成功读取的字节数
if err != nil && err != io.EOF {
panic(err)
}
//最后一行就会把EOF读到,所以不能使用EOF返回
//if err == io.EOF {
// break
//}
if n == 0 {
break
}
fmt.Println(string(buf[:n]))
}
}
3.2 使用 syscall 库(不推荐)
os 库本质上也是调用 syscall 库,但由于 syscall 过于底层,如非特殊需要,一般不会使用 syscall
本篇为了内容的完整度,这里也使用 syscall 来举个例子。
本例中,会每次读取 100 字节的数据,并发送到通道中,由另外一个协程进行读取并打印出来。
package main
import (
"fmt"
"sync"
"syscall"
)
func main() {
fd, err := syscall.Open("christmas_apple.py", syscall.O_RDONLY, 0)
if err != nil {
fmt.Println("Failed on open: ", err)
}
defer syscall.Close(fd)
var wg sync.WaitGroup
wg.Add(2)
dataChan := make(chan []byte)
go func() {
wg.Done()
for {
data := make([]byte, 100)
n, _ := syscall.Read(fd, data)
if n == 0 {
break
}
dataChan <- data
}
close(dataChan)
}()
go func() {
defer wg.Done()
for {
select {
case data, ok := <-dataChan:
if !ok {
return
}
fmt.Printf(string(data))
default:
}
}
}()
wg.Wait()
}
写文件
defer fout.Close() //别忘了关闭文件句柄
writer := bufio.NewWriter(fout)
writer.WriteString("明月多情应笑我")
writer.WriteString("\n") //需要手动写入换行符
writer.flush() //强行将缓存写入文件
例子
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.OpenFile("1", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755) //以追加,如果没有就创建和只写的模式打开文件 并设置文件的权限为0755
fmt.Println(err)
if err != nil {
panic(err)
}
defer file.Close() //用defer设置关闭文件
r := bufio.NewWriter(file) //创建writer
nn, err := r.Write([]byte("用write写\n")) //将字符串转换成byte切片写入
fmt.Println(nn, err)
nn, err = r.WriteString("用WriteString写\n") //直接写入字符串
fmt.Println(nn, err)
r.Flush() //最后一定要刷新缓存,将内容写入磁盘
}
读写文件综合例子
package homework6
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
var new_txt = "bigtxt.txt"
var bigfile, err = os.OpenFile(new_txt, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0755)
func Init() {
if err != nil {
panic(errors.New("无法创建新文件"))
}
}
// 关闭
func exit() {
if bigfile != nil {
bigfile.Close()
}
}
// 整理文件
func bigtxt(path string) error {
infos, err := os.ReadDir(path)
if err != nil {
return err
}
writer := bufio.NewWriter(bigfile)
for _, d := range infos {
p := filepath.Join(path, d.Name())
if d.IsDir() {
bigtxt(p)
}
if strings.HasSuffix(p, ".txt") {
fmt.Println(p)
oldtxt, err := os.Open(p)
if err != nil {
panic(fmt.Errorf("文件%s打开失败", p))
}
defer oldtxt.Close()
buff := make([]byte, 1024)
reader := bufio.NewReader(oldtxt)
for {
n, err := reader.Read(buff)
if err != nil && err != io.EOF {
panic(err)
}
if n == 0 {
break
}
writer.Write(buff[:n])
}
}
}
writer.Flush()
return nil
}
创建文件/目录
os.Create(name string)//创建文件
os.Mkdir(name string, perm fs.FileMode)//创建目录
os.MkdirAll(path string, perm fs.FileMode)//增强版Mkdir,沿途的目录不存在时会一并创建
os.Rename(oldpath string, newpath string)//给文件或目录重命名,还可以实现move的功能
os.Remove(name string)//删除文件或目录,目录不为空时才能删除成功
os.RemoveAll(path string)//增强版Remove,所有子目录会递归删除
文件路径的操作
os.Getwd() (dir string, err error)获取当前执行命令的目录filepath.Join(elem ...string) string拼接路径filepath.Dir(path string) string获取文件的目录名filepath.Base(path string) string获取文件的文件名filepath.Abs(path string) (string, error)获取文件的绝对路径filepath.Split(path string) (dir, file string)切割文件路径 返回目录名和文件名filepath.Ext(path string) string获取文件的后缀os.Stat(name string) (FileInfo, error)获取文件的状态信息FileInfo.IsDir()是否为文件夹FileInfo.Name()文件名FileInfo.ModTime()文件的修改时间FileInfo.Mode()文件的权限FileInfo.Size()文件的字节数
package main
import (
"fmt"
"os"
"path/filepath"
)
func main() {
pwd, _ := os.Getwd() // 获取到当前目录,相当于python里的os.getcwd()
fmt.Println("当前的操作路径为:", pwd) //当前的操作路径为: E:\Codes\GO\week5
//文件路径拼接
f1 := filepath.Join(pwd, "test", "1.txt")
fmt.Println("文件的路径为:", f1) //文件的路径为: E:\Codes\GO\week5\test\1.txt
//文件的目录名
fmt.Println("文件的目录名:", filepath.Dir(f1)) //文件的目录名: E:\Codes\GO\week5\test
//文件的文件名
fmt.Println("文件的文件名:", filepath.Base(f1)) //文件的文件名: 1.txt
//文件的绝对路径
adspath, _ := filepath.Abs("evn/3.txt")
fmt.Println("文件的绝对路径为:", adspath) //文件的绝对路径为: E:\Codes\GO\week5\evn\3.txt
//拆分路径
dirname, filename := filepath.Split(f1)
fmt.Println("目录名为:", dirname, "文件名为", filename) //目录名为: E:\Codes\GO\week5\test\ 文件名为 1.txt
//扩展名相关
fmt.Println("f1的扩展名为:", filepath.Ext(f1)) //f1的扩展名为: .txt
//通过os.Stat()函数返回的文件状态,如果有错误则根据错误状态来判断文件或者文件夹是否存在
fileinfo, err := os.Stat(f1)
if err != nil {
fmt.Println(err.Error()) //如果文件不存在 CreateFile E:\Codes\GO\week5\test\1.txt: The system cannot find the path specified.
if os.IsNotExist(err) {
fmt.Println("file:", f1, " not exist!")
}
} else {
fmt.Println(fileinfo.IsDir()) //判断是否为文件夹 //false
fmt.Println(!fileinfo.IsDir()) //true
fmt.Println(fileinfo.Name()) //获取文件的名字 //1.txt
fmt.Println(fileinfo.ModTime()) //获取修改时间 返回的是个时间对象 //2022-09-30 16:30:59.0343772 +0800 CST
fmt.Println(fileinfo.Mode()) //获取权限 //-rw-rw-rw-
fmt.Println(fileinfo.Size()) //获取文件的字节长度 //61
}
}
遍历目录
ioutil.ReadDir(dirname string) ([]fs.FileInfo, error)获取文件信息的切片
从 Go 1.16 开始,现在由包 io 或包 os 提供相同的功能,并且在新代码中应该首选这些实现。
用法也是一样的
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
func walk(path string) error {
pathinfos, err := ioutil.ReadDir(path)
if err != nil { //获取目录中的文件信息
return fmt.Errorf("读取目录不成功")
}
for _, pathinfo := range pathinfos {
path1 := filepath.Join(path, pathinfo.Name()) //通过filepath.Join连接父目录和当前目录
if pathinfo.IsDir() { //如果是目录,就递归子遍历
if err := walk(path1); err != nil {
return err
}
} else {
fmt.Println(path1)
}
}
return nil
}
func main() {
//获取当前执行命令的目录
path, err := os.Getwd()
if err != nil {
panic(err)
}
walk(path)
}
默认的log输出到控制台。
log.Printf("%d+%d=%d\n", 3, 4, 3+4)
log.Println("Hello Golang")
log.Fatalln("Bye, the world") //日志输出后会执行os.Exit(1)
指定日志输出到文件
格式
log.New(out io.Writer, prefix string, flag int) *Logger
- out 是打开的文件,用
os.OpenFile()打开的一个可写文件 - prefix 每条日志的前缀
- flag 设置日志的格式
log.Ldate日期格式的 2009/01/23log.Lmicroseconds精确到微秒的时间 01:23:23.123123log.Ltime精确到秒 01:23:23log.Llongfile完整文件名和行号 /a/b/c/d.go:23log.Lshortfile文件名和行号 d.go:23log.LUTC如果设置了Ldate或Ltime,则使用UTC而不是本地时区log.Lmsgprefix将“前缀”从行首移到消息之前log.LstdFlags相当于Ldate | Ltime
logWriter := log.New(fout, "[BIZ_PREFIX]", log.Ldate|log.Lmicroseconds) //通过flag参数定义日志的格式
logWriter.Println("Hello Golang")
例子
package main
import (
"fmt"
"log"
"os"
"time"
)
var file, err = os.OpenFile("1.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
func logs() {
logger := log.New(file, "[abc]", log.Ldate|log.Lmicroseconds)
logger.Println("第一行")
logger.Println("第二行")
logger.Println("第三行")
}
//关闭文件
func onexit() {
if file != nil {
fmt.Println("退出了")
file.Close()
}
}
func main() {
defer func() {
//异常退出的时候关闭文件
if err := recover(); err != nil {
onexit()
}
}()
n := 0
//循环打印日志
for i := 0; i < 10; i++ {
logs()
time.Sleep(3 * time.Second)
//模拟故障
if i == 5 {
_ = 1 / n
}
}
//正常退出的时候关闭文件
onexit()
}
调用系统命令
cmd_path, err := exec.LookPath(“df”) //查看系统命令所在的目录,确保命令已安装
cmd := exec.Command("df", "-h") //相当于命令df -h,注意Command的每一个参数都不能包含空格
output, err := cmd.Output() //cmd.Output()运行命令并获得其输出结果
cmd = exec.Command("rm", "./data/test.log")
cmd.Run() //如果不需要获得命令的输出,直接调用cmd.Run()即可
本文来自博客园,作者:厚礼蝎,转载请注明原文链接:https://www.cnblogs.com/guangdelw/p/17761387.html

浙公网安备 33010602011771号