Go语言学习篇05
Golang 文件操作
文件简介
- 基本介绍
- 文件是
数据源
,数据库本质是文件 - 文件在程序中是以流的形式来操作的
- 文件是
- 流的概述
- 流:数据在数据源(文件)和程序(内存)之间经历的路径
- 输入流:数据从数据源(文件)到程序(内存)的路径
- 输出流:数据从程序(内存)到数据源(文件)的路径
os.File封装了所有文件相关的操作,File是一个结构体
文件的使用
1)打开一个文件进行读写操作
os.Open(name string) (*File, error)
2)关闭一个文件
File.Close()
3)其它
file对象、file指针、file文件句柄 //3种叫法
打开和关闭文件
- 文件是一个指针
func Open
func Open(name string) (file *File, err error)
Open打开一个文件用于读取
。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有O_RDONLY模式。如果出错,错误底层类型是*PathError。
func (*File) Close
func (f *File) Close() error
Close关闭文件f
,使文件不能用于读写。它返回可能出现的错误。
案例
package main
import (
"fmt"
"os"
)
func main() {
//file对象、file指针、file文件句柄
var filePath string = "D:/axis.log"
file, err := os.Open(filePath)
if err != nil {
fmt.Println("文件打开error:", err)
return
}
//打开文件
fmt.Println(file) //&{0xc000100780} file就是一个指针
//关闭文件
error := file.Close()
if error != nil {
fmt.Println("关闭文件error:", error)
}
}
带缓冲的文件读取
1)读取文件的内容并显示在终端
(带缓冲区的方式,适用于大文件
),使用os.Open, file.Close(),
bufio.NewReader(), reader.ReaderString 函数和方法
- 缓冲:
- 读一部分,处理一部分
- 适用于所有大文件
代码
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
file, error := os.Open("D:/axis.log")
if error != nil {
fmt.Println("文件打开失败,", error)
return
}
defer file.Close()//延时机制
//创建一个指针内型的reader
//默认缓冲4096字节,读一部分,取一部分=缓冲
reader := bufio.NewReader(file)
for {
str, err := reader.ReadString('\n')
if err == io.EOF {
fmt.Println("已达文件末尾")
break
}
if err != nil {
fmt.Println("读取过程中出现错误,", err)
break
}
fmt.Print(str)
}
fmt.Println("文件读取结束...")
}
结果
2020-08-02 16:00:24,505 0 [ main] INFO ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@fb434: startup date [Sun Aug 02 16:00:24 CST 2020]; root of context hierarchy
2020-08-02 16:00:24,571 66 [ main] INFO ry.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [applicationContext.xml]
已达文件末尾
文件读取结束...
一次性的文件读取
- 适用于小文件
io/ioutil包下
func ReadFile
func ReadFile(filename string) ([]byte, error)
ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF。因为本函数定义为读取整个文件
,它不会将读取返回的EOF视为应报告的错误。
代码
package main
import (
"fmt"
"io/ioutil"
)
func main() {
bytes, error := ioutil.ReadFile("d:\axis.log")
if error != nil {
fmt.Println("文件读取出错,", error)
return
}
fmt.Println(string(bytes))
}
结果
2020-08-02 16:00:24,505 0 [ main] INFO ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@fb434: startup date [Sun Aug 02 16:00:24 CST 2020]; root of context hierarchy
2020-08-02 16:00:24,571 66 [ main] INFO ry.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [applicationContext.xml]
结论
- ioutil.ReaderFile(path string) 不带缓冲,只适用于小文件
写文件操作
Constants
const (
O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件
O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件
O_RDWR int = syscall.O_RDWR // 读写模式打开文件
O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部
O_CREATE int = syscall.O_CREAT // 如果不存在将创建一个新文件
O_EXCL int = syscall.O_EXCL // 和O_CREATE配合使用,文件必须不存在
O_SYNC int = syscall.O_SYNC // 打开文件用于同步I/O
O_TRUNC int = syscall.O_TRUNC // 如果可能,打开时清空文件
)
func OpenFile
func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
OpenFile是一个更一般性的文件打开函数,大多数调用者都应用Open或Create代替本函数。它会使用指定的选项(如O_RDONLY等)、指定的模式(如0666等)打开指定名称的文件。如果操作成功,返回的文件对象可用于I/O。如果出错,错误底层类型是*PathError。
name是文件
flag是打开的方式
FileMode只在Unix、Linux有效
写文件新建01
- 创建一个新文件,写入内容 5句"hello go"
- os.Open
- 使用带缓存的方式:bufio.NewWriter()
代码
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
filePath := "d:/carter.txt"
file, error := os.OpenFile(filePath, os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0666)
if error != nil {
fmt.Println("文件打开失败,", error)
return
}
defer file.Close()
//准备写入的内容
str := "hello go"
//带缓冲的写入
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
//有些不能识别\n换行符
writer.WriteString(str + strconv.Itoa(i) + "\r\n")
}
//writer是带缓存的
//内容先写入缓存,使用Flush真正写入文件中
//否则文件没有数据,数据还在缓存中
writer.Flush()
fmt.Println("文件写入成功...")
}
结果
hello go0
hello go1
hello go2
hello go3
hello go4
结论
- bufio.NewWriter是带缓存的
- 内容先写入缓存,并没有写到数据源(文件)
- 使用Flush( ),真正写入文件中
写文件覆盖02
1)已存在的文件,进行覆盖
- 思路
- 只写+清空:
os.O_WRONLY | os.O_TRUNC
- 换行:
\r\n
- 只写+清空:
写文件追加03
1)原先的文件上追加文字
- 思路
- 只写+追加:
os.O_WRONLY | os.O_APPEND
- 换行:
\r\n
- 只写+追加:
读写文件追加04
1)打开一个存在的文件,将原来的文件读出显示在终端,并追加5句"hello world"
- 思路
- 只读+追加:
os.O_RDONLY | os.O_APPEND
- 换行:
\r\n
- 只读+追加:
代码
package main
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
)
func showFile (file *os.File) {
fmt.Println("文件读取中...")
//读取源文件类型
reader := bufio.NewReader(file)
for {
str, err := reader.ReadString('\n')
if err == io.EOF {
break
}
//显示在终端
fmt.Print(str)
}
}
func main() {
filePath := "d:/carter.txt"
file, error := os.OpenFile(filePath, os.O_RDONLY | os.O_APPEND, 0666)
if error != nil {
fmt.Println("文件打开失败,", error)
return
}
defer file.Close()
//显示文件
showFile(file)
//准备写入的内容
str := "hello world"
//带缓冲的写入
writer := bufio.NewWriter(file)
for i := 1; i < 5; i++ {
writer.WriteString(str + strconv.Itoa(i) + "\r\n")
}
//writer是带缓存的
//内容先写入缓存,使用Flush真正写入文件中
//否则文件没有数据,数据还在缓存中
writer.Flush()
fmt.Println("OK~")
}
文件之间的读写
1)编写一个程序,将一个文件的内容,写入到另一个文件中。
注意:这两个文件已经存在
说明:
使用ioutil.ReadFile 、 ioutil.WriteFile完成
- 都是一次性的
案例2
- 文件1—>文件2
- 读:
iouilt.ReadFile
[一次性] - 打开:
os.OpenFile()
[带缓存] - 关闭:
file.Close()
- 写+追加:
os.O_WRONLY | os.O_APPEND
bufio.NewWriter(name string)
[带缓存]- 刷新缓存:
writer.Flush()
- 读:
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
)
func main() {
file1Path := "d:/axis.log"
file2Path := "d:/carter.txt"
//1、读取文件1
//ioutil中是一次性的,不需要关闭文件
data, error1 := ioutil.ReadFile(file1Path)
if error1 != nil {
fmt.Println("file1文件读取有误,error=", error1)
return
}
//2、打开文件2(写+追加)
//os中是带缓存的,需要Close()
file2, error2 := os.OpenFile(file2Path, os.O_WRONLY | os.O_APPEND, 0666)
if error2 != nil {
fmt.Println("文件打开出错,", error2)
return
}
defer file2.Close()//关闭文件资源
//3、写文件
writer := bufio.NewWriter(file2)
length, error3 := writer.Write(data)
fmt.Println("length:", length)
if error3 != nil {
fmt.Println("写入过程有误,", error3)
return
}
//4、刷新缓存
writer.Flush()
fmt.Println("成功写入到carter.txt中")
}
文件是否存在?
os.Stat()
函数- 返回的error是nil,说明文件(夹)是存在的
- 返回的error是IsNotExist( ),说明文件(夹)是不存在的
- 返回的是其它类型,说明文件(夹)不确定是否存在
文件是否存在的函数
package main
import (
"fmt"
"os"
)
func FileExists(filePath string) (bool, error) {
fileInfo, error := os.Stat(filePath)
if error == nil {
if fileInfo.IsDir() {
fmt.Println("这是个文件夹...")
}
return true, nil
}
if os.IsNotExist(error) {
return false, nil
}
return false, error
}
func main() {
flag, err := FileExists("D:/222s")
if flag {
fmt.Println("文件存在...")
} else if err == nil {
fmt.Println("文件不存在...")
} else {
fmt.Println(err)
}
}
拷贝图片
如果文件不存在,自己创建
func Copy
func Copy(dst Writer, src Reader) (written int64, err error)
将src的数据拷贝到dst,直到在src上到达EOF或发生错误。返回拷贝的字节数和遇到的第一个错误。
对成功的调用,返回值err为nil而非EOF,因为Copy定义为从src读取直到EOF,它不会将读取到EOF视为应报告的错误。如果src实现了WriterTo接口,本函数会调用src.WriteTo(dst)进行拷贝;否则如果dst实现了ReaderFrom接口,本函数会调用dst.ReadFrom(src)进行拷贝。
代码
package main
import (
"bufio"
"fmt"
"io"
"os"
)
//编写函数接收两个文件路径
func CopyFile(dstFileName string, srcFileName string) (written int64, err error) {
srcFile, srcError := os.Open(srcFileName)
if srcError != nil {
fmt.Println("Error opening file,", srcError)
return
}
defer srcFile.Close()//戒得关闭句柄
//通过srcFile获取到Reader
reader := bufio.NewReader(srcFile)
//只写+创建
dstFile, dstError := os.OpenFile(dstFileName, os.O_WRONLY | os.O_CREATE, 0666)
if dstError != nil {
fmt.Println("Error opening file,", dstError)
return
}
defer dstFile.Close()//防止内存泄露
//通过dstFileName获取到writer
writer := bufio.NewWriter(dstFile)
return io.Copy(writer, reader)//目标文件writer+源文件reader
}
func main() {
srcFile := "C:/Users/Pictures/4K壁纸/美女.jpg"
dstFile := "C:/Users/Desktop/abc.jpg"
_, error := CopyFile(dstFile, srcFile)
if error != nil{
fmt.Println("Error Coping file,", error)
} else {
fmt.Println("Copy completed...")
}
}
统计字符类型
1)统计英文、数字、空格和其它字符数量
说明:
统计一个文件中含有的英文、数字、空格及其它字符数量
- 思路
- 打开一个文件
- 创建带缓存的Reader
- 每Reader一行进行一次统计
- 将结果保存到结构体中
代码
package main
import (
"bufio"
"fmt"
"io"
"os"
)
type CharCount struct {
EnglishCount int
NumberCount int
SpaceCount int
OtherCount int
}
func main() {
filePath := "d:/carter.txt"
file, error := os.Open(filePath)
defer file.Close()
if error != nil {
fmt.Println("Error opening file,", error)
return
}
var charCount CharCount
reader := bufio.NewReader(file)
//循环读取文件内容
for {
str, err := reader.ReadString('\n')//换行符为准,进行分隔
if err == io.EOF {
break
}
if err != nil {
fmt.Println("文件读取错误...")
break
}
//兼容中文字符
strSlice := []rune(str)
for _, value := range strSlice {
switch {//注意:这个地方不要key,分支结构
case value >= 'a' && value <= 'z':
charCount.EnglishCount++
case value >= 'A' && value <= 'Z':
charCount.EnglishCount++
case value == ' ' || value == '\t':
charCount.SpaceCount++
case value > '0' && value < '9':
charCount.NumberCount++
default:
charCount.OtherCount++
}
}
}
fmt.Println("字母个数:", charCount.EnglishCount)
fmt.Println("数字个数:", charCount.NumberCount)
fmt.Println("空格个数:", charCount.SpaceCount)
fmt.Println("其它字符个数:", charCount.OtherCount)
}
结果
字母个数: 4
数字个数: 4
空格个数: 4
其它字符个数: 5
结论
注意switch的分支用法
Args解析命令行参数
我们希望能够获取密令行输入的各种参数,该如何处理?
os.Args
是一个string切片,按空格统计
代码
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println("参数个数:", len(os.Args))
for index, value := range os.Args {
fmt.Printf("args[%v]=%v\n", index, value)
}
}
运行
D:\Work\Goland\Go\src\showComannd\main>go build -o test.exe main.go
D:\Work\Goland\Go\src\showComannd\main>test.exe d:/aaa/bbb/axis.log 88
参数个数: 3
args[0]=test.exe
args[1]=d:/aaa/bbb/axis.log
args[2]=88
flag包解析命令行参数
模板:
mysql -u carter -p 1234 -h localhost -P 3306
代码
package main
import (
"flag"
"fmt"
)
func main() {
var username string
var password string
var host string
var port int
flag.StringVar(&username, "u", "", "获取用户名,默认为空")
flag.StringVar(&password, "p", "", "获取密码,默认为空")
flag.StringVar(&host, "h", "localhost", "主机默认是localhost")
flag.IntVar(&port, "P", 3306, "端口默认3306")
//必须调用该方法
flag.Parse()
//输出结果
fmt.Printf("username=%v\n", username)
fmt.Printf("password=%v\n", password)
fmt.Printf("host=%v\n", host)
fmt.Printf("port=%v\n", port)
}
结果
D:\Work\Goland\Go\src\showComannd\flag\main>main.exe -u carter -p 1122
username=carter
password=1122
host=localhost
port=3306
Golang Json篇
Json 基本介绍
1)JSON(JavaScript Object Notation)是一种轻量级的数据交换格式
2)JSON在2001年开始推广使用,是现如今的主流的数据格式
3)JSON易于机器解析和生成,并有效的提升网络传输效率
4)通常在网络传输时会将数据(结构体、map等序列化成json字符串,到接收方得到json字符串时,再反序列化,恢复成原来的数据类型
Json数据格式说明
在JS语言中,一切都是对象。
Json序列化的使用
- 介绍
Json序列化是指,将有key-value结构的数据类型(比如结构体、map、切片)序列化成json字符串的操作
- 应用案例
将结构体、map和切片的序列化
- 代码演示
package main
import (
"encoding/json"
"fmt"
)
// Define a structure
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
Birthday string `json:"birthday"`
Salary float64 `json:"salary"`
Skill string `json:"skill"`
}
func testStruct() {
var stu Student = Student{
Name: "孙悟空",
Age: 700,
Birthday: "1211-11-12",
Salary: 10000.50,
Skill: "筋斗云",
}
// Serialization
bytes, error := json.Marshal(&stu)
if error != nil {
fmt.Println("Serialization error,error=", error)
return
}
fmt.Println("Student序列化后的结果:", string(bytes))
}
func testMap() {
// 1、Define a map
var map01 map[string]interface{}
// 2、Use map ,need to make
map01 = make(map[string]interface{})
map01["name"] = "唐僧"
map01["sex"] = "男"
map01["address"] = "东土大唐"
data, error := json.Marshal(map01)
if error != nil {
fmt.Println("Serialization error,error=", error)
return
}
fmt.Println("map序列化后的结果:", string(data))
}
// Slice contains multiple maps
func testSlice() {
var slice []map[string]interface{}
var map1 map[string]interface{}
// Need to make before use
map1 = make(map[string]interface{})
map1["name"] = "carter"
map1["age"] = 18
map1["sex"] = "man"
var map2 map[string]interface{}
map2 = make(map[string]interface{})
map2["name"] = "廖述幸"
map2["age"] = 20
map2["sex"] = "男"
slice = append(slice, map1, map2)
// Slice serialization
data, error := json.Marshal(slice)
if error != nil {
fmt.Println("Serialization error,error=", error)
return
}
fmt.Println("slice序列化后的结果:", string(data))
}
func main() {
// 1、Structure serialization
testStruct()
// 2、Map serialization
testMap()
// 3、Slice serialization
testSlice()
}
- 结果
Student序列化后的结果: {"name":"孙悟空","age":700,"birthday":"1211-11-12","salary":10000.5,"skill":"筋斗云"}
map序列化后的结果: {"address":"东土大唐","name":"唐僧","sex":"男"}
slice序列化后的结果: [{"age":18,"name":"carter","sex":"man"},{"age":20,"name":"廖述幸","sex":"男"}]
基本数据类型Json序列化
- 序列化后是本身,意义不大
- 代码演示
package main
import (
"encoding/json"
"fmt"
)
func testBasicDataTypes() {
var num float64 = 123.45
data, error := json.Marshal(num)
if error != nil {
fmt.Println("Serialization error,error=", error)
return
}
fmt.Println("num float64序列化后的结果:", string(data))
}
func main() {
// Serialization of basic data types
testBasicDataTypes()
}
- 结果
num float64序列化后的结果: 123.45
反序列化
基本介绍
- 介绍
Json反序列化是指:将Json字符串反序列化成对应的数据类型(比如结构体、map、切片)的操作
Json反序列化成 结构体、map、切片
- 注意
- 反序列化前后类型一致
- Unmarshal中封装了make操作
- json.Unmarshal([]byte(“序列化的值”), &类型)
代码
package main
import (
"encoding/json"
"fmt"
)
// Define a structure
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
Birthday string `json:"birthday"`
Salary float64 `json:"salary"`
Skill string `json:"skill"`
}
func unMarshalStruct() {
str := `{"name":"孙悟空","age":700,"birthday":"1211-11-12","salary":10000.5,"skill":"筋斗云"}`
//define a Student instance
var stu Student
// Parameter is pointer
error := json.Unmarshal([]byte(str), &stu)
if error != nil {
fmt.Println("Struct反序列化失败,error", error)
return
}
fmt.Println("Struct反序列化的结果:", stu)
}
func unMarshalMap() {
map01 := `{"address":"东土大唐","name":"唐僧","sex":"男"}`
//Need not to make, Unmarshal has make operate
var mapResult map[string]interface{}
error := json.Unmarshal([]byte(map01), &mapResult)
if error != nil {
fmt.Println("Map反序列化失败,error", error)
return
}
fmt.Println("Map反序列化的结果:", mapResult)
}
func unMarshalSlice() {
slice := `[{"age":18,"name":"carter","sex":"man"},{"age":20,"name":"廖述幸","sex":"男"}]`
//Need not to make, Unmarshal has make operate
var sliceResult []map[string]interface{}
error := json.Unmarshal([]byte(slice), &sliceResult)
if error != nil {
fmt.Println("Slice反序列化失败,error", error)
return
}
fmt.Println("Slice反序列化的结果:", sliceResult)
}
func main() {
// 1、Unmarshal structure
unMarshalStruct()
// 2、Unmarshal map
unMarshalMap()
// 3、Unmarshal slice
unMarshalSlice()
}
结果
Struct反序列化的结果: {孙悟空 700 1211-11-12 10000.5 筋斗云}
Map反序列化的结果: map[address:东土大唐 name:唐僧 sex:男]
Slice反序列化的结果: [map[age:18 name:carter sex:man] map[age:20 name:廖述幸 sex:男]]