Golang文件读写操作详情

 更新时间:2022年07月25日 10:46:58   作者:​ Leefs​  
这篇文章主要介绍了Golang文件读写操作详情,文件是数据源(保存数据的地方)的一种,文件最主要的作用就是保存数据,文件在程序中是以流的形式来操作的,更多详细内容需要的朋友可以参考一下

一、概念

文件是数据源(保存数据的地方)的一种,文件最主要的作用就是保存数据。

文件在程序中是以流的形式来操作的。

  • 输入流和输出流

  • :数据在数据源(文件)和程序(内存)之间经历的路径
  • 输入流:数据从数据源(文件)到程序(内存)的路径
  • 输出流:数据从程序(内存)到数据源(文件)的路径

二、读取文件操作

2.1 打开和关闭文件

打开文件:

func Open(filename string) (file *File, err error)

Open打开一个文件用于读取。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有O_RDONLY模式。如果出错,错误底层类型是*PathError

关闭文件:

func (f *File) Close() error

Close关闭文件f,使文件不能用于读写。它返回可能出现的错误。

示例:

package main
import (
	"fmt"
	"os"
)
func main() {
	//只读方式打开当前目录下的test.txt
	file, err := os.Open("test.txt")
	if err != nil {
		fmt.Println("open file failed!,err:",err)
	}
	//返回的是一个指针
	fmt.Println(&file)

	//关闭文件
	//err = file.Close()
	//if err != nil{
	//	fmt.Println("close file failed!,err:",err)
	//}
	//为了防止文件忘记关闭,通常使用defer注册文件关闭语句。
	defer file.Close() // 关闭文件
}

运行结果:

0xc0000ce018

defer 语句

  • defer—般用于资源的释放和异常的捕捉。
  • defer语句会将其后面跟随的语句进行延迟处理;跟在defer后面的语言将会在程序进行最后的return之后再执行。
  • defer归属的函数即将返回时,将延迟处理的语句按defer的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被 defer的语句,最先被执行。

2.2 file.Read() 读取文件

Read 方法定义

func (f *File) Read(b []byte) (n int, err error)

从文件对象中读取长度为b的字节,返回当前读到的字节数以及错误信息。因此使用该方法需要先初始化一个符合内容大小的空的字节列表。读取到文件的末尾时,该方法返回0,io.EOF

ReadAt方法定义

func (file *File) ReadAt(b []byte, off int64) (n int, err Error)

从文件的off偏移量开始读取长度为b的字节。返回读取到字节数以及错误信息。当读取到的字节数n小于想要读取字节的长度len(b)的时候,该方法将返回非空的error。当读到文件末尾时,err返回io.EOF。

  • b:是指定字节长度的缓冲区
  • off:int64类型的偏移量,从此位置开始读取。

注意:ReadAt 绝对不允许出现,没有读满 buffer,又非 EOF,又没有 err 的情况发生,这个是接口语义明确规定的,这是一个非常细节的区别。

一次性读取

适用于读取较小文件使用:

package main
import (
	"fmt"
	"io"
	"os"
)
func main() {
	//1、只读方式打开当前目录下的test2.txt
	file, err := os.Open("test2.txt")
	if err != nil {
		fmt.Println("open file failed!,err:",err)
		return
	}
	//3、当函数退出时,及时关闭file
	//使用 defer 内置函数 当函数退出时才会调用,要及时关闭否则会内存泄露
	defer file.Close()
	//2、使用Read方法读取数据,注意一次只会读取128个字节
	tmp := make([]byte, 128)
	n, err := file.Read(tmp)
	//使用ReadAt方法读取数据,注意一次只会读取6个字节
	//tmp := make([]byte, 6)
	//n, err := file.ReadAt(tmp,6)

	//io.EOF 表示文件的末尾
	if err == io.EOF {
		fmt.Println("文件读取完毕")
		return
	}
	if err != nil {
		fmt.Println("read file failed,err:",err)
		return
	}
	fmt.Printf("读取了 %d 字节数据\n", n)
	fmt.Println(string(tmp[:n]))
}

