第63篇 笔记-Go 文件的读写操作

本文详细介绍了Go语言中进行文件操作的各种方法,包括写入和读取纯文本、JSON、XML文件,以及二进制文件的处理。此外,还展示了如何使用buffer优化文件读写性能,读取INI配置文件内容,以及遍历目录操作。示例代码覆盖了从基本的文件创建、打开到高级的数据序列化和反序列化,为Go语言的文件操作提供了全面的实践指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1. 写纯文本文件

2. 读纯文本文件

3. 写 JSON 文件

4. 读 JSON 文件

5. 写 XML 文件

6. 读 XML 文件

7. 写二进制文件

8. 读二进制文件

9. 使用buffer写文件

10. 使用buffer读文件

11. 读取 INI 配置文件中的值

12. 目录遍历操作


1. 写纯文本文件

由于Go语言的 fmt 包中打印函数强大而灵活,写纯文本数据非常简单直接,示例代码如下所示:

package main
import (
    "bufio"
    "fmt"
    "os"
)
func main() {
    //创建一个新文件,写入内容
    filePath := "./output.txt"
    file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
    if err != nil {
        fmt.Printf("打开文件错误= %v \n", err)
        return
    }
    //及时关闭
    defer file.Close()
    //写入内容
    str := "D:\\Go_WorkSpace\\src\\github.com\\goExample_test\\mytest\\file\\writeTxt\n" 
    //写入时,使用带缓存的 *Writer
    writer := bufio.NewWriter(file)
    for i := 0; i < 3; i++ {
        writer.WriteString(str)
    }
    //因为 writer 是带缓存的,因此在调用 WriterString 方法时,内容是先写入缓存的
    //所以要调用 flush方法,将缓存的数据真正写入到文件中。
    writer.Flush()
}

/* 代码会在当前目录下生成一个 output.txt 文件,文件内容如下:
D:\Go_WorkSpace\src\github.com\goExample_test\mytest\file\writeTxt
D:\Go_WorkSpace\src\github.com\goExample_test\mytest\file\writeTxt
D:\Go_WorkSpace\src\github.com\goExample_test\mytest\file\writeTxt
*/

 

2. 读纯文本文件

打开并读取一个纯文本格式的数据跟写入纯文本格式数据一样简单。要解析文本来重建原始数据可能稍微复杂,这需根据格式的复杂性而定。

示例代码如下所示:

package main
import (
    "bufio"
    "fmt"
    "io"
    "os"
)
func main() {
    //打开文件
    file, err := os.Open("./input.txt")
    if err != nil {
        fmt.Println("文件打开失败 = ", err)
    }
    //及时关闭 file 句柄,否则会有内存泄漏
    defer file.Close()
    //创建一个 *Reader , 是带缓冲的
    reader := bufio.NewReader(file)
    for {
        str, err := reader.ReadString('\n') //读到一个换行就结束
        if err == io.EOF {                  //io.EOF 表示文件的末尾
            break
        }
        fmt.Print(str)
    }
    fmt.Println("文件读取结束...")
}

/* 代码会读取当前目录下的 input.txt 文件
D:\Go_WorkSpace\src\github.com\goExample_test\mytest\file\readTxt
D:\Go_WorkSpace\src\github.com\goExample_test\mytest\file\readTxt
D:\Go_WorkSpace\src\github.com\goExample_test\mytest\file\readTxt
文件读取结束...
*/

 

3. 写 JSON 文件

(1)JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成。它基于 JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999 的一个子集。

(2)JSON 是一种使用 UTF-8 编码的纯文本格式,采用完全独立于语言的文本格式,由于写起来比 XML 格式方便,并且更为紧凑,同时所需的处理时间也更少,致使 JSON 格式越来越流行,特别是在通过网络连接传送数据方面。

(3)开发人员可以使用 JSON 传输简单的字符串、数字、布尔值,也可以传输一个数组或者一个更复杂的复合结构。在 Web 开发领域中,JSON 被广泛应用于 Web 服务端程序和客户端之间的数据通信。

(4)Go语言内建对 JSON 的支持,使用内置的 encoding/json 标准库,开发人员可以轻松使用Go程序生成和解析 JSON 格式的数据。

使用Go语言创建一个 json 文件非常方便,示例代码如下:

package main

import (
    "encoding/json"
    "fmt"
    "os"
)

type Website struct {
    Name   string `xml:"name,attr"`
    Url    string
    Course []string
}

