3.8 结构体的使用
- 结构体
type user struct{
name string
password string
}
func main(){
a:=user{name:"wang",password:"1024"}
b:=user{"wang","1024"}
c:=user{name:"wang"}
c.password="1024"
var d user
d.name="wang"
d.password="1024"
}
func checkPassword(u user,password string) bool{
return u.password==password
}
func checkPassword2(u *user,password string )bool{
return u.password==password
} //可以看到go语言中没有使用->用法,统一用.来指向成员
可以为结构体去定义一些方法
func (u user)checkPassword(password string )bool{
return u.password==password
}
func (u user) resetPassword(password string){
u.password=password
} //没有返回值就不写(void不存在)
3.9 错误处理
在Go语言中,nil是一个预定义的标识符,用来表示空值或无效值。
需要注意的是,除了指针、引用类型和接口类型之外的其他类型,如整数、浮点数、布尔值等,它们不能被赋值为nil
error 使用前要导包 import errors
error 是接口类型
func finduser(users []user,name string)(v *user,err error){
for _,u:range users{
if u.name==name{
return *u,nil
}
}
return nil,errors.New("not found")//进行错误提示
}
error不存在时,常常就赋值为nil
if u,err:=finduser([]user{{"wang","1024"}},"li");err!=nil{
....
} //if后面的语句中至少且仅有一个布尔表达式,也可以有其余赋值表达式,用分号隔开
//对于 if 语句来说,条件判断语句必须放在最后面,因此可以在if中的布尔表达式执行前,执行所有的其余语句,且有效
3. 10接口的使用与理解深入
在Go语言中,error是一个内置的接口类型,而不是一个新的类型。它定义了一个简单的方法签名,用于表示可能发生的错误。 type error interface { Error() string }
在Go语言中,接口(interface)是一种类型,用于定义一组方法的集合。接口通过定义方法签名来描述方法集,但不提供方法的具体实现。
接口的定义使用 type 和 interface 关键字,并且没有任何变量字段。一个接口可以包含任意数量的方法。
下面是一个简单的接口示例,定义了一个叫Writer的接口,包含了一个名为Write的方法: type Writer interface { Write(data []byte) (int, error) } 在上述示例中,我们定义了一个名为Writer的接口,它包含了一个Write方法。Write方法接收一个[]byte类型的数据,并返回写入的字节数和可能发生的错误
在Go语言中,error是一个内置的接口类型,而不是一个新的类型。它定义了一个简单的方法签名,用于表示可能发生的错误。 type error interface { Error() string }
在Go语言中,接口(interface)是一种类型,用于定义一组方法的集合。接口通过定义方法签名来描述方法集,但不提供方法的具体实现。
接口的定义使用 type 和 interface 关键字,并且没有任何变量字段。一个接口可以包含任意数量的方法。
下面是一个简单的接口示例,定义了一个叫Writer的接口,包含了一个名为Write的方法: type Writer interface { Write(data []byte) (int, error) } 在上述示例中,我们定义了一个名为Writer的接口,它包含了一个Write方法。Write方法接收一个[]byte类型的数据,并返回写入的字节数和可能发生的错误
-
接口的理解
-
接口(interface)和结构体(struct)都是类型(type)的概念,但用途和表达方式有所不同。
接口是一种抽象类型,它定义了一组方法的集合,但不包含具体的实现。接口通过方法签名来描述方法,可以被其他类型实现。通过接口,可以定义一组规范或契约,使得不同的类型可以以统一的方式进行操作。
结构体是一种复合类型,用于封装和组织数据。结构体可以包含各种类型的字段,用于描述数据的属性。结构体还可以定义方法,用于对结构体进行操作和处理。结构体可以实现接口,提供方法的具体实现。
通过接口,可以表达方法的标签或约定,指定了类型应该具有哪些方法。通过结构体来实现接口,可以为该类型提供方法的具体实现,从而满足接口的要求。
接口实际上是一种抽象类型,它定义了行为,而不关心类型的具体实现细节。通过使用接口,可以将不同的类型视为统一的类型,以便以一致的方式进行操作。
值得注意的是,实现接口的过程是隐式的,无需在类型中显式声明实现了某个接口。只要类型拥有接口中定义的全部方法,并且这些方法与接口中的方法签名完全匹配,就认为该类型实现了接口。
这种隐式的实现方式有一些优点:
- 可以在不修改原始类型定义的情况下,为类型添加实现接口的方法。这意味着你可以为任意已有的类型定义新的方法,使其满足某个接口的要求,而无需改变已有的代码。
- 同一个类型可以同时实现多个接口。只需要确保该类型的方法集满足所有接口的方法要求即可。
- 可以在不改变原有代码的情况下,为已有类型添加新的接口实现。这样可以使代码更加灵活和可扩展
3.11 字符串操作
import "strings"
a :="hello"
strings.Contains(a,"ll") // true 查找是否含有连续子序列
strings.Count(a,"l") // 2 查找重复片段
strings.HasPrefix(a,"he") // true 是否以特定的前缀开头
strings.HasSuffix(a,"llo")// true 是否以特定的后缀结尾
strings.Index(a,"ll") //2 查找第一个字母的首地址
strings.Join([]string{"he","llo"},"-") //he-llo 连接字符串,后者为连接符
strings.Repeat(a,2) //hellohello 重复某个字符串n遍
strings.Replace(a,"e","E",-1) //hEllo
func Replace(s, old, new string, n int) string
s:要进行替换操作的字符串。
old:要被替换的字符或子字符串。
new:用于替换的新字符或子字符串。
n:指定替换的次数。如果为 -1,则表示在源字符串中全部替换。
strings.Split("a-b-c","-") //分割字符串 得到切片[a,b,c] string的切片
strings.ToLower(a) //大写转为小写
strings.ToUpper(a) //小写转大写
len()获取长度 其中一个中文字符为三个长度 一个英文字符为一个长度
- 字符串格式化
type point struct{
x,y int
}
s :="hello"
n:=123
p:=point{1,2}
fmt.Println(s,n) //hello 123
fmt.Println(p) //{1 2}
fmt.Printf("s=%v\n",s) //s=hello
fmt.Printf("n=%v\n",n) //n=123
fmt.Printf("p=%v\n",p) //p={1 2} %v 是一个通用的格式化动词,它根据值的实际类型进行格式化输出。它可以用于输出布尔值、整数、浮点数、字符串、数组、切片、映射、结构体等各种类型的值。
需要注意的是,%v 可以输出各种类型的值,但对于某些类型,可能不是最优的格式化选项。在某些情况下,可以使用其他格式化动词(如 %d、%f、%s 等)来更精确地控制输出的格式。
fmt.Printf("p=%+v\n",p) //p={x:1 y:2} 更详细的信息 %+v
fmt.Printf("p=%#v\n",p) //p=main.point{x:1 y:2} 更更详细的信息 %+v
f :=3.1415926
fmt.Printf("%.2f\n",f) //3.14
- json数据处理
json数据处理
import (
"encoding/json"
"fmt"
}
type userInfo struct{
Name string
Age int `json:"age"` JSON中的字段名为"age"而非Age 这里是反引号
//尽管大小写不完全一致,但由于标记(tag) json:"Age" 的存在,Go 的 JSON 反序列化仍然可以成功匹配并将值赋给 Age 字段
Hobby []string
}
func main(){
a:=userInfo{Name:"wang",Age:18,Hobby[]string{"golang","typescript"}}
buf,err :=json.Marshal(a)
//使用 userInfo{Name: "wang", Age: 18, Hobby: []string{"golang", "typescript"}} 的形式创建了一个 userInfo 结构体的实例,并将其赋值给变量 a。在这个结构体实例中,Name 字段的值为 "wang",Age 字段的值为 18,Hobby 字段的值为一个包含两个字符串元素的字符串切片 []string{"golang", "typescript"}。
//变量 buf 和 err 被定义并分配空间,buf 是一个 []byte 类型(字节切片)的变量用于存储 JSON 序列化后的结果,err 是一个错误类型的变量用于存储可能发生的错误。
//调用 json.Marshal(a) 函数对结构体 a 进行 JSON 序列化,将其转换为 JSON 格式的字节数组切片。json.Marshal() 函数接受一个参数,即需要序列化的结构体或值。
//将序列化后的结果赋值给 buf 变量,同时将序列化过程中可能发生的错误赋值给 err 变量。
//json数据里是一串字符串,buf里是每一个字符所对应的ascii值
if err!=nil{
panic(err)
//panic(err) 是一个 Go 语言的内置函数,用于在程序运行时引发异常(panic)。它接受一个参数 err,通常是一个 error 类型的值,用于描述出现的错误或异常情况。
//当程序执行到 panic(err) 这一行代码时,会立即停止当前函数的执行,并开始执行当前函数的延迟(defer)函数(如果有的话)。然后,程序会向上返回(回溯)执行调用栈,直到遇到可以处理这个异常的 recover 语句。
}
fmt.Println(buf) //{123,34,78,97....}这里面是序列后的每个字节中代表的数字
fmt.Println(string(buf)) // 转化为字符
{"Name":"wang","age":18,"Hobby":{"golang","typescript"}}
buf,err=json.MarshalIndent(a,"","\t")
//定义了两个变量 buf 和 err,用于存储 JSON 序列化后的结果和可能发生的错误。其中,buf 是一个 []byte 类型的变量,用于存储序列化后的字节数组切片。err 是一个错误类型的变量,用于存储序列化过程中可能出现的错误。
//调用 json.MarshalIndent(a, "", "\t") 函数对结构体 a 进行 JSON 序列化,并进行格式化输出。json.MarshalIndent() 函数接受三个参数:需要序列化的结构体或值,一个空字符串 "" 作为缩进的前缀,和一个制表符 "\t" 作为缩进的字符串。该函数将结构体 a 转换为 JSON 格式的字节数组切片,并在生成的字符串中添加缩进和换行符,以提高可读性。
//将序列化后的结果赋值给 buf 变量,同时将序列化过程中可能发生的错误赋值给 err 变量。
//json.MarshalIndent() 函数的第二个参数用于指定缩进的前缀,可以是任意字符串,常见的是使用空字符串或空格。第三个参数用于指定缩进的字符串,通常使用制表符 "\t"。使用 json.MarshalIndent() 函数生成的 JSON 字符串将会具有良好的可读性,每一级缩进使用制表符表示,并包含换行符。这在调试、日志记录或提供给人类阅读的场景中非常有用。
if err !=nil{
panic(err)
}
fmt.Println(string(buf))
var b userInfo
err=json.Unmarshal(buf,&b)
//定义了一个变量 err,用于存储函数可能返回的错误信息。err 是一个错误类型的变量。
//调用 json.Unmarshal(buf, &b) 函数对 buf 中的 JSON 数据进行反序列化,并将结果存储到变量 b 中。json.Unmarshal() 函数接受两个参数:需要解码的 JSON 数据(在这里是 buf,一个 []byte 类型的变量),以及一个指向目标结构体 b 的指针(&b)。该函数将 buf 中的 JSON 数据解码为对应的 Go 数据结构,并将结果存储到 b 变量所指向的位置。
//将反序列化过程中可能出现的错误赋值给变量 err。
//解码过程中,函数会根据 JSON 数据中的字段名和结构体中的字段名进行匹配,并将对应的值赋给结构体字段。如果匹配失败或其他解码错误出现,函数将会返回相应的错误。
if err !=nil{
panic(err)
}
fmt.Println("%#v",b) //main.userInfo{Name:"wang","Age":18,"Hobby":{"golang","typescript"}}
}
在 Go 语言中,结构体类型和字段名是属于不同的命名空间。因此,结构体类型名称与结构体字段名称可以重名而不会引发错误。
在示例代码中,使用 User 结构体时,结构体类型 Address 和结构体字段 Address 虽然具有相同的名称,但它们代表不同的实体。编译器可以通过上下文来区分它们的含义。
- JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,用于表示结构化的数据。它是一种文本格式,通常用于通过网络传输数据或将数据存储在文件中。
JSON 的语法简洁清晰,易于阅读和编写,同时也易于解析和生成。它由以下几个基本数据类型组成:
0. 对象(Object):由键值对组成的无序集合,使用花括号 `{}` 表示。键值对中的键必须是字符串,值可以是任意 JSON 类型。
0. 数组(Array):由值组成的有序集合,使用方括号 `[]` 表示。值可以是任意 JSON 类型,多个值之间以逗号分隔。
0. 字符串(String):由双引号 `"` 包围的Unicode字符序列,支持转义字符。
0. 数值(Number):表示整数或浮点数。
0. 布尔值(Boolean):表示真值或假值。
0. 空值(null):表示空对象或空数组。
-
结构体字段的标签(tag)是用于定义结构体字段的元数据。结构体标签可以在运行时被反射机制读取,用于进行一些运行时的操作,比如JSON序列化、ORM映射等。
在给结构体字段添加标签时,可以使用各种格式和键值对的形式来定义不同的元数据。在你提供的示例中,
json:"age"是一个结构体字段标签,用于指定在JSON序列化和反序列化时该字段的行为。具体来说,
json:"age"表示在进行JSON序列化和反序列化时,该结构体字段的名称应该是 “age”。这是因为默认情况下,Go语言的JSON处理库会使用结构体字段的名称作为JSON对象中的键。在进行JSON序列化和反序列化时,会使用标记中指定的字段名。同时,标记也不会影响结构体字段的访问或使用。
- 添加标签的主要目的是为了与外部系统进行数据交互,特别是在与其他编程语言或框架进行数据交换时。
-
自定义字段名: 通过添加标签,你可以将结构体字段的名称映射到特定的键名。这对于需要与其他系统进行数据交互的情况非常有用,因为不同的系统可能对字段名称有不同的约定。通过使用标签,你可以确保序列化的 JSON 数据中的字段名称与目标系统预期的字段名称一致
type User struct {
Name string `json:"username"`
Age int `json:"age"`
IsMale bool `json:"is_male"`
}
- **
版本兼容性:** 在修改结构体时,如果你需要向后兼容旧版本的数据,可以使用标签来处理字段的变动。可以通过在旧字段上使用标签进行重命名,并添加新的字段。这样可以确保旧版本的数据仍然可以正确地反序列化 - 嵌套结构体和循环引用: 标签也非常有用于处理嵌套结构体和循环引用的情况。通过指定嵌套结构体字段的路径,你可以准确地在复杂的数据结构中指导序列化和反序列化过程,有时一样的名字很容易干扰
- 使用标签可以提供灵活性和控制,使得结构体在与外部系统进行数据交互时能够精确地进行映射和转换。尽管在某些情况下可能看起来多此一举,但它在与其他系统进行数据交换和版本控制时确实发挥了重要的作用
- 如果自定义了json别称,那么数据进行json序列化时,显示的键名会改变,反序列化时,也必须使用更改后的别称
3.13 时间函数
import(
"time")
now :=time.Now()
fmt.Println(now)
// 2023-07-28 16:52:53 +0800 CST m=+0.000000001。这个时间表示当前的年、月、日、时、分、秒,还包含了时区(CST)和时间偏移量(+0800)时间的相对差异
//获取当前时间 年月日,时间
构造时间
t :=time.Data(2022,3,27,1,25,36,0,time.UTC)
然后可以通过
t.Year(),t.Month(),t.Day(),t.Hour(),t.Minute() 来获取对应数据,月份为英文字符串
t.format("2006-01-02 15:04:05") //按这样的格式进行时间字符格式化
//使用这些指示符,你可以根据需要构建自定义的时间格式。例如,t.Format("2006-01-02 15:04:05") 会将时间按照 “年-月-日 时:分:秒” 的格式进行格式化。
time.Date() 是 time 包中的一个函数,用于创建 Time 对象。
参数 2022、3、27 是指定日期的年、月、日。
参数 1、25、36 是指定时间的时、分、秒。
参数 0 是指定时间的纳秒。
time.UTC 是指定使用的时区,这里是使用的协调世界时(Coordinated Universal Time)。
diff :=t1.Sub(t2) 用t1-t2得到的时间差,精确到s
diff.Minutes(),diff.Seconds() //转化为对应的总分钟数,总秒数
t3,err :=time.Parse("2006-01-02 15:04:05","2022-03-27 01:25:36")
time.Parse() 接受两个参数:格式化字符串和要解析的时间字符串。它会尝试根据格式化字符串的指示符,将时间字符串解析为对应的时间对象。
如果解析成功,time.Parse() 将返回对应的时间对象和一个空的错误值。如果解析失败,它将返回一个无效的时间对象和一个非空的错误值。
格式化字符串参数在 time.Parse() 函数中的作用是告诉函数如何解析时间字符串。
因此,格式化字符串参数在 time.Parse() 函数中的作用是为解析器提供关于时间字符串的结构和格式信息,以确保正确解析时间字符串并创建对应的时间对象。
now.Unix()// 表示从 Unix 纪元(1970 年 1 月 1 日 00:00:00 UTC)开始到 now 表示的时间点经过的秒数。时间戳
也可以time.Unix(秒数,纳秒数) 返回值就是时间对象了
3.14 获取进程相关信息
import (
"os"
"os/exec"
)
提供了与操作系统交互和执行外部命令的功能。
os.Args
os.Args 是一个字符串切片,包含命令行参数,其中第一个元素是该程序的名称。可以使用 os.Args[1:] 获取除程序名称之外的命令行参数
os.Getenv("PATH")
用于获取操作系统环境变量 PATH 的值。该函数将返回一个字符串,表示给定环境变量的值
OS.Setenv("AA","BB")
用于设置环境变量 “AA” 的值为 “BB”。这会将环境变量 AA 设置为 BB
buf,err := exec.Command("grep","127.0.0.1","/etc/hosts").CombinedOutput()
是一个用于执行命令的函数。在这个示例中,我们执行了 “grep” 命令来在 “/etc/hosts” 文件中搜索 “127.0.0.1”。同时捕获命令的标准输出和标准错误输出,并返回一个字节切片和一个可能发生的错误
buf依旧是字节切片 []byte
if err !=nil{
panic(err)
}
fmt.Println(string(buf)) //127.0.0.1
4 小结
- 对于GO语言剧本语法的学习与使用可以进一步深化对于新型语言的特色与性能优势处,同时对后续go语言的开发工作有进一步的奠定作用.