运行结果:

读取了 13 字节数据
Hello Golang!

循环读取

使用 for 循环读取文件中的所有数据:

package main
import (
	"fmt"
	"io"
	"os"
)
//循环读取文件
func main() {
	//只读方式打开当前目录下的test.txt
	file, err := os.Open("test.txt")
	if err != nil {
		fmt.Println("open file failed!,err:",err)
		return
	}
	//关闭文件
	defer file.Close()
	//循环读取文件
	var content []byte
	//使用Read方法读取数据,注意一次只会读取128个字节
	tmp := make([]byte, 128)

	for {
		n, err := file.Read(tmp)	//每次读取128个字节
		if err == io.EOF {
			fmt.Println("文件读取完毕")
			break
		}
		if err != nil {
			fmt.Println("read file failed,err:",err)
			return
		}
		//每次读取的内容都追加到已知的byte切片中
		content = append(content,tmp[:n]...)
	}
	//将byte类型转换结果,打印结果
	fmt.Println(string(content))
}

运行结果:

文件读取完毕
水陆草木之花,可爱者甚蕃。晋陶渊明独爱菊。自李唐来,世人甚爱牡丹。
予独爱莲之出淤泥而不染,濯清涟而不妖,中通外直,不蔓不枝,香远益清,亭亭净植,可远观而不可亵玩焉。

予谓菊,花之隐逸者也;牡丹,花之富贵者也;莲,花之君子者也。
噫!菊之爱,陶后鲜有闻。莲之爱,同予者何人?牡丹之爱,宜乎众矣!

说明:

这里的循环读取文件其实就是一个不断追加的过程,将每次读取的128个字节追加到预先定义好的content切片中,最后将切片转换成string类型,进行打印显示。

2.3 bufio 读取文件

语法:

//bufio.NewReader(rd io.Reader) *Reader
r := bufio.NewReader(file)

//func (b *Reader) ReadString(delim byte) (string, error)
n, err := r.Read(buf)

参数

返回值:

使用 NewReader 读取文件时,首先,需要打开文件,接着, 使用打开的文件返回的文件句柄当作 函数参数 传入 NewReader。

最后,使用 NewReader 返回的 reader 对象调用 Read 来读取文件。文件读取结束的标志是返回的 n 等于 0,因此,如果需要读取整个文件内容,那么我们需要使用 for 循环 不停的读取文件,直到 n 等于 0。

  • file:要读取的文件句柄;
  • buf:读取的数据存放的缓冲区。
  • n:读取到的长度
  • err:读取失败,则返回错误信息。

示例:

package main
import (
	"bufio"
	"fmt"
	"io"
	"os"
)
//bufio读取文件
func main() {

	//只读方式打开当前目录下的test.txt
	file, err := os.Open("test.txt")
	if err != nil {
		fmt.Println("open file failed!,err:",err)
		return
	}
	//关闭文件,避免内存泄露
	defer file.Close()
	//通过bufio缓冲区读取文件
	reader := bufio.NewReader(file)	//建立缓冲区,将文件内容放入到缓冲区
	//循环读取文件信息
	for {
		line, err := reader.ReadString('\n')	//读到一个换行就结束

		if err == io.EOF {	 //io.EOF 表示文件的末尾
			//输出最后的内容
			if len(line) != 0 {
				fmt.Println(line)
			}
			fmt.Println("文件读取完毕")
			break
		}
		if err != nil {
			fmt.Println("read file failed,err:",err)
			return
		}
		fmt.Println(line)
	}
}

运行结果:

水陆草木之花,可爱者甚蕃。晋陶渊明独爱菊。自李唐来,世人甚爱牡丹。
予独爱莲之出淤泥而不染,濯清涟而不妖,中通外直,不蔓不枝,香远益清,亭亭净植,可远观而不可亵玩焉。
予谓菊,花之隐逸者也;牡丹,花之富贵者也;莲,花之君子者也。
噫!菊之爱,陶后鲜有闻。莲之爱,同予者何人?牡丹之爱,宜乎众矣!
文件读取完毕

