首页 > 其他分享 >golang 通过 os 包进行文件读写

golang 通过 os 包进行文件读写

时间:2023-04-25 16:14:14浏览次数:32  
标签:return err nil data 读写 golang error os

goos包 主要与操作系统打交道,实际底层还是通过相关的系统调用实现文件的读写操作,今天我们就来聊聊通过 os包 实现文件的读写操作。

我们在使用 os包 进行相关操作时,主要流程:

  • 读操作
    • open -> read -> close
  • 写操作
    • open -> read -> write -> close

总体来说,读写操作都躲不过上述流程,现在我们先看看 os包 的相关接口。

1.os包的源码

Open()/OpenFile()/Create()

// Open
func Open(name string) (*File, error) {
	return OpenFile(name, O_RDONLY, 0) // 默认通过 OpenFile实现,只读方式
}

// Create
func Create(name string) (*File, error) {
	return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666) // 实际也是调用OpenFIle,注意flag中声明了 create操作
}

// OpenFile,注释中已明确,主要就是,大部分时候,我们应该明确使用的读-open,新建-create,文件追加-openfile(声明append flag)
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
	testlog.Open(name)
	f, err := openFileNolog(name, flag, perm)
	if err != nil {
		return nil, err
	}
	f.appendMode = flag&O_APPEND != 0

	return f, nil
}

os.File
这个结构体很重要,几乎所有的文件操作都是以此结构体作为操作对象,但我们今天主要关心文件读写,我们只看读写相关方法实现。

看看读写的源码:

// Read
// Read reads up to len(b) bytes from the File and stores them in b.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}
	n, e := f.read(b)
	return n, f.wrapErr("read", e)
}

// read
// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
func (f *File) read(b []byte) (n int, err error) {
	n, err = f.pfd.Read(b)
	runtime.KeepAlive(f)
	return n, err
}

// Write
// Write writes len(b) bytes from b to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
	if err := f.checkValid("write"); err != nil {
		return 0, err
	}
	n, e := f.write(b)
	if n < 0 {
		n = 0
	}
	if n != len(b) {
		err = io.ErrShortWrite
	}

	epipecheck(f, e)

	if e != nil {
		err = f.wrapErr("write", e)
	}

	return n, err
}

// write
// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
func (f *File) write(b []byte) (n int, err error) {
	n, err = f.pfd.Write(b)
	runtime.KeepAlive(f)
	return n, err
}

简单概括下就是:

  • Read,从 File 对象中读取内容,装进 []byte
  • Write,把 []byte 中的内容,写入 File 对象中

2.通过 os 实现文件读写

读操作

var filename string = "D:\\demo1\\src\\demo23\\go-io\\file\\file.txt"

func read() []byte {
	file, err := os.Open(filename)
	defer file.Close()
	if err != nil {
		fmt.Printf("err: %s\n", err)
		return nil
	}
	var content []byte
	buf := make([]byte, 10) 
	var r int
	for {
		n, err := file.Read(buf) // 每次读取10字节内容
		content = append(content, buf[:n]...) // 读取内容写入返回数组中
		r += n
		if err != nil {
			if err == io.EOF {
				fmt.Printf("err: %s\n", err)
			}
			break
		}
	}

	return content
}

写操作

var dstFile string = "os_file_w.txt"

func write() bool {
	fw, err := os.OpenFile(dstFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 777) // 使用 OpenFile,传入 flag,可以实现安全的打开创建写入
	if err != nil {
		fmt.Printf("err: %s\n", err)
		return false
	}
	defer fw.Close()

	b, err := os.ReadFile(filename) // 读取源文件内容
	if err != nil {
		fmt.Printf("err: %s\n", err)
		return false
	}

	_, err = fw.Write(b) // 调用 File。Write() 方法实现数据的写入,实际场景中,要考虑写入文件大小
	if err != nil {
		fmt.Printf("err: %s\n", err)
		return false
	}

	return true
}

通过上面的文件操作,我们可以看到,通过 os包 File 对象,我们可以简单实现文件的读和写。

3.通过 os.ReadFile/os.WriteFile 实现文件读写

在介绍 io包 时我们提到了相关函数,这里我们深入看看。

先看看源码:

// ReadFile reads the named file 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.
func ReadFile(name string) ([]byte, error) {
	f, err := Open(name)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var size int
	if info, err := f.Stat(); err == nil {
		size64 := info.Size()
		if int64(int(size64)) == size64 {
			size = int(size64)
		}
	}
	size++ // one byte for final read at EOF

	// If a file claims a small size, read at least 512 bytes.
	// In particular, files in Linux's /proc claim size 0 but
	// then do not work right if read in small pieces,
	// so an initial read of 1 byte would not work correctly.
	if size < 512 {
		size = 512
	}

	data := make([]byte, 0, size)
	for {
		if len(data) >= cap(data) {
			d := append(data[:cap(data)], 0)
			data = d[:len(data)]
		}
		n, err := f.Read(data[len(data):cap(data)])
		data = data[:len(data)+n]
		if err != nil {
			if err == io.EOF {
				err = nil
			}
			return data, err
		}
	}
}

