声明变量
变量、类型和关键字变量的类型在变量名的后面
- 用 = 声明
var a i n t
var b bool
a = 15
b = false
- 用:= 声明
a := 15
b := false
- 多个 var 声明可以成组;const 和 import 同样允许这么做。
var (
x int
b bool
)
有相同类型的多个变量同样可以在一行内完成声明:var x, y int 让 x 和 y 都是 int类型变量。
同样可以使用 平行赋值:a, b := 20, 16
特殊的变量名是 _(下划线)。任何赋给它的值都被丢弃
常量在 Go 中,也就是 constant。
可以使用 iota 生成枚举值。意思是 “不是最小在 Go 中字符串是不可变的。从 C 来的用户,下面的情况在 Go 中是非法的。
var s string = "hello"
s[0] = 'c' <- 修改第一个字符为’c’,这会报错
- Go 原生支持复数 使用复数的一个例子:
var c complex64 = 5+5i
fmt.Printf("Value is: %v", c)
Printf() 的 %v 参数含义是 “用默认格式打印这个值”,将会打印:(5+5i)
字符串
常用函数
a := "hello"
fmt.Println(strings.Contains(a, "ll")) // true
fmt.Println(strings.Count(a, "l")) // 2
fmt.Println(strings.HasPrefix(a, "he")) // true
fmt.Println(strings.HasSuffix(a, "llo")) // true
fmt.Println(strings.Index(a, "ll")) // 2
fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
fmt.Println(strings.Repeat(a, 2)) // hellohello
fmt.Println(strings.Replace(a, "e", "E", -1)) // hEllo
fmt.Println(strings.Split("a-b-c", "-")) // [a b c]
fmt.Println(strings.ToLower(a)) // hello
fmt.Println(strings.ToUpper(a)) // HELLO
fmt.Println(len(a)) // 5
b := "你好"
fmt.Println(len(b)) // 6
标准格式
s := "hello"
n := 123
p := point{1, 2} //结构体声明
fmt.Println(s, n) // hello 123
fmt.Println(p) // {1 2}
//无论什么类型都用%v
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}
fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
f := 3.141592653
fmt.Println(f) // 3.141592653
fmt.Printf("%.2f\n", f) // 3.14
%v:默认格式化
这种格式化方法使用默认的打印格式来输出变量 p 的值。它会根据变量的类型选择合适的格式进行打印。对于结构体类型,将按照字段顺序逐个打印输出。对于数字类型,将直接输出数字。这是一种通用的格式化方式。
%+v:包含字段名的格式化
这种格式化方法在 %v 的基础上,除了打印变量的值外,还会打印结构体的字段名。这样可以更清晰地显示出结构体的字段及其对应的值。
%#v:Go 语法表示的格式化
这种格式化方法在 %v 的基础上,以一种类似于 Go 语法的格式输出变量的值。它会打印出结构体的完整类型以及每个字段的名称和值。这对于了解结构体的结构和内容非常有帮助。
多行字符串
s := "Starting part"
+"Ending part"
另一种方式是使用反引号`作为原始字符串符号:
s := `Starting part
Ending part`
控制结构
这里没有do或者while循环,只有for。 语法有所不同(同C相比),没有圆括号。 Go有goto语句标签名是大小写敏感的。 如果想在for中执行多个变量,应当使用平行赋值。
break j <-现在终止的是j循环,而不是i的
swich-case结构默认跑完一个分支就不继续
Silce(切片)
可更改长度的数组,一般写作[]T,T是元素类型
如果你需要测试一个slice是否是空的,使用len(s) == 0来判断,而不应该用s == nil来判断
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get:", s[2]) // c
fmt.Println("len:", len(s)) // 3
s = append(s, "d")
s = append(s, "e", "f")
fmt.Println(s) // [a b c d e f]
c := make([]string, len(s))
copy(c, s)
fmt.Println(c) // [a b c d e f]
fmt.Println(s[2:5]) // [c d e]
fmt.Println(s[:5]) // [a b c d e]
fmt.Println(s[2:]) // [c d e f]
good := []string{"g", "o", "o", "d"}
fmt.Println(good) // [g o o d]
Map(哈希表)
一般定义map的方法:
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
//等价写法
m := map[string]int{
"one": 1,
"two": 2,
}
fmt.Println(m) // map[one:1 two:2]
fmt.Println(len(m)) // 2
fmt.Println(m["one"]) // 1
fmt.Println(m["unknow"]) // 0
r, ok := m["unknow"]
fmt.Println(r, ok) // 0 false
delete(m, "one")
m2 := map[string]int{"one": 1, "two": 2}
var m3 = map[string]int{"one": 1, "two": 2}
fmt.Println(m2, m3)
Range
range是个选代器,当被调用的时候,从它循环的内容中返回一个键值对。
在循环遍历切片时使用 range:
nums := []int{2, 3, 4}
sum := 0
for i, num := range nums {
sum += num
if num == 2 {
fmt.Println("index:", i, "num:", num) // index: 0 num: 2
}
}
fmt.Println(sum) // 9
每次迭代,range 返回当前元素在切片中的索引和对应的值。
在循环遍历映射(map)时使用 range:
m := map[string]string{"a": "A", "b": "B"}
for k, v := range m {
fmt.Println(k, v) // b B; a A
}
每次迭代,range 返回映射中的键和对应的值。我们可以通过 k 和 v 分别获取键和值,并将它们打印出来。由于映射是无序的,所以迭代的顺序可能不同。
在循环遍历映射时仅使用键或值:
for k := range m {
fmt.Println("key", k) // key a; key b
}
for _, v := range m {
fmt.Println("value", v) // value A;value B
}
在第一个循环中,我们只使用 range 返回的键,并将它们打印出来。在第二个循环中,我们使用 _ 忽略了 range 返回的键,只使用值并将其打印出来。因为我们只关心循环的键或值,而不需要使用索引或其他变量,所以我们使用 _ 来忽略它们。
结构体
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"
fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
fmt.Println(checkPassword(a, "haha")) // false
fmt.Println(checkPassword2(&a, "haha")) // false
}
func checkPassword(u user, password string) bool {
return u.password == password
}
func checkPassword2(u *user, password string) bool {
return u.password == password
}
将函数改为成员方法:
func (u user) checkPassword(password string) bool {
return u.password == password
} //值接收者方法
func (u *user) resetPassword(password string) {
u.password = password
} //指针接收者方法
func main() {
a := user{name: "wang", password: "1024"}
a.resetPassword("2048")
fmt.Println(a.checkPassword("2048")) // true
}
值接收者方法和指针接收者方法的区别:
- 修改原始实例:值接收者方法在方法内部对接收者进行操作时,操作的是接收者的副本,不会对原始实例产生影响。而指针接收者方法通过指针操作原始实例,可以修改原始实例的状态。
- 参数传递:值接收者方法将接收者作为值传递给方法,方法使用的是接收者的副本。而指针接收者方法将接收者作为指针传递给方法,方法使用的是接收者的地址,可以直接访问和修改接收者的字段。
- 性能开销:值接收者方法在调用时需要进行值的复制,可能会产生一定的性能开销。而指针接收者方法通过指针传递,可以避免复制大型结构体的开销。
- 方法调用:对于值类型的实例,可以同时调用值接收者方法和指针接收者方法;而对于指针类型的实例,只能调用指针接收者方法,不能调用值接收者方法。 选择使用值接收者方法还是指针接收者方法取决于具体需求。如果需要修改原始实例的状态,或者实例较大并希望避免复制开销,可以使用指针接收者方法。如果只是对实例进行简单操作,不需要修改原始实例的状态,可以使用值接收者方法。
异常
使用返回err来处理
在函数返回值类型里面加err 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") //有错误返回nil
}
func main() {
u, err := findUser([]user{{"wang", "1024"}}, "wang")
if err != nil { //有错误返回错误值
fmt.Println(err)
return
}
fmt.Println(u.name) // wang
}
JSON
针对已有的结构体,只要将每个字段的首字母大写
type userInfo struct {
Name string
Age int `json:"age"`
Hobby []string
}
就可以用json.Marshal()数列化,可以再用json.Unmarshal反数列化
时间处理
now := time.Now()
fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
fmt.Println(t) // 2022-03-27 01:25:36 +0000 UTC
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
fmt.Println(t.Format("2006-01-02 15:04:05")) //使用实例标准化 2022-03-27 01:25:36
diff := t2.Sub(t)
fmt.Println(diff) // 1h5m0s
fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
if err != nil {
panic(err)
}
fmt.Println(t3 == t) // true
fmt.Println(now.Unix()) // 1648738080 时间戳
数值解析
f, _ := strconv.ParseFloat("1.234", 64) //(要解析的字符串, 浮点数精度位数)
fmt.Println(f) // 1.234
n, _ := strconv.ParseInt("111", 10, 64) //(要解析的字符串, 基数(n进制), 整数位数)
fmt.Println(n) // 111
n, _ = strconv.ParseInt("0x1000", 0, 64)
fmt.Println(n) // 4096
n2, _ := strconv.Atoi("123") //将字符串解析为 int 类型的整数
fmt.Println(n2) // 123
n2, err := strconv.Atoi("AAA")
fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
当基数为 0 时,函数会根据字符串的前缀来判断进制方式。如果字符串以 "0x" 开头,表示是一个十六进制数;如果字符串以 "0" 开头,表示是一个八进制数;其他情况下,默认采用十进制。
进程信息
fmt.Println(os.Args) // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
fmt.Println(os.Setenv("AA", "BB"))
buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
if err != nil {
panic(err)
}
fmt.Println(string(buf)) // 127.0.0.1 localhost
GPT说:
这段代码展示了在 Go 编程语言中使用
os和exec包的一些功能。
fmt.Println(os.Args):os.Args是一个字符串切片,保存了程序运行时传递的命令行参数。通过fmt.Println打印出os.Args的值,可以显示程序运行时传递的参数列表。在这个例子中,输出是[/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d],其中/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main是可执行文件的路径,a b c d是传递给程序的参数。
fmt.Println(os.Getenv("PATH")):os.Getenv函数用于获取环境变量的值。在这个例子中,通过传递"PATH"作为参数,获取系统环境变量"PATH"的值,并将其打印出来。输出的结果是"/usr/local/go/bin..."。
fmt.Println(os.Setenv("AA", "BB")):os.Setenv函数用于设置环境变量的值。在这个例子中,将环境变量"AA"的值设置为"BB",并将设置结果打印出来。输出的结果是true,表示设置环境变量成功。
exec.Command和CombinedOutput:这两个函数用于执行外部命令。exec.Command用于创建一个执行指定命令的*exec.Cmd结构体,而CombinedOutput方法用于执行该命令并返回其标准输出和标准错误的组合结果。 这段代码通过执行命令grep 127.0.0.1 /etc/hosts来在/etc/hosts文件中查找包含127.0.0.1的行,并将结果保存在变量buf中。如果执行命令时出现错误,使用panic函数抛出该错误。否则,将buf的内容转换为字符串并打印出来。在这个例子中,输出的结果是127.0.0.1 localhost,表示在/etc/hosts文件中找到了包含127.0.0.1的行。