2.4 ioutil 读取文件

语法:

func ReadFile(name string) ([]byte, error)
  • name:文件路径地址

使用 io/ioutil.ReadFile 方法一次性将文件读取到内存中,只需要将文件名作为参数传入。

示例:

package main
import (
	"fmt"
	"io/ioutil"
)
//ioutil 读取整个文件
func main() {
	content, err := ioutil.ReadFile("test.txt")
	if err != nil {
		fmt.Println("read failed,err:",err)
		return
	}
	fmt.Println(string(content))
}

运行结果:

水陆草木之花,可爱者甚蕃。晋陶渊明独爱菊。自李唐来,世人甚爱牡丹。
予独爱莲之出淤泥而不染,濯清涟而不妖,中通外直,不蔓不枝,香远益清,亭亭净植,可远观而不可亵玩焉。
予谓菊,花之隐逸者也;牡丹,花之富贵者也;莲,花之君子者也。
噫!菊之爱,陶后鲜有闻。莲之爱,同予者何人?牡丹之爱,宜乎众矣!

注意:如果文件比较大,一次性读取整个文件会占用很大的内存,影响执行效率。

建议读取小文件时使用,不太适用于大文件的读取。

效率比较

  • 当文件较小(KB 级别)时,ioutil > bufio > file.Read()。
  • 当文件大小比较常规(MB 级别)时,三者差别不大,但 bufio 优势已经显现出来。
  • 当文件较大(GB 级别)时,bufio > file.Read()> ioutil。

当读取小文件时,使用ioutil效率明显优于file.Read()bufio,但如果是大文件,bufio读取会更快,效率更高。

三、写入文件操作

3.1 os.OpenFile()函数

语法:

func OpenFile(name string, flag int, perm uint32) (file *File, err Error)

参数

os.OpenFile()函数能够以指定模式打开文件,从而实现文件写入相关功能。

  • name:要文件路径+文件名;
  • flag:打开文件的模式,只读、读写等;
  • perm:文件权限,一个八进制数。r(读)04,W(写)02,x(执行)01。

模式flag种类:

模式含义
os.O_WRONLY只写
os.O_CREATE如果不存在文件,创建文件
os.O_RDONLY只读
os.O_RDWR可读可写
os.O_TRUNC打开时清空文件原先内容
os.O_APPEND追加

若同时想用多种可用|拼接不同模式。

文件权限perm:

使用4位8进制数来表示三种类型用户的权限,首位取0,形式即0XXX

  • 第一个X表示的是文件所有者的权限;
  • 第二个X表示的是组用户的权限;
  • 第三个X表示的是其他用户的权限。

每位数字所代表的权限:读r=4,写w=2,可执行x=1

数字rwx权限
0---所有权限均无
1--x可执行
2-w-可写
3-wx可写,可执行
4r--可读
5r-x可读,可执行
6rw-可读,可写
7rwx可读,可写,可执行

常使用的0644-rw-r--r--),表示文件所有者可读写,同组用户及其他用户只可读。

3.2 Write和WriteString 方式写入

Write语法:

func (file *File) Write(b []byte) (n int, err Error)

参数

返回值:

使用 Write 方法写文件,接受的 参数 是一个要写入的文件内容的 字节 数组。如果写入成功,返回成功写入的字节数,如果写入失败,返回 error 信息。

WriteString语法:

func (f *File) WriteString(s string) (n int, err error)

参数

返回值:

使用 WriteString 方法写文件,接受的参数是一个要写入的文件内容的 字符串。如果写入成功,返回成功写入的字节数,如果写入失败,返回 error 信息。

  • file:文件对象
  • b:要写入的文件内容
  • n: 成功写入的字节数
  • err:写入失败,则返回错误信息
  • f:文件对象
  • s:要写入的文件内容
  • n:成功写入的字节数
  • err:写入失败,则返回错误信息

示例:

