一、文件基本介绍
1.1、打开一个文件
基本介绍:打开一个文件用于读取,如果操作成功,返回的文件对象的方法可用于读取文件数据。如果出错,错误底层类型是"*.PathError"
func Open(name string) (*File, error)
name string:打开的文件路径
*File:返回值1,文件对象
error:返回值2,错误err
1.2、关闭一个文件
基本介绍:语法如下
func (f *File) Close() error
(f *File) Close():文件对象的Close方法
error:返回值1,错误err
打开文件,关闭文件,快速入门案例:
package main import ( "fmt" "os" ) func main() { // 打开一个文件(默认有2个返回值:文件对象,错误) file, err:=os.Open("sudada.log") // 如果err有值,则输出错误 if err != nil { fmt.Println("打开文件失败,错误:",err) } // 通过文件对象file的值 fmt.Println(file) // 返回值:&{0xc000100a00} fmt.Println(file.Name()) // 返回值:sudada.log // 关闭文件(默认有1个返回值:错误) close_err := file.Close() if close_err != nil { fmt.Println("打开文件失败,错误:",close_err) } }
1.3、读文件内容
1.读取文件内容并显示在终端(带缓冲区的方式):使用bufio.NewReader(),reader.ReadString函数和方法。
步骤:先打开文件,然后读文件,最后关闭文件
package main import ( "bufio" "fmt" "io" "os" ) func main() { // 打开一个文件(有2个返回值:文件对象,错误) file, err:=os.Open("sudada.log") // 如果err有值,则输出错误 if err != nil { fmt.Println("打开文件失败,错误:",err) } // 在函数要退出时,关闭文件 defer file.Close() // 创建一个 *Reader,是带缓冲的(默认4096字节) reader := bufio.NewReader(file) // 循环读取文件的内容: reader.ReadString (有2个返回值:文件内容,错误) for { str,err := reader.ReadString('\n') // 读到"换行"就结束 // 读到文件结尾时,就break if err == io.EOF { break } // 打印读取到的文件内容 fmt.Print(str) // 返回值:hello world // 返回值:sudada // 返回值:beijing } }
2.读取文件内容并显示在终端(使用ioutil一次将整个文件读入到内存中),这种方式适合小文件:使用ioutil.ReadFile函数
步骤:一次将文件读取到位
package main import ( "fmt" "io/ioutil" ) func main() { // 使用ioutil.ReadFile一次性将文件读取到位 file := "sudada.log" content,err := ioutil.ReadFile(file) if err != nil { fmt.Println(err) } // 显示读取的内容 fmt.Printf("%v",string(content)) // hello world // sudada // beijing // shanghai }
1.4、常用的4种写文件方式
基本介绍:语法如下
func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
name string:打开的文件
flag int:文件打开的模式
os.O_WRONLY 只写模式
os.O_RDWR 读写模式
os.O_RDONLY 只读模式
os.O_CREATE 如果文件不存在则创建
os.O_EXCL 和os.O_CREATE配合使用,文件必须不存在
os.O_SYNC 打开文件用于同步IO
os.O_TRUNC 如果可能,打开是清空文件os.O_APPEND 追加内容
perm FileMode:文件的权限控制(linux)
file *File:返回值1,文件对象
err error:返回值2,错误err
快速入门案例:
1.创建一个新文件,写入内容:hello world(模式 os.O_WRONLY | os.O_CREATE )
package main import ( "bufio" "fmt" "os" ) func main() { // 创建一个新文件,写入内容:hello world // 1.文件名"test.log" filename := "test.log" // 打开文件"test.log" file,err :=os.OpenFile(filename, os.O_WRONLY | os.O_CREATE, 0666) if err != nil { fmt.Println(err) return } // 2.写入到文件的内容(\r\n表示换行) str:="hello world sudada \r\n" // 写入文件时,使用带缓存的 *Writer writer:=bufio.NewWriter(file) writer.WriteString(str) // writer带缓存,在调用WriteString方法写入时,需要flush到磁盘。 writer.Flush() // 3.关闭文件句柄 defer file.Close() }
2.打开一个存在的文件,覆盖文件的内容(模式 os.O_WRONLY | os.O_TRUNC )
package main import ( "bufio" "fmt" "os" ) func main() { // 1.文件名"test.log" filename := "test.log" // 打开文件"test.log" file,err :=os.OpenFile(filename, os.O_WRONLY | os.O_TRUNC, 0666) if err != nil { fmt.Println(err) return } // 2.写入到文件的内容(\r\n表示换行) str:="hello world sudada \r\n" // 写入文件时,使用带缓存的 *Writer writer:=bufio.NewWriter(file) writer.WriteString(str) // writer带缓存,在调用WriteString方法写入时,需要flush到磁盘。 writer.Flush() // 3.关闭文件句柄 defer file.Close() }
3.打开一个存在的文件,追加内容(模式 os.O_WRONLY | os.O_APPEND )
package main import ( "bufio" "fmt" "os" ) func main() { // 1.文件名"test.log" filename := "test.log" // 打开文件"test.log" file,err :=os.OpenFile(filename, os.O_WRONLY | os.O_APPEND, 0666) if err != nil { fmt.Println(err) return } // 2.写入到文件的内容(\r\n表示换行) str:="hello world new \r\n" // 写入文件时,使用带缓存的 *Writer writer:=bufio.NewWriter(file) writer.WriteString(str) // writer带缓存,在调用WriteString方法写入时,需要flush到磁盘。 writer.Flush() // 3.关闭文件句柄 defer file.Close() }
4.打开一个存在的文件,读取原来的内容,然后在追加内容(模式 os.O_RDWR | os.O_APPEND )
package main import ( "bufio" "fmt" "io" "os" ) func main() { // 1.文件名"test.log" filename := "test.log" // 打开文件"test.log" file,err :=os.OpenFile(filename, os.O_RDWR | os.O_APPEND, 0666) if err != nil { fmt.Println(err) return } // 2.先读文件 reader:=bufio.NewReader(file) for{ str,err := reader.ReadString('\n') // 如果读取到文件的末尾,就break if err == io.EOF { break } // 显示文件已有的内容 fmt.Print(str) } // 3.然后再追加文件内容 str:="hello shanghai \r\n" // 写入文件时,使用带缓存的 *Writer writer:=bufio.NewWriter(file) writer.WriteString(str) // writer带缓存,在调用WriteString方法写入时,需要flush到磁盘。 writer.Flush() // 4.关闭文件句柄 defer file.Close() }
5.将文件a复制到文件b(使用ioutil.ReadFile() 和 ioutil.WriteFile()方法)
package main import ( "fmt" "io/ioutil" ) func main() { file_test := "test.log" file_new_test := "new_test.log" // 1.读文件"test.log"的内容 data, read_err := ioutil.ReadFile(file_test) if read_err != nil { fmt.Println("read file test.log error: ",read_err) return } // 2.将"test.log"的文件内容写到"new_test.log" write_err := ioutil.WriteFile(file_new_test, data, 0666) if write_err != nil { fmt.Println("writer file new_test.log error: ",write_err) return } }
1.5、判断文件或目录是否存在
os.Stat(file_path) 方法的返回值说明:
1.返回的错误为nil,说明文件或文件夹存在
2.返回的错误使用os.IsNotExist()为true,说明文件或文件夹不存在
3.返回的错误为其他类型,则不确定是否存在package main import ( "fmt" "os" ) func main() { file_path := "test.log" _, err := os.Stat(file_path) // os.Stat(file_test) 的用法: // 1.返回的错误为nil,说明文件或文件夹存在 // 2.返回的错误使用os.IsNotExist()为true,说明文件或文件夹不存在 // 3.返回的错误为其他类型,则不确定是否存在 // err == nil 时,文件存在 if err == nil { fmt.Printf("file %v exist !", file_path) } // os.IsNotExist()为true时,文件不存在 if os.IsNotExist(err) { fmt.Printf("file %v is not exist !", file_path) } } // 封装的一个函数判断文件是否存在 func PathExist(path string) (bool, error) { _, err := os.Stat(path) // os.Stat(file_test) 的用法 // 1.返回的错误为nil,说明文件或文件夹存在 // 2.返回的错误使用os.IsNotExist()为true,说明文件或文件夹不存在 // 3.返回的错误为其他类型,则不确定是否存在 // err == nil 时,文件存在 if err == nil { return true, nil } // os.IsNotExist()为true时,文件不存在 if os.IsNotExist(err) { return false, err } return false, err } func main() { file_path := "1test.log" flg, msg:=PathExist(file_path) if flg { fmt.Printf("file %v exist !", file_path) } else { fmt.Printf("file %v is not exist, msg: %v\n", file_path, msg) } }
1.6、文件拷贝
Copy函数是io包提供的
package main import ( "bufio" "fmt" "io" "os" ) func CopyFile(dstFileName string, srcFileName string)(written int64, err error) { // 获取源文件 srcFile,err := os.Open(srcFileName) if err != nil { fmt.Println("open file error: ",err) } // 关闭文件句柄 defer srcFile.Close() // 通过srcFile,获取到Reader reader := bufio.NewReader(srcFile) // 打开目标文件 dstFile, err := os.OpenFile(dstFileName,os.O_WRONLY | os.O_CREATE,0666) if err != nil { fmt.Println("open file error: ",err) return } writer := bufio.NewWriter(dstFile) // 关闭文件句柄 defer dstFile.Close() // 通过io.Copy将源文件copy到目标文件 return io.Copy(writer, reader) } func main() { // 将文件funingla.png, copy到fufu.png srcFileName := "funingla.png" dstFileName := "fufu.png" _,err := CopyFile(dstFileName, srcFileName) if err == nil { fmt.Println("copy success") } else { fmt.Println("error: ",err) } }
二、命令行参数
2.1、获取命令行参数
方法1:os.Args是一个string切片,用来存储所有的命令行参数。
package main import ( "fmt" "os" ) func main() { fmt.Println("命令行的参数有",len(os.Args)) // 编译os.Args切片就拿到所有的命令行参数 for i,v := range os.Args { fmt.Printf("args[%v]=%v\n",i,v) } // 执行命令(空格分隔):go run main.go test aa abc // 第1个参数(文件名):main.exe // 第2个参数(实际的参数1):test // 第3个参数(实际的参数2):aa // 第4个参数(实际的参数3):abc }
方法2:flag包解析命令行参数
package main import ( "flag" "fmt" ) func main() { var user string var port int var pwd string var host string // &user:接收输入的"-u"的值,默认值为"",参数解释:"用户名" flag.StringVar(&user,"u","","用户名") // &port:接收输入的"-port"的值,默认值为3306,参数解释:"端口,默认为3306" flag.IntVar(&port,"port",3306,"端口,默认为3306") // &pwd:接收输入的"-p"的值,默认值为"",参数解释:"端口,默认为空" flag.StringVar(&pwd,"p","","密码,默认为空") // &host:接收输入的"-h"的值,默认值为"localhost",参数解释:"主机地址,默认为localhost" flag.StringVar(&host,"h","localhost","主机地址,默认为localhost") // 必须转换 flag.Parse() // 输出结果 fmt.Printf("user=%v,port=%v,pwd=%v,host=%v\n",user,port,pwd,host) // 执行命令(必须以空格分隔):go run main.go -u root -p asd!@#%^&( -port 3306 -h 127.0.0.1 // 返回结果:user=root,port=3306,pwd=asd!@#%&(,host=127.0.0.1 }
三、Json序列化
序列化快速入门:data, err := json.Marshal(monster)
data == 拿到序列化后的json格式数据
monster == 被序列化的格式(结构体,map,切片)
3.1、结构体-序列化
package main import ( "encoding/json" "fmt" ) type Monster struct { Name string Age int Birthday string Skill string } // 结构体Json序列化 func testStruct() { var monster = Monster{ Name:"sudada", Age:18, Birthday:"2000-06-11", Skill:"牛牛冲击", } data, err := json.Marshal(monster) if err != nil { fmt.Println("序列化失败:",err) } // struct序列化结果 fmt.Println(string(data)) // 返回值(key的值被大写了,因为结构体的字段是大写的):{"Name":"sudada","Age":18,"Birthday":"2000-06-11","Skill":"牛牛冲击"} } func main() { testStruct() }
结构体json序列化时tag的使用(可以将结构体的字段小写)
package main import ( "encoding/json" "fmt" ) type Monster struct { name string `json:"name"` // 反射机制 Age int `json:"age"` // 反射机制 Birthday string `json:"birthday"` // 反射机制 Skill string `json:"skill"` // 反射机制 } // 结构体Json序列化 func testStruct() { var monster = Monster{ name: "sudada", Age:18, Birthday:"2000-06-11", Skill:"牛牛冲击", } // 结构体字段不能小写,因为在json包内无法识别小写的"结构体的字段名" // 如果"结构体字段小写"且进行序列化,那么"序列化后的数据"不包含这个"小写字段"的值 // 这里小写name字段为例,序列化后得到的值:{"age":18,"birthday":"2000-06-11","skill":"牛牛冲击"} data, err := json.Marshal(monster) if err != nil { fmt.Println("序列化失败:",err) } // struct序列化结果 fmt.Println(string(data)) // 返回值(字段小写):{"name":"sudada","age":18,"birthday":"2000-06-11","skill":"牛牛冲击"} } func main() { testStruct() }
3.2、map-序列化
package main import ( "encoding/json" "fmt" ) // map序列化 func mapTest() { var a map[string]interface{} a = make(map[string]interface{}) a["name"]="sudada" a["age"]="18" a["skill"]="丢丢" // map序列化 data, err := json.Marshal(a) if err != nil { fmt.Println("序列化失败:",err) } fmt.Println(string(data)) // 返回值:{"age":"18","name":"sudada","skill":"丢丢"} } func main() { mapTest() }
3.3、切片-序列化
package main import ( "encoding/json" "fmt" ) // 切片序列化 func testSlice() { // 切片内有多个map var slice []map[string]interface{} var m1 map[string]interface{} m1 = make(map[string]interface{}) m1["name"]="sudada" m1["age"]="18" m1["skill"]="丢丢" slice = append(slice, m1) fmt.Println(slice) // 返回值:[map[age:18 name:sudada skill:丢丢]] // 切片序列化 data, err := json.Marshal(slice) if err != nil { fmt.Println("序列化失败:",err) } fmt.Println(string(data)) // 返回值:[{"age":"18","name":"sudada","skill":"丢丢"}] } func main() { testSlice() }
3.4、反序列化(将json格式的数据,转换为"结构体,map,切片")
在反序列化一个json数据时,要确保反序列化后的数据类型和原来序列化前的数据类型一致。
反序列化快速入门:err := json.Unmarshal([]byte(str),&slice)
str == json格式的数据
slice == 被反序列化的格式(结构体,map,切片)
3.4.1、结构体-反序列化
json数据的key,需要和结构体字段保持完全一致
package main import ( "encoding/json" "fmt" ) type Monster struct { Name string Age int Birthday string Skill string } func test() { // 结构体变量 var monster Monster // 将json格式的数据,反序列化为struct str := "{\"name\":\"sudada\",\"age\":18,\"birthday\":\"2000-06-11\",\"skill\":\"牛牛冲击\"}" // 反序列化 err := json.Unmarshal([]byte(str), &monster) if err != nil { fmt.Println("反序列化失败",err) } fmt.Println(monster) // 返回值:{sudada 18 2000-06-11 牛牛冲击} } func main() { test() }
3.4.2、map-反序列化
package main import ( "encoding/json" "fmt" ) func main() { // 将json格式的数据,反序列化为map str := "{\"age\":\"18\",\"name\":\"sudada\",\"skill\":\"丢丢\"}" // 定义map var a map[string]interface{} // 反序列化map时,不需要make,因为make操作在Unmarshal函数内已经自动执行了 err := json.Unmarshal([]byte(str),&a) if err != nil { fmt.Println("反序列化失败",err) } fmt.Println(a) // 返回值:map[age:18 name:sudada skill:丢丢] }
3.4.2、切片-反序列化
package main import ( "encoding/json" "fmt" ) func main() { // 将json格式的数据,反序列化为切片 str := "[{\"age\":\"18\",\"name\":\"sudada\",\"skill\":\"丢丢\"}]" // 定义切片 var slice []map[string]interface{} // 反序列化切片 err := json.Unmarshal([]byte(str),&slice) if err != nil { fmt.Println("反序列化失败",err) } fmt.Println(slice) // 返回值:[map[age:18 name:sudada skill:丢丢]] }
四、反射
4.1、什么是反射?
1.反射可以在运行时,动态获取变量的各种信息,比如变量的类型type,类别kind;
2.如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段,方法);
3.通过反射,可以修改变量的值,可以调用关联的方法;
4.使用反射,需要import("reflect");4.2、反射重要的函数和概念
1.reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型
2.reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型,reflect.Value是一个结构体类型。
3.变量,interface{}和reflect.Value是可以相互转换的,实际开发会经常用到。3.1、如何将interface{}转成reflect.Value
rValue:=reflect.ValueOf(变量名)3.2、如何将reflect.Value转成interface{}
rVal:=rValue.Interface()3.3、如何将interface{}转成原来的变量类型
v:=rVal.(Stu)4.3、反射的快速入门
4.3.1、基本数据类型,interface{},reflect.Value进行反射的基本操作
package main import ( "fmt" "reflect" ) func test(b interface{}) { // 通过"反射"获取变量b的reflect.Type 类型 rType := reflect.TypeOf(b) fmt.Println(rType) // 返回值:string // 查看通过反射获取到的数据"rType"本身的类型 fmt.Printf("%T\n",rType) // 返回值:*reflect.rtype // 通过"反射"获取变量b的reflect.Value 类型 rValue := reflect.ValueOf(b) fmt.Println(rValue) // 返回值:sudada // 查看通过反射获取到的数据"rValue"本身的类型 fmt.Printf("%T\n",rValue) // 返回值:reflect.Value // 通过"反射"获取变量b的reflect.Kind 类别 rKind:=rType.Kind() // rValue.Kind() == rType.Kind() fmt.Println(rKind) // 返回值:string // 查看通过反射获取到的数据"rKind"本身的类型 fmt.Printf("%T\n",rKind) // 返回值:reflect.Kind // rValue.String()拿到的是,形参"b"原本的值(把name:="sudada"传入给b时,rValue.String()拿到的就是"sudada"这个值) newName:=rValue.String() fmt.Printf("%T\n",rValue.String()) // 返回值:string fmt.Println(newName) // 返回值:sudada // 将rValue转成interface{} iv:=rValue.Interface() fmt.Printf("%T\n",iv) // 返回值:string fmt.Println("iv",iv) // 返回值:iv sudada // 将interface{}通过断言转成变量原本的类型 name2:=iv.(string) fmt.Printf("%T\n",name2) // 返回值:string fmt.Println("name2",name2) // 返回值:name2 sudada } func main() { name := "sudada" test(name) }
4.3.2、结构体类型,interface{},reflect.Value进行反射的基本操作
package main import ( "fmt" "reflect" ) type Student struct { Name string Age int } func test(b interface{}) { // 通过"反射"获取变量b的reflect.Type 类型 rType := reflect.TypeOf(b) fmt.Println(rType) // 返回值:main.Student // 查看通过反射获取到的数据"rType"本身的类型 fmt.Printf("%T\n",rType) // 返回值:*reflect.rtype // 通过"反射"获取变量b的reflect.Value 类型 rValue := reflect.ValueOf(b) fmt.Println(rValue) // 返回值:{sudada 18} // 查看通过反射获取到的数据"rValue"本身的类型 fmt.Printf("%T\n",rValue) // 返回值:reflect.Value // 通过"反射"获取变量b的reflect.Kind 类别 rKind:=rType.Kind() // rValue.Kind() == rType.Kind() fmt.Println(rKind) // 返回值:struct // 查看通过反射获取到的数据"rKind"本身的类型 fmt.Printf("%T\n",rKind) // 返回值:reflect.Kind // 将rValue转成interface{} iv:=rValue.Interface() fmt.Printf("%T\n",iv) // 返回值:main.Student fmt.Println(iv) // 返回值:{sudada 18} // 这里不能通过iv.Age,iv.Name取到值 // 将interface{}通过断言转成变量原本的类型 stu:=iv.(Student) fmt.Printf("%T\n",stu) // 返回值:main.Student fmt.Println(stu.Age) // 返回值:18 fmt.Println(stu.Name) // 返回值:sudada } func main() { stu := Student{ Name: "sudada", Age: 18, } test(stu) }
4.4、反射的注意事项
1.reflect.Value.Kind,获取变量的类型,获取到的是一个常量;
2.Type是类型,Kind是类别,Type和Kind可能是相同的,也可能是不同的;
3.通过反射,可以让变量在interface{}和reflect.Value之间相互转换(变量<-->interface{}<-->reflect.Value)4.使用反射的方式获取变量的值(并返回对应的类型),要求数据类型匹配,比如xxx的类型是string,那么就应该使用reflect.ValueOf(xxx).String(),使用其他类型时会报错。
package main import ( "fmt" "reflect" ) func test(b interface{}) { // 通过"反射"获取变量b的reflect.Value 类型,对应的值 rValue := reflect.ValueOf(b).String() fmt.Println(rValue) } func main() { name := "sudada" test(name) }
5.通过反射修改变量的值
package main import ( "fmt" "reflect" ) func test(b interface{}) { // 通过"反射"获取变量b的reflect.Value 类型 rValue := reflect.ValueOf(b) // 通过"反射"修改"变量"的值。(先获取到指针对应的值,然后修改) rValue.Elem().SetString("sss") } func main() { name := "sudada" // 传入变量的指针 test(&name) fmt.Println("main", name) // 返回值:main sss }
4.5、反射的最佳实践
4.5.1、使用反射,(1)来"遍历"结构体的字段,(2)"调用"结构体的方法,(3)"获取"结构体标签的值;
Method方法:获取结构体的方法,传入'索引值'[0 1 2](这里取值0 1 2并不是按照"方法的定义顺序",而是"方法名称的排序顺序",也就是ASCII码);
Call方法:获取到方法后,调用该方法;package main import ( "fmt" "reflect" ) type Monster struct { Name string `json:"name"` Age int `json:"age"` Score float64 Sex string } // 结构体方法Print func (this Monster)Print() { fmt.Println("--start--") fmt.Println(this) fmt.Println("--stop--") } // 结构体方法GetSum(求和) func (this Monster)GetSum(num1,num2 int) int { return num1 + num2 } // 结构体赋值 func (this Monster)Set(name string,age int,score float64,sex string) { this.Name = name this.Age = age this.Score = score this.Sex = sex } // func test(b interface{}) { // 通过"反射"获取reflect.Type 类型 vType:=reflect.TypeOf(b) fmt.Println(vType) // 通过"反射"获取reflect.Value 类型 vValue:=reflect.ValueOf(b) // 通过"反射"获取reflect.Kind 类别 vKind:=vValue.Kind() // 如果"类别"不为"结构体",那么就报错退出 if vKind != reflect.Struct{ fmt.Println("b is not struct") return } // 获取到"结构体Monster"的字段数量 num:=vValue.NumField() fmt.Println(num) // 返回值:4 // 遍历结构体的所有字段的值(传入的值是几个就是几个) for i:=0; i<num; i++{ fmt.Println(vValue.Field(i)) // 返回值:niuniu 500 30.8 // 获取到所有结构体标签,需要通过reflect.Type来获取tag标签的值(使用reflect.Value获取不到) tagVal:=vType.Field(i).Tag.Get("json") // 如果该字段有tag标签就显示名称,否则不显示 if tagVal != "" { fmt.Println(tagVal) // 返回值:name age (对应的就是Monster内定义的标签名称) } } // 获取该结构体的所有方法 numOfMethod:=vValue.NumMethod() fmt.Println("结构体的方法数量为:",numOfMethod) // 返回值:3 // 调用结构体的方法,不传参(第2个方法Print),这里取值(0 1 2)并不是按照"方法的定义顺序",而是"方法名称的排序顺序",也就是ASCII码 vValue.Method(1).Call(nil) // 返回值: // --start-- // {niuniu 500 30.8 } // --stop-- // 调用结构体的方法,传参(第1个方法GetSum) // 定义一个切片,并传入2个值 var params []reflect.Value params=append(params,reflect.ValueOf(10)) params=append(params,reflect.ValueOf(20)) // 把值传入给方法 res:=vValue.Method(0).Call(params) // 这里的返回值,还是一个[]reflect.Value fmt.Println(res[0]) // 返回值:30 } func main() { mon:=Monster{ Name: "niuniu", Age: 500, Score: 30.8, } test(mon) }
五、go连接到redis
5.1、redis包的选择(redigo为例)
golang操作redis主要有两个库,go-redis(github.com/redis/go-redis)和redigo(github.com/gomodule/redigo)。
两者操作都比较简单,区别上redigo更像一个client执行各种操作都是通过Do函数去做的,redis-go对函数的封装更好;5.2、redis操作string类型的key:value(使用redigo包为例)
package main import ( "fmt" "github.com/gomodule/redigo/redis" ) func main() { // 1、连接到redis conn, err := redis.Dial("tcp","127.0.0.1:6379") if err != nil { fmt.Println("connect redis failed",err) } // 4、最后关闭redis defer conn.Close() // 2、往redis写数据 string key:value _, err = conn.Do("set","name","sudada") if err!=nil{ fmt.Println("set key error") return } // 3、获取redis的值 string key:value values, err := redis.String(conn.Do("get","name")) if err!=nil{ fmt.Println("get key error") return } fmt.Println("get key success, keys is: ",values) // 返回值:sudada }
5.3、redis操作Hash类型的key:value(使用redigo包为例)
5.3.1、一次set一个数据
package main import ( "fmt" "github.com/gomodule/redigo/redis" ) func main() { // 1、连接到redis conn, err := redis.Dial("tcp","127.0.0.1:6379") if err != nil { fmt.Println("connect redis failed",err) } // 4、最后关闭redis defer conn.Close() // 2、往redis写数据 hash key:value _, err = conn.Do("hset","user01","name","jake") // "user01"是hash值 if err!=nil{ fmt.Println("hset key error") return } _, err = conn.Do("hset","user01","age",18) // "user01"是hash值 if err!=nil{ fmt.Println("hset key error") return } // 3、获取redis的值 hash key:value values1, err1 := redis.String(conn.Do("hget","user01","name")) // "user01"是hash值 if err1!=nil{ fmt.Println("hget key error") return } fmt.Println("hget key success, keys is: ",values1) // 返回值:jake values2, err2 := redis.Int(conn.Do("hget","user01","age")) // "user01"是hash值 if err2!=nil{ fmt.Println("hget key error") return } fmt.Println("hget key success, keys is: ",values2) // 返回值:18 }
5.3.2、一次set多个数据
package main import ( "fmt" "github.com/gomodule/redigo/redis" ) func main() { // 1、连接到redis conn, err := redis.Dial("tcp","127.0.0.1:6379") if err != nil { fmt.Println("connect redis failed",err) } // 4、最后关闭redis defer conn.Close() // 2、往redis写数据 hash key:value _, err = conn.Do("hmset","user02","name","jake","age",19) // "user01"是hash值 if err!=nil{ fmt.Println("hmset key error") return } // 3、获取redis的值 hash key:value values1, err1 := redis.Strings(conn.Do("hmget","user02","name","age")) // "user01"是hash值 if err1!=nil{ fmt.Println("hmget key error") return } fmt.Println("hget key success, keys is: ",values1) // 返回值:[jake 19] for _,v := range values1{ fmt.Println(v) // 返回值:jake 19 } }
5.4、给key设置缓存时间(使用redigo包为例)
package main import ( "fmt" "github.com/gomodule/redigo/redis" ) func main() { // 1、连接到redis conn, err := redis.Dial("tcp","127.0.0.1:6379") if err != nil { fmt.Println("connect redis failed",err) } // 4、最后关闭redis defer conn.Close() // 2、给redis的key设置缓存时间(秒) _, err = conn.Do("expire","name", 10) if err!=nil{ fmt.Println("expire time error") return } }
5.5、go-redis包的使用
package main import ( "fmt" "github.com/go-redis/redis" "time" ) func main() { // 1、连接redis conn := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", // 密码 DB: 0, // 数据库 PoolSize: 20, // 连接池大小 }) // 4、关闭redis defer conn.Close() // 2、设置一个key:value以及过期时间 res1 := conn.Set("name","sudada",10*time.Second) fmt.Println(res1) // set name sudada ex 10: OK // 3、获取key对应的value res, err :=conn.Get("name").Result() if err!=nil { fmt.Println("get key error",err) return } fmt.Println(res) // 返回值:sudada }
5.6、redis连接池
1.实现初始化一定数量的连接,放到连接池;
2.当go需要操作redis时,直接从redis连接池取出连接即可;
3.节省临时获取redis连接的事件,提高效率;package main import ( "fmt" "github.com/gomodule/redigo/redis" ) var pool *redis.Pool // 程序启动时,就初始化连接处(这里使用"init函数(主函数执行之前,就已经执行了init函数)"实现) func init() { pool = &redis.Pool{ MaxIdle: 10, // 最大空闲连接数 MaxActive: 0, // 和数据库的最大连接数,0表示没有限制 IdleTimeout: 100, // 最大空闲时间 Dial: func() (redis.Conn, error) { return redis.Dial("tcp","127.0.0.1:6379") }, } } func main() { // 使用连接池 // 1、先从pool池,取出一个连接 coon := pool.Get() // 从pool池,取出一个连接,使用完毕后,关闭该连接 defer coon.Close() // 2、set key _,err := coon.Do("set","name", "sudada") if err != nil { fmt.Println("set key error: ",err) } // 3、get key values,err2 := redis.String(coon.Do("get","name")) if err2 != nil { fmt.Println("set key error: ",err2) } fmt.Println(values) // 返回值:sudada // 关闭连接池(关闭后,redis连接池就不能使用了) pool.Close() }