func main() {
    info := []Website{{"Golang", "http://net/golang/", []string{"http://net/cplus/", "http://net/linux_tutorial/"}}, {"Java", "http://net/java/", []string{"http://net/socket/", "http://net/python/"}}}

    // 创建文件
    filePtr, err := os.Create("info.json")
    if err != nil {
        fmt.Println("文件创建失败", err.Error())
        return
    }
    defer filePtr.Close()

    // 创建Json编码器
    encoder := json.NewEncoder(filePtr)

    err = encoder.Encode(info)
    if err != nil {
        fmt.Println("编码错误", err.Error())

    } else {
        fmt.Println("编码成功")
    }
}

/* 编码成功,代码会在当前目录下生成一个 info.json 文件,文件内容如下:
[{"Name":"Golang","Url":"http://net/golang/","Course":["http://net/cplus/","http://net/linux_tutorial/"]},{"Name":"Java","Url":"http://net/java/","Course":["http://net/socket/","http://net/python/"]}]
*/

4. 读 JSON 文件

读 JSON 数据与写 JSON 数据一样简单,示例代码如下:

package main

import (
    "encoding/json"
    "fmt"
    "os"
    "log"
)

type Website struct {
    Name   string `xml:"name,attr"`
    Url    string
    Course []string
}

func main() {
    filePtr, err := os.Open("./info.json")
    if err != nil {
        fmt.Println("文件打开失败 [Err:%s]", err.Error())
        return
    }
    defer filePtr.Close()
    var info []Website
    // 创建json解码器
    decoder := json.NewDecoder(filePtr)
    err = decoder.Decode(&info)
    if err != nil {
        fmt.Println("解码失败", err.Error())
    } else {
        fmt.Println("解码成功")
        fmt.Println(info)
		fmt.Println(info[0].Name)
        // 按格式输出
        data, err := json.MarshalIndent(info, "", "    ")
        if err != nil {
            log.Fatalf("JSON marshaling failed: %s", err)
        }
        fmt.Printf("%s\n", data)
    }
}

/*
解码成功
[{Golang http://net/golang/ [http://net/cplus/ http://net/linux_tutorial/]} {Java http://net/java/ [http://net/socket/ http://net/python/]}]
Golang
[
    {
        "Name": "Golang",
        "Url": "http://net/golang/",
        "Course": [
            "http://net/cplus/",
            "http://net/linux_tutorial/"
        ]
    },
    {
        "Name": "Java",
        "Url": "http://net/java/",
        "Course": [
            "http://net/socket/",
            "http://net/python/"
        ]
    }
]
*/

 

5. 写 XML 文件

(1)XML(extensible Markup Language)格式被广泛用作一种数据交换格式,并且自成一种文件格式。与上一节介绍的 JSON 相比 XML 要复杂得多,而且手动写起来相对乏味得多。

(2)在 JSON 还未像现在这么广泛使用时,XML 的使用相当广泛。XML 作为一种数据交换和信息传递的格式,使用还是很广泛的,现在很多开放平台接口,基本都会支持 XML 格式。

(3)Go语言内置的 encoding/xml 包可以用在结构体和 XML 格式之间进行编解码,其方式跟 encoding/json 包类似。然而与 JSON 相比 XML 的编码和解码在功能上更苛刻得多,这是由于 encoding/xml 包要求结构体的字段包含格式合理的标签,而 JSON 格式却不需要。

使用 encoidng/xml 包可以很方便的将 xml 数据存储到文件中,示例代码如下:

package main
import (
    "encoding/xml"
    "fmt"
    "os"
)
type Website struct {
    Name   string `xml:"name,attr"`
    Url    string
    Course []string
}
func main() {
    //实例化对象
    info := Website{"Go语言中文网", "http://net/golang/", []string{"Go语言入门教程", "Golang入门教程"}}
    f, err := os.Create("./info.xml")
    if err != nil {
        fmt.Println("文件创建失败", err.Error())
        return
    }
    defer f.Close()
    //序列化到文件中
    encoder := xml.NewEncoder(f)
    err = encoder.Encode(info)
    if err != nil {
        fmt.Println("编码错误:", err.Error())
        return
    } else {
        fmt.Println("编码成功")
    }
}

/* 代码会在当前目录生成一个 info.xml 文件,文件的内容如下所示:
<Website name="Go语言中文网"><Url>http://net/golang/</Url><Course>Go语言入门教程</Course><Course>Golang入门教程</Course></Website>
*/

 

6. 读 XML 文件

读 XML 文件比写 XML 文件稍微复杂,特别是在必须处理一些我们自定义字段的时候(例如日期)。但是,如果我们使用合理的打上 XML 标签的结构体,就不会复杂。示例代码如下:

package main
import (
    "encoding/xml"
    "fmt"
    "os"
)
type Website struct {
    Name   string `xml:"name,attr"`
    Url    string
    Course []string
}
func main() {
    //打开xml文件
    file, err := os.Open("./info.xml")
    if err != nil {
        fmt.Printf("文件打开失败:%v", err)
        return
    }
    defer file.Close()
    info := Website{}
    //创建 xml 解码器
    decoder := xml.NewDecoder(file)
    err = decoder.Decode(&info)
    if err != nil {
        fmt.Printf("解码失败:%v", err)
        return
    } else {
        fmt.Println("解码成功")
        fmt.Println(info)
    }
}

/*
解码成功
{Go语言中文网 http://net/golang/ [Go语言入门教程 Golang入门教程]}
*/

 

7. 写二进制文件

Go语言中的 encoding/gob 包也提供了与 encoding/json 包一样的编码解码功能,并且容易使用。gob 格式是Go语言上用于文件存储和网络传输最为方便的格式。

下面通过一个简单的示例来演示一下Go语言是如何生成一个二进制文件的,代码如下所示:

package main
import (
    "encoding/gob"
    "fmt"
    "os"
)
func main() {
    info := "http://net/golang/"
    file, err := os.Create("./output.txt")
    if err != nil {
        fmt.Println("文件创建失败", err.Error())
        return
    }
    defer file.Close()
    encoder := gob.NewEncoder(file)
    err = encoder.Encode(info)
    if err != nil {
        fmt.Println("编码错误", err.Error())
        return
    } else {
        fmt.Println("编码成功")
    }
}

/* 编码成功,会在当前目录下生成一个 output.gob 文件*/

 

8. 读二进制文件

package main
import (
    "encoding/gob"
    "fmt"
    "os"
)
func main() {
    file, err := os.Open("./output.gob")
    if err != nil {
        fmt.Println("文件打开失败", err.Error())
        return
    }
    defer file.Close()
    decoder := gob.NewDecoder(file)
    info := ""
    err = decoder.Decode(&info)
    if err != nil {
        fmt.Println("解码失败", err.Error())
    } else {
        fmt.Println("解码成功")
        fmt.Println(info)
    }
}

/*
解码成功
http://net/golang/
*/

 

9. 使用buffer写文件

(1)buffer 是缓冲器的意思,Go语言要实现缓冲读取需要使用到 bufio 包。bufio 包本身包装了 io.Reader 和 io.Writer 对象,同时创建了另外的 Reader 和 Writer 对象,因此对于文本 I/O 来说,bufio 包提供了一定的便利性。

(2)buffer 缓冲器的实现原理就是,将文件读取进缓冲(内存)之中,再次读取的时候就可以避免文件系统的 I/O 从而提高速度。同理在进行写操作时,先把文件写入缓冲(内存),然后由缓冲写入文件系统。

(3)bufio 和 io 包中有很多操作都是相似的,唯一不同的地方是 bufio 提供了一些缓冲的操作,如果对文件 I/O 操作比较频繁的,使用 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)

示例代码如下所示:

package main
import (
    "bufio"
    "fmt"
    "os"
)
func main() {
    name := "demo.txt"
    content := "http://net/golang/"
    fileObj, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
    if err != nil {
        fmt.Println("文件打开失败", err)
    }
    defer fileObj.Close()
    writeObj := bufio.NewWriterSize(fileObj, 4096)
    //使用 Write 方法,需要使用 Writer 对象的 Flush 方法将 buffer 中的数据刷到磁盘
    buf := []byte(content)
    if _, err := writeObj.Write(buf); err == nil {
        if err := writeObj.Flush(); err != nil {
            panic(err)
        }
        fmt.Println("数据写入成功")
    }
}

/* 数据写入成功,会在当前目录之下生成 demo.txt 文件,内容:
http://net/golang/
*/

 

10. 使用buffer读文件

使用 bufio 包读取文件也非常方便,先来看下 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('\n') 或 ReadString('\n'),或者使用一个 Scanner 来代替。ReadLine 通过调用 ReadSlice 方法实现,返回的也是缓存的切片,用于读取一行数据,不包括行尾标记(\n 或 \r\n)
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
//读取单个 UTF-8 字符并返回一个 rune 和字节大小
func (b *Reader) ReadRune() (r rune, size int, err error)

示例代码如下:

package main
import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)
func main() {
    fileObj, err := os.Open("demo.txt")
    if err != nil {
        fmt.Println("文件打开失败:", err)
        return
    }
    defer fileObj.Close()
    //一个文件对象本身是实现了io.Reader的 使用bufio.NewReader去初始化一个Reader对象,存在buffer中的,读取一次就会被清空
    reader := bufio.NewReader(fileObj)
    buf := make([]byte, 1024)
    //读取 Reader 对象中的内容到 []byte 类型的 buf 中
    info, err := reader.Read(buf)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("读取的字节数:" + strconv.Itoa(info))
    //这里的buf是一个[]byte,因此如果需要只输出内容,仍然需要将文件内容的换行符替换掉
    fmt.Println("读取的文件内容:", string(buf))
}