package main
import (
	"fmt"
	"os"
)
//创建并写入数据
//Write 和 WriteString
func main() {
	//os.O_CREATE|os.O_RDWR:如果不存在文件,创建文件,可读可写
	//0666对应:-rw-rw-rw-
	file, err := os.OpenFile("D:/bb.txt", os.O_CREATE|os.O_RDWR, 0666)
	if err != nil {
		fmt.Println("open file failed,err:",err)
		return
	}
	defer file.Close()
	str := "Hello Golang\r\n"
	file.Write([]byte(str))	//写入字节切片数据
	file.WriteString("直接写入的字符串数据") //直接写入字符串数据
}

3.3 bufio.NewWriter

语法:

func NewWriter(w io.Writer) *Writer
func (b *Writer) WriteString(s string) (int, error)
func (b *Writer) Flush() error

将要写入的内容写入缓存中,在执行flush的时候才会被写到磁盘。

  • 创建writer实例
  • 将信息写入缓存
  • 将缓冲写入文件

示例:

package main
import (
	"bufio"
	"fmt"
	"os"
)
//bufio.NewWriter
func main() {
	//1、打开文件
	file,err := os.OpenFile("D:/cc.txt",os.O_CREATE|os.O_TRUNC|os.O_WRONLY,0666)
	if err != nil {
		fmt.Println("open file failed,err:",err)
		return
	}
	//5、关闭文件流
	defer file.Close()

	//2、创建writer对象
	writer := bufio.NewWriter(file)

	for i := 0; i < 10; i++ {
		writer.WriteString("Hello Golang\r\n")	//3、将数据先写入缓存
	}
	writer.Flush()	//4、将缓存中的内容写入文件
}

3.4 ioutil.WriteFile

语法:

func WriteFile(filename string, data []byte, perm os.FileMode) error

参数

返回值

使用 WriteFile 方法写文件,接受的第一个 参数 是一个 string 类型 的文件名,第二个参数是一个要写入的文件内容的 byte 数组,最后一个参数是文件的权限。如果写入成功,返回空的 error 信息,如果写入失败,返回 error 信息。

  • filename:文件路径+文件名称
  • data:要写入的文件内容
  • perm:文件权限
  • err:写入失败,则返回错误信息

示例:

package main
import (
	"fmt"
	"io/ioutil"
)
//ioutil.WriteFile
func main() {
	str := "Hello Golang"
	err := ioutil.WriteFile("D:/dd.txt", []byte(str), 0666)
	if err != nil {
		fmt.Println("write file failed,err:",err)
		return
	}
}

四、复制文件

4.1 通过ioutil进行复制

package main

import (
	"fmt"
	"io/ioutil"
)
//复制文件
//ioutil 进行复制
//编写一个函数,接收两个文件路径 srcFileName dstFileName
func CopyFile(srcFileName string,dstFileName string)(err error){
	input, err := ioutil.ReadFile(srcFileName)
	if err != nil {
		fmt.Println(err)
		return err
	}
	err = ioutil.WriteFile(dstFileName, input, 0644)
	if err != nil {
		fmt.Println("Error creating",dstFileName)
		fmt.Println(err)
		return err
	}
	return nil
}
func main() {
	srcFile := "D:/aa.zip"
	dstFile := "D:/bb.zip"
	err := CopyFile(srcFile, dstFile)
	if err == nil {
		fmt.Printf("拷贝完成\n")
	}else {
		fmt.Printf("拷贝错误 err=%v\n",err)
	}
}

4.2 以文件流的方式复制文件

package main
import (
	"fmt"
	"io"
	"os"
)
//复制数据
func CopyFile(srcFileName string,dstFileName string)(err error){
	source, _ := os.Open(srcFileName)
	destination, _ := os.OpenFile(dstFileName, os.O_CREATE|os.O_WRONLY, 0666)
	buf := make([]byte, 128)
	for {
		n, err := source.Read(buf)
		if err != nil && err != io.EOF {
			return err
		}
		if n == 0 {
			break
		}
		if _,err := destination.Write(buf[:n]); err != nil {
			return err
		}
	}
	return nil
}
func main() {
	srcFile := "D:/aa.zip"
	dstFile := "D:/bb.zip"
	err := CopyFile(srcFile, dstFile)
	if err == nil {
		fmt.Printf("拷贝完成\n")
	}else {
		fmt.Printf("拷贝错误 err=%v\n",err)
	}
}

