Golang特点
- 原生并发
- 轻量简单
- 部署方便
常量
// 定义常量
const a = '12' # 单个
const a, b = 12, '12' # 多个
// 枚举,可以有表达式,但是要用内置函数len(), cap(), unsafe.Sizeof()
const{
a = "abc"
b = len(a)
c = unsafe.Sizeof(a)
}
iota // 可以认为是枚举的行索引,第一个 iota 等于 0
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
大写字母开头的常量在包外可见
小写字母开头的常量只能在包内访问
函数体内声明的常量只能在函数体内生效
常量中只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
变量
声明变量
var a int
var a int = 1
var a, b, c int //同一类型的多个变量可以声明在同一行
a := 1
a, b, c := 5, 7, "abc"
var (
a int
b string
)
a, b = b, a //交换两个变量的值
当一个变量被声明之后,系统自动赋予它该类型的零值:int 为 0
,float 为 0.0
,bool 为 false
,string 为空字符串""
,指针为 nil
。记住,所有的内存在 Go 中都是经过初始化的。
变量命名:小驼峰
布尔
result := true
数值
1、整数:
- int8(-128 -> 127)
- int16(-32768 -> 32767)
- int32(-2,147,483,648 -> 2,147,483,647)
- int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)
整型的零值为 0
2、无符号整数:
- uint8(0 -> 255)
- uint16(0 -> 65,535)
- uint32(0 -> 4,294,967,295)
- uint64(0 -> 18,446,744,073,709,551,615)
3、浮点型(IEEE-754 标准):
- float32(+- 1e-45 -> +- 3.4 * 1e38)
- float64(+- 5 1e-324 -> 107 1e308)(推荐)
float32 精确到小数点后 7 位,float64 精确到小数点后 15 位. 使用 ==
或者 !=
来比较浮点数时应当非常小心
尽可能地使用 float64,math
包中所有有关数学运算的函数都会要求接收这个类型
浮点型的零值为 0.0。
4、复数
- complex64 (32 位实数和虚数)
- complex128 (64 位实数和虚数)
复数使用 re+imI
来表示,其中 re
代表实数部分,im
代表虚数部分,I
代表根号负 1。
eg:var c1 complex64 = 5 + 10i // 输出: 5 + 10i
5、格式化符号
%d
十进制表示%b
二进制表示%o
八进制表示%c
相应Unicode码点所表示的字符%U
Unicode格式:123,等同于 "U+007B"%x
十六进制表示,字母形式为小写 a-f%X
十六进制表示,字母形式为大写 A-F%q
单引号围绕的字符字面值,由Go语法安全地转义%g
用于格式化浮点型——%f
输出浮点数;%e
输出科学计数表示法%0d
用于规定输出定长的整数%n.mg
用于表示数字 n 并精确到小数点后 m 位,除了使用 g 之外,还可以使用 e 或者 f,例如:%5.2e
来输出 3.4 的结果为3.40e+00
。%v
只输出所有的值%+v
先输出字段类型,再输出该字段的值%#v
先输出结构体名字值,再输出结构体(字段类型+字段的值)%T
相应值的类型的Go语法表示%%
百分号,字面上的%,非占位符含义
6、位运算
- 按位与
&
- 按位或
|
- 按位异或
^
- 位清除
&^
7、逻辑运算符
==、!=、<、<=、>、>=
8、算数运算符
+、-、* 、 /
/
对于整数运算而言,结果依旧为整数
取余运算符只能作用于整数
9、随机数
a := rand.Int()
r := rand.Intn(8)
rand.Intn
返回介于 [0, n) 之间的伪随机数。
rand.Float32
和 rand.Float64
返回介于 [0.0, 1.0) 之间的伪随机数,其中包括 0.0 但不包括 1.0
10、运算符优先级
优先级 运算符
7: ^ !
6: * / % << >> & &^
5: + - | ^
4: == != < <= >= >
3: <-
2: &&
1: ||
字符串
字符串是一种值类型,且值不可变,即创建某个文本后你无法再次修改这个文本的内容;更深入地讲,字符串是字节的定长数组。
Go 中的字符串也可能根据需要占用 1 至 4 个字节(示例见第 4.6 节),这与其它语言如 C++、Java 或者 Python 不同(Java 始终使用 2 个字节)。Go 这样做的好处是不仅减少了内存和硬盘空间占用,同时也不用像其它语言那样需要对使用 UTF-8 字符集的文本进行编码和解码。
Go 中使用 strings
包来完成对字符串的主要操作。
拼接字符串:
-
在少量仅有字符串类型的变量拼接字符串时,并且对性能要求不高的场景,推荐使用操作符 + 的方式; str := "a" + "b" + "c"
-
在字符串类型切片中的元素拼接字符串时,推荐使用 strings.Join 方式; s := []string{"a", "b", "c"} str := strings.Join(s, ", ")
-
在少量多种类型变量拼接字符串时,推荐使用 fmt.Sprint 的方式;
str := fmt.
Sprint
("a", 1, "b\r\n")
str1 := fmt.Sprintf
("name:%s,age=%d.\r\n", "lucy", 17)
str2 := fmt.Sprintln
("a", 1, "b") -
在少量字符串、字符和 Unicode 拼接字符串时,并且对性能有一定要求的场景,推荐使用
buyes.Buffer
的方式;var b bytes.
Buffer
b.WriteString
("My ")
b.WriteString("name ")
b.WriteString("is ")
b.WriteString("lucy.")
str := b.String
() -
在大量字符串、字符和 Unicode 拼接字符串时。并且对性能有一定要求的场景,推荐使用
strings.Builder
的方式。var b bytes.
Builer
b.WriteString
("My ")
b.WriteString("name ")
b.WriteString("is ")
b.WriteString("lucy.")
str := b.String
()
获取字符串长度:
len
(str)utf8.RuneCountInString
(str) 效率会更高一点
获取字符:
- 第 i 个字节:
str[i - 1]
, - 最后 1 个字节:
str[len(str)-1]
, - 第 1 个字节:
str[0]
获取子字符串:
str[start:end]
:从字符串 str 获取到从索引 start 开始到end-1
位置的子字符串str[start:]
则表示获取从 start 开始到 len(str)-1 位置的子字符串str[:end]
表示获取从 0 开始到end-1
的子字符串。
前缀和后缀:
- strings.
HasPrefix
(str, "He") bool - strings.
HasSuffix
(str, "ld!") bool
是否包含某字符串:
- strings.
Contains
(s, substr string) bool
判断子字符串或字符在父字符串中的索引:
- strings.
Index
(s, str string) int //第一个字符的索引 - strings.
LastIndex
(s, str string) int //str 在字符串 s 中最后出现位置的索引,-1 表示不包含 - strings.
IndexRune
(s string, r rune) int //如果 ch 是非 ASCII 编码的字符
字符串替换:
- strings.
Replace
(str, old, new, n) string //将字符串 str 中的前 n 个字符串 old 替换为字符串 new,n = -1 则替换所有字符串 old 为字符串 new
修改某个字符:
- s := "hello" ;c := []byte(s); c[0] = 'c' ;s2 := string(c) ; // s2 == "cello"
- 必须先将字符串转换成字节数组,然后再通过修改数组中的元素值来达到修改字符串的目的,最后将字节数组转换回字符串格式。
统计字符串出现次数:
- strings.
Count
(s, str string) int //计算字符串 str 在字符串 s 中出现的非重叠次数
重复字符串:
- strings.
Repeat
(s, count int) string //重复 count 次字符串 s 并返回一个新的字符串
修改大小写:
- strings.
ToLower
(s) string //将字符串中的 Unicode 字符全部转换为相应的小写字符: - strings.
ToUpper
(s) string
修剪字符串:
- strings.
TrimSpace
(s) //剔除字符串开头和结尾的空白符号 - strings.
Trim
(s, "cut") //将开头和结尾的 cut 去除掉 - strings.
TrimLeft
(s, "cut") //仅开头 - strings.
TrimRight
(s, "cut") // 仅结尾
分割字符串:
- strings.
Fields
(s) //用空白作为分隔符将字符串分割为若干块,并返回一个 slice,习惯使用 for-range 循环来对其进行处理 - strings.
Split
(s, sep) //自定义分割符号对字符串分割,返回 slice ,习惯使用 for-range 循环来对其进行处理
拼接 slice 到字符串:
- strings.
Join
(sl []string, sep string) string //将元素类型为 string 的 slice 使用分割符号来拼接组成一个字符串
类型转换:
-
任何类型 T 转换为字符串总是成功的
-
strconv.
Itoa
(i int) string //返回数字 i 所表示的字符串类型的十进制数。 -
strconv.
FormatFloat
(f float64, fmt byte, prec int, bitSize int) string将 64 位浮点型的数字转换为字符串,其中 fmt 表示格式(其值可以是 'b'、'e'、'f' 或 'g'),prec 表示精度,bitSize 则使用 32 表示 float32,用 64 表示 float64。
-
val, err = strconv.
Atoi
(s string) (i int, err error) //将字符串转换为 int 型 -
val, err = strconv.
ParseFloat
(s string, bitSize int) (f float64, err error) //将字符串转换为 float64 型。
时间日期
time包
carbon库:https://segmentfault.com/a/1190000021754301
当前时间
- time.
Now
() - carbon.
Now
() //.ToDateTimeString()
代码耗时
- time.Now()
end := time.Now()
fmt.Println(end.Sub(start))
日期格式化
-
now := time.Now()
now.Format("2006/01/02 15:04")
其他文档:http://docs.studygolang.com/pkg/strings/
指针
&:获取变量的内存地址,叫做指针
*:修改指针对应的变量的值
package main
import "fmt"
func main() {
s := "good bye"
var p *string = &s
*p = "ciao"
fmt.Printf("p: %p\n", p) // 0x2540820
fmt.Printf("*p: %s\n", *p) // ciao
fmt.Printf("s: %s\n", s) // ciao
}
指针传递是很廉价的,只占用 4 个或 8 个字节。当程序在工作中需要占用大量的内存,或很多变量,或者两者都有,使用指针会减少内存占用和提高效率。被指向的变量也保存在内存中,直到没有任何指针指向它们,所以从它们被创建开始就具有相互独立的生命周期。
不能得到一个文字或常量的地址.
传递指针给函数不但可以节省内存(因为没有复制变量的值),而且赋予了函数直接修改外部变量的能力,所以被修改的变量不再需要使用 return 返回。如下的例子,reply 是一个指向 int 变量的指针,通过这个指针,我们在函数内修改了这个 int 变量的数值。
package main
import (
"fmt"
)
// this function changes reply:
func Multiply(a, b int, reply *int) {
*reply = a * b
}
func main() {
n := 0
reply := &n
Multiply(10, 5, reply)
fmt.Println("Multiply:", *reply) // Multiply: 50
}
什么时候用指针?
什么是receiver?
func (t T) method_name(t T){}
1
这里面的T就是receiver
如果receiver是map、func或者chan,不要使用指针
如果receiver是slice并且该函数并不会修改此slice,不要使用指针
如果该函数会修改receiver,此时一定要用指针
如果receiver是struct并且包含互斥类型sync.Mutex,或者是类似的同步变量,receiver必须是指针,这样可以避免对象拷贝
如果receiver是较大的struct或者array,使用指针则更加高效。多大才算大?假设struct内所有成员都要作为函数变量传进去,如果觉得这时数据太多,就是struct太大
如果receiver是struct,array或者slice,并且其中某个element指向了某个可变量,则这个时候receiver选指针会使代码的意图更加明显
如果receiver使较小的struct或者array,并且其变量都是些不变量、常量,例如time.Time,value receiver更加适合,因为value receiver可以减少需要回收的垃圾量。
最后,如果不确定用哪个,使用指针类的receiver
类型转换
//string到int
int,err:=strconv.Atoi(string)
//string到int64
int64, err := strconv.ParseInt(string, 10, 64)
//int到string
string:=strconv.Itoa(int)
//int64到string
string:=strconv.FormatInt(int64,10)
//string到float32(float64)
float,err := strconv.ParseFloat(string,32/64)
//float到string
string := strconv.FormatFloat(float32, 'E', -1, 32)
string := strconv.FormatFloat(float64, 'E', -1, 64)
// 以下为参数含义
// 'b' (-ddddp±ddd,二进制指数)
// 'e' (-d.dddde±dd,十进制指数)
// 'E' (-d.ddddE±dd,十进制指数)
// 'f' (-ddd.dddd,没有指数)
// 'g' ('e':大指数,'f':其它情况)
// 'G' ('E':大指数,'f':其它情况)