/*
读取的字节数:18
读取的文件内容: http://net/golang/
*/

 

11. 读取 INI 配置文件中的值

设置 example.ini 文件内容:

[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
hideDotFiles = dotGitOnly

[remote "origin"]
url = https://github.com
fetch = +refs/heads/*:refs/remotes/origin/*

[branch "master"]
remote = origin
merge = refs/heads/master

示例代码如下:

package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

// 根据文件名,段名,键名获取ini的值
func getValue(filename, expectSection, expectKey string) string {
	// 打开文件
	file, err := os.Open(filename)

	// 文件找不到,返回空
	if err != nil {
		return ""
	}

	// 在函数结束时,关闭文件
	defer file.Close()

	// 使用读取器读取文件
	reader := bufio.NewReader(file)

	// 当前读取的段的名字
	var sectionName string

	for {
		// 读取文件的一行
		linestr, err := reader.ReadString('\n')
		if err != nil {
			break
		}

		// 切掉行的左右两边的空白字符
		linestr = strings.TrimSpace(linestr)

		// 忽略空行
		if linestr == "" {
			continue
		}

		// 忽略注释
		if linestr[0] == ';' {
			continue
		}

		// 行首和尾巴分别是方括号的,说明是段标记的起止符
		if linestr[0] == '[' && linestr[len(linestr)-1] == ']' {
			// 将段名取出
			sectionName = linestr[1 : len(linestr)-1]

			// 这个段是希望读取的
		} else if sectionName == expectSection {
			// 切开等号分割的键值对
			pair := strings.Split(linestr, "=")

			// 保证切开只有1个等号分割的简直情况
			if len(pair) == 2 {
				// 去掉键的多余空白字符
				key := strings.TrimSpace(pair[0])

				// 是期望的键
				if key == expectKey {
					// 返回去掉空白字符的值
					return strings.TrimSpace(pair[1])
				}
			}
		}
	}
	return ""
}

func main() {
	fmt.Println(getValue("example.ini", "remote \"origin\"", "fetch"))
	fmt.Println(getValue("example.ini", "core", "hideDotFiles"))
	fmt.Println(getValue("example.ini", "branch \"master\"", "remote"))
	fmt.Println(getValue("example.ini", "core", "url"))
}

/*
+refs/heads/*:refs/remotes/origin/*
dotGitOnly
origin
          //最后一行,因为 段名 与 键值名 没有对应,返回空
*/

 

12. 目录遍历操作

package main

import (
	"fmt"	
	"io/ioutil"	
	"os"	
	"path"	
	"strings"	
)
	
type fileList struct {
	path string	
	fileInfo os.FileInfo	
}
	
func main(){
	dir := "D:/Go_WorkSpace/src/github.com/goExample/"	
	dirList, err := readDir(dir);
	
	if err != nil {
	    fmt.Println("readdir error:", err)	
	    return	
	}
	
	for k, v := range dirList {
	    fmt.Println(k, v.path + v.fileInfo.Name())	
	}	
}
	
//递归遍历目录下所有文件	
func readDir(dir string) (data []fileList, err error) {
	//判断文件或目录是否存在	
	file, err := os.Stat(dir)	
	if err != nil {
	    return data, err	
	}
	
	//如果不是目录,直接返回文件信息	
	if !file.IsDir() {
	    data = append(data, fileList{path.Dir(dir) + "/", file})	
	    return data, err	
	}
	
	fileInfo, err := ioutil.ReadDir(dir)	
	if err != nil {
	    fmt.Println(fileInfo)	
	    return data, err	
	}
	
	//目录为空	
	if len(fileInfo) == 0 {
	    return	
	}
	
	for _, v := range fileInfo {
	    if v.IsDir() {
	        if subDir, err := readDir(dir + v.Name()); err != nil {
	            return data, err	
	        } else {
	            data = append(data, subDir...)	
	        }	
	    } else {
	        data = append(data, fileList{strings.TrimRight(dir, "/") + "/", v})	
	    }
    }	
    return data, err	
}

/*
0 D:/Go_WorkSpace/src/github.com/goExample/inireader/example.ini
1 D:/Go_WorkSpace/src/github.com/goExample/inireader/inireader.go
2 D:/Go_WorkSpace/src/github.com/goExample/listDir/main.go
3 D:/Go_WorkSpace/src/github.com/goExample/readBuffer/demo.txt
4 D:/Go_WorkSpace/src/github.com/goExample/readBuffer/main.go
5 D:/Go_WorkSpace/src/github.com/goExample/readGob/main.go
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wonderBlock

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值