五、其他操作

package main
import (
	"fmt"
	"os"
)
func main() {
	//文件重命名
	err01 := os.Rename("D:/aa.txt","D:/ee.txt")	//只能同盘操作
	if err01 != nil {
		fmt.Println(err01)
	}
	//创建目录
	err02 := os.Mkdir("D:/aa", 0666)
	if err02 != nil {
		fmt.Println(err02)
	}
	//一次创建多个目录
	err03 := os.MkdirAll("D:/aa/bb/cc",0666)	//创建多级目录
	if err03 != nil {
		fmt.Println(err03)
	}
	//删除目录和文件
	err04 := os.Remove("D:/ee.txt")
	if err04 != nil {
		fmt.Println(err04)
	}
	//一次删除多个目录或者文件
	err05 := os.RemoveAll("D:/aa")
	if err05 != nil {
		fmt.Println(err05)
	}
}

到此这篇关于Golang文件读写操作详情的文章就介绍到这了,更多相关Go文件操作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • go货币计算时如何避免浮点数精度问题

    go货币计算时如何避免浮点数精度问题

    在开发的初始阶段,我们经常会遇到“浮点数精度”和“货币值表示”的问题,那么在golang中如何避免这一方面的问题呢,下面就跟随小编一起来学习一下吧
    2024-02-02
  • Golang 的defer执行规则说明

    Golang 的defer执行规则说明

    这篇文章主要介绍了Golang 的defer执行规则说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • go 压缩解压zip文件源码示例

    go 压缩解压zip文件源码示例

    这篇文章主要为大家介绍了go压缩及解压zip文件的源码示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Golang语言使用像JAVA Spring注解一样的DI和AOP依赖注入实例

    Golang语言使用像JAVA Spring注解一样的DI和AOP依赖注入实例

    这篇文章主要为大家介绍了Golang语言使用像JAVA Spring注解一样的DI和AOP依赖注入实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • GO中的时间操作总结(time&dateparse)

    GO中的时间操作总结(time&dateparse)

    日常开发过程中,对于时间的操作可谓是无处不在,但是想实现时间自由还是不简单的,多种时间格式容易混淆,本文为大家整理了一下GO中的时间操作,有需要的可以参考下
    2023-09-09
  • Golang内存管理之内存逃逸分析

    Golang内存管理之内存逃逸分析

    逃逸分析是指由编译器决定内存分配的位置,不需要程序员指定,这篇文章主要为大家详细介绍了Golang中内存逃逸分析的几种方法,需要的可以参考一下
    2023-07-07
  • 4个场景教会你Go中Goroutine和通道是怎么用的

    4个场景教会你Go中Goroutine和通道是怎么用的

    本篇给出了4个在运维开发工作中较为常见的且也是比较典型的场景,通过这些场景来带大家掌握Go中Goroutine和通道是怎么使用的,需要的可以学习一下
    2023-05-05
  • Golang两行代码实现发送钉钉机器人消息

    Golang两行代码实现发送钉钉机器人消息

    创建一个钉钉机器人必须使用加签,本文通过Golang两行代码实现发送钉钉机器人消息,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2021-12-12
  • Go语言读取YAML 配置文件的两种方式分享

    Go语言读取YAML 配置文件的两种方式分享

    在日常开发中,YAML 格式的文件基本上被默认为是配置文件,其内容因为缩进带来的层级感看起来非常直观和整洁。本文分享了读取YAML 配置文件的两种方式,需要的可以参考一下
    2022-12-12
  • golang http使用踩过的坑与填坑指南

    golang http使用踩过的坑与填坑指南

    这篇文章主要介绍了golang http使用踩过的坑与填坑指南,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04

最新评论