// WriteFile writes data to the named file, creating it if necessary.
// If the file does not exist, WriteFile creates it with permissions perm (before umask);
// otherwise WriteFile truncates it before writing, without changing permissions.
func WriteFile(name string, data []byte, perm FileMode) error {
	f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm)
	if err != nil {
		return err
	}
	_, err = f.Write(data)
	if err1 := f.Close(); err1 != nil && err == nil {
		err = err1
	}
	return err
}

从上面的源码中,我们可以清晰地看到,实际上也是将 File 的相关方法做了一层封装,进而实现读写操作,比如在读操作中,在循环读取内容时,需要先判断 data 切片是否需要扩容,然后再读取内容到 data 中,直到碰到 io.EOF error,或者是其他 error,而对于写操作,则在打开文件后,直接调用 File.Write() 写入数据,总体来说,在这个层面的封装比较简单。

最后我们看看通过 os.ReadFile/os.WriteFile 的使用示例:

var dstFile_os string = "os_wr_file_w.txt"

func readFile(filename string) ([]byte, error) {
	return os.ReadFile(filename)
}

func writeFile(dst string, data []byte) error {
	return os.WriteFile(dst, data, 777)
}

直接调用即可,相关方法已经屏蔽细节,但具体实现我们还是有必要了解的。

标签:return,err,nil,data,读写,golang,error,os
From: https://www.cnblogs.com/davis12/p/17352639.html

相关文章

  • gcc 中-O -O1 -O2 -O3 -Os -Ofast -Og优化的原理
    一般来说,如果不指定优化标识的话,gcc就会产生可调试代码,每条指令之间将是独立的:可以在指令之间设置断点,使用gdb中的p命令查看变量的值,改变变量的值等。并且把获取最快的编译速度作为它的目标。    当优化标识被启用之后,gcc编译器将会试图改变程序的结构(当然会在保证变换之后......
  • 创信国产操作系统uos桌面卡死的解决办法
    如果是整个桌面卡死,不要按主机电源键强制关机,重启后有可能会有配置文件缺失的问题。----首先尝试:按Ctrl+Alt+F2,进入tty2输入用户名,回车输入密码,回车输入命令回车:killallkwin_x11按Ctrl+Alt+F1,回到桌面----如果还是没有缓解:按Ctrl+Alt+F2,进入tty2按Ctrl+......
  • linux之dlopen、dlsym和dlclose使用和举例
     之前用过这三个函数一直没时间整理一下。今天抽时间整理一下。1、函数简介dlopen基本定义功能:打开一个动态链接库 包含头文件: #include<dlfcn.h> 函数定义: void*dlopen(constchar*pathname,intmode); 函数描述: 在dlopen的()函数以指......
  • OSS
    1.OSS(对象存储服务)文件直传具有以下几个优点:快速传输:使用OSS文件直传,文件可以直接从客户端上传到OSS服务器,跳过了传统的中转服务器,节省了时间和带宽。安全性高:OSS文件直传支持HTTPS传输协议,可以确保数据传输的安全性,同时客户端和应用程序服务器可以直接与OSS服务器......
  • PostgreSQL插件那么多,怎样管理最高效?
    摘要:华为云RDSforPostgreSQL通过插件管理功能,很好地解决了PostgreSQL版本与插件耦合的问题,帮助用户更直观、更快速地安装管理数据库插件。本文分享自华为云社区《PostgreSQL插件那么多,怎样管理最高效?》,作者:GaussDB数据库。云服务环境下,如何让客户更方便地在各个PostgreSQL的......
  • vite + vue3 + vue-router4 + ts + element plus + pinia + axios构建项目
    最后是完整的vite.config.ts、main.ts配置1、先用vite创建一个项目npmcreatevite@latest2、安装elementplusyarnaddelement-plus@element-plus/icons-vuevite.config.ts配置组件按需导入,图标自动导入npminstall-Dunplugin-vue-componentsunplugin-auto-impor......
  • iOS面试!
    只是为了防止原作者删除以保存备份一下,方便以后需要时查看https://www.cnblogs.com/berry1124/articles/17352477.html原出处转载自:https://www.jianshu.com/p/9713f816a995 ......
  • lightdb/postgresql中的统计信息详解
    和oracle,lightdb也支持单列和多列统计信息,见14.2. StatisticsUsedbythePlanner(light-pg.com)。默认情况下,analyze收集的统计信息是针对单列的,多个列之间通常没有依赖关系,在多个where里面,这容易导致基数计算失真。planner使用statistics的方式,在sql执行的plan步骤,Chapter......
  • 达梦读写分离分发测试(Jmeter 压测)
    1. 测试目的本次测试目的主要是验证达梦读写分离集群是否生效,查询负载请求是否会自动分发给备库执行2. 达梦读写分离部署(一写一读,过程忽略)配置ip地址实例名端口号数据库版本主库192.168.145.66DM6652364-2-98-21.12.16-153423-10040-SEC......
  • mac os下安装k8s的kubernetes-dashboard
     需要这个的继续往下看 环境macos 12.6.2 安装dokcer https://docs.docker.com/desktop/install/mac-install/安装k8s https://blog.csdn.net/qq_20042935/article/details/124472626 <-看这位兄弟写的很详细了安装kubernetes-dashboard 1.创建命名空间 kubec......