目录
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
*/