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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)