整型
在Go语言中,% 取模运算符的符号和被取模数的符号总是一致的,因此 -5%3 和 -5%-3 结果都是 -2
除法运算符 / 依赖于操作数是否全为整数,整数除法会向着0方向截断余数
无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合、分析二进制文件格式或者是哈希和加密操作等
当使用fmt包打印一个数值时,可用 %d、%o 或 %x 参数控制输出的进制格式。通常 Printf 格式化字符串包含多个 % 参数时将会包含对应相同数量的额外操作数,但是 % 之后的 [1] 副词告诉 Printf 函数再次使用第一个操作数。第二,% 后的 # 副词告诉 Printf 在用 %o、%x 或 %X 输出时生成 0、0x 或 0X 前缀
字符使用 %c 参数打印,或者是用 %q 参数打印带单引号的字符
o := 0666
fmt.Printf("%d %[1]o %#[1]o\n", o) // "438 666 0666"
x := int64(0xdeadbeef)
fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x)
// Output:
// 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF
ascii := 'a'
fmt.Printf("%d %[1]c %[1]q\n", ascii) // "97 a 'a'"
浮点数
用 Printf 函数的 %g 参数打印浮点数,将采用更紧凑的表示形式打印,并提供足够的精度,但是对应表格的数据,使用 %e(带指数)或 %f 的形式打印可能更合适。这三个打印形式都可以指定打印的宽度和控制打印精度
正无穷Inf、负无穷-Inf、非数NaN
复数
Go语言提供了两种精度的复数类型:complex64 和 complex128。内置的 complex 函数用于构建复数,内建的 real 和 imag 函数分别返回复数的实部和虚部
布尔型
&& 的优先级比 || 高
字符串
一个字符串是一个不可改变的字节序列
内置的 len 函数可以返回一个字符串中的字节数目,索引操作 s[i] 返回第 i 个字节的字节值
字符串切片区间:左闭右开。切片与原字符串共享内存
+操作符可用于拼接字符串
字符串面值
转义
\a 响铃
\b 退格
\f 换页
\n 换行
\r 回车
\t 制表符
\v 垂直制表符
' 单引号(只用在 ''' 形式的rune符号面值中)
" 双引号(只用在 "..." 形式的字符串面值中)
\ 反斜杠
字符串与Byte切片
标准库中有四个包对字符串处理尤为重要:bytes、strings、strconv和unicode包
strings包提供了许多如字符串的查询、替换、比较、截断、拆分和合并等功能
bytes包也提供了很多类似功能的函数,但是针对和字符串有着相同结构的[]byte类型。因为字符串是只读的,因此逐步构建字符串会导致很多分配和复制。在这种情况下,使用bytes.Buffer类型将会更有效
strconv包提供了布尔型、整型数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换
unicode包提供了IsDigit、IsLetter、IsUpper和IsLower等类似功能,它们用于给字符分类。每个函数有一个单一的rune类型的参数,返回一个布尔值。ToUpper和ToLower之类的转换函数将用于rune字符的大小写转换
字符串和数字转换
strconv包提供这类转换功能
fmt.Sprintf返回格式化的字符串 y := fmt.Sprintf("%d", x) // x=123
将一个字符串解析为整数,可以使用strconv包的Atoi或ParseInt函数,还有用于解析无符号整数的ParseUint函数
x, err := strconv.Atoi("123") // x is an int
y, err := strconv.ParseInt("123", 10, 64) // base 10, up to 64 bits 第三个参数是用于指定整型数的大小
fmt.Scanf 可解析输入的字符串和数字,特别是当字符串和数字混合在一行的时候,它可以灵活处理不完整或不规则的输入
常量
可以批量声明多个常量
const (
e = 2.71828182845904523536028747135266249775724709369995957496696763
pi = 3.14159265358979323846264338327950288419716939937510582097494459
)
iota 常量生成器
常量声明可以使用iota常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加一(在其它编程语言中,这种类型一般被称为枚举类型)
type Weekday int
const (
Sunday Weekday = iota // 0
Monday // 1
Tuesday // 2
Wednesday
Thursday
Friday
Saturday
)
无类型常量
无类型的常量不仅可以提供更高的运算精度,而且可以直接用于更多的表达式而不需要显式的类型转换
数组和结构体都是有固定内存大小的数据结构。相比之下,slice和map则是动态的数据结构,它们将根据需要动态增长
数组
长度固定,Slice(切片)是可以增长和收缩的动态序列,slice功能也更灵活
内置的len函数将返回数组中元素的个数
如果在数组的长度位置是“...”省略号,则表示数组的长度是根据初始化值的个数来计算
var a [3]int // array of 3 integers
fmt.Println(a[0]) // print the first element
fmt.Println(a[len(a)-1]) // print the last element, a[2]
q := [...]int{1, 2, 3}
fmt.Printf("%T\n", q) // "[3]int"
可指定一个索引和对应值列表的方式初始化,初始化索引的顺序是无关紧要的
type Currency int
const (
USD Currency = iota // 美元
EUR // 欧元
GBP // 英镑
RMB // 人民币
)
symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"}
fmt.Println(RMB, symbol[RMB]) // "3 ¥"
Slice
Slice(切片)代表变长的序列,序列中每个元素都有相同的类型。一个slice类型一般写作 []T ,其中 T 代表 slice 中元素的类型。一个 slice 由三个部分构成:指针、长度和容量,长度对应slice中元素的数目;长度不能超过容量,容量一般是从slice的开始位置到底层数据的结尾位置。内置的 len 和 cap 函数分别返回 slice 的长度和容量
slice的切片操作 s[i:j] 左闭右开
slice和数组的字面值语法很类似,都是用花括弧包含一系列的初始化元素,但是对于slice并没有指明序列的长度,这会隐式地创建一个合适大小的数组
不能用==判断两个slice是否含有全部相等元素,可以用bytes.Equal函数来判断两个字节型slice是否相等([]byte)
内置的make函数创建一个指定元素类型、长度和容量的slice。容量部分可以省略,在这种情况下,容量将等于长度。
make([]T, len) // T为类型
make([]T, len, cap) // 同make([]T, cap)[:len] slice只引用了底层数组的前len个元素,但是容量将包含整个的数组。额外的元素是留给未来的增长用的
append函数
向 slice 追加元素
appendInt 函数专门用于处理 []int 类型的 slice,每次只能向slice追加一个元素,append函数则可以追加多个元素
Map
无序的 key/value 对的集合。一个 map 就是一个哈希表的引用,map 类型可以写为 map[K]V,map中key/value类型相同,但是key和value之间可以是不同的数据类型
内置的 make 函数可以创建 map:ages := make(map[string]int)
另一种创建空的map的表达式是: map[string]int{}
内置的 delete 函数可以删除元素(如果元素不在map中也可以正常运行):delete(ages, "alice") // remove element ages["alice"]
遍历 map 中全部的 key/value 可以使用 range 风格的 for 循环实现,和之前的 slice 遍历语法类似
sort 包的 Strings 函数对字符串 slice 进行排序sort.Strings(names)
map 类型的零值是 nil。在向 map 存数据前必须先创建 map
结构体
下面两个语句声明了一个叫 Employee 的命名的结构体类型,并且声明了一个 Employee 类型的变量 dilbert,dilbert 结构体变量的成员可以通过点操作符访问
type Employee struct {
ID int
Name string // Name, Address string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}
var dilbert Employee
通常一行对应一个结构体成员,成员的名字在前类型在后,不过如果相邻的成员类型相同的话可以被合并到一行
如果结构体成员名字是以大写字母开头的,那么该成员就是导出的
结构体没有任何成员的话就是空结构体(struct{}),它的大小为0,也不包含任何信息
结构体字面值
1、以结构体成员定义的顺序为每个结构体成员指定一个字面值,但是结构体成员有细微的调整就可能导致上述代码不能编译。在外部包中用此法赋值来初始化结构体中未导出的成员会报错
type Point struct{ X, Y int }
p := Point{1, 2}
2、以成员名字和相应的值来初始化,可以包含部分或全部的成员
如果考虑效率的话,较大的结构体通常会用指针的方式传入和返回;如果要在函数内部修改结构体成员的话,也必须用指针方式传入。在Go语言中,所有的函数参数都是值拷贝传入的,函数参数将不再是函数调用时的原始变量
结构体比较
如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的
可比较的结构体类型和其他可比较的类型一样,可以用于map的key类型
type address struct {
hostname string
port int
}
hits := make(map[address]int)
hits[address{"golang.org", 443}]++
结构体嵌入和匿名成员
type Point struct {
X, Y int
}
type Circle struct {
Center Point
Radius int
}
type Wheel struct {
Circle Circle
Spokes int
}
var w Wheel
w.Circle.Center.X = 8 // 结构体类型清晰了,但是也导致了访问每个成员变得繁琐
w.Circle.Center.Y = 8
w.Circle.Radius = 5
w.Spokes = 20
Go语言有一个特性让我们只声明一个成员对应的数据类型而不指名成员的名字;这类成员就叫匿名成员。匿名成员的数据类型必须是命名的类型或指向一个命名的类型的指针
因此就可以直接访问叶子属性而不需要给出完整的路径
type Circle struct {
Point // Point类型被嵌入到了Circle结构体
Radius int
}
type Wheel struct {
Circle // Circle类型被嵌入到了Wheel结构体
Spokes int
}
// 直接访问叶子属性而不需要给出完整的路径
var w Wheel
w.X = 8 // equivalent to w.Circle.Point.X = 8
w.Y = 8 // equivalent to w.Circle.Point.Y = 8
w.Radius = 5 // equivalent to w.Circle.Radius = 5
w.Spokes = 20
结构体字面值并没有简短表示匿名成员的语法,必须遵循形状类型声明时的结构
w = Wheel{8, 8, 5, 20} // compile error: unknown fields
w = Wheel{X: 8, Y: 8, Radius: 5, Spokes: 20} // compile error: unknown fields
w = Wheel{Circle{Point{8, 8}, 5}, 20}
w = Wheel{
Circle: Circle{
Point: Point{X: 8, Y: 8},
Radius: 5,
},
Spokes: 20, // NOTE: trailing comma necessary here (and at Radius)
}
fmt.Printf("%#v\n", w)
// Output:
// Wheel{Circle:Circle{Point:Point{X:8, Y:8}, Radius:5}, Spokes:20}
JSON
JSON是对JavaScript中各种类型的值——字符串、数字、布尔值和对象——Unicode本文编码
一个JSON数组是一个有序的值序列,写在一个方括号中并以逗号分隔;一个JSON数组可以用于编码Go语言的数组和slice。一个JSON对象是一个字符串到值的映射,写成一系列的name:value对形式,用花括号包含并以逗号分隔;JSON的对象类型可以用于编码Go语言的map类型(key类型是字符串)和结构体
结构体slice转为JSON的过程叫编组(marshaling),编组通过调用json.Marshal函数完成,返回一个编码后的字节slice,包含很长的字符串,并且没有空白缩进
json.MarshalIndent函数将产生整齐缩进的输出,该函数有两个额外的字符串参数用于表示每一行输出的前缀和每一个层级的缩进
在编码时,默认使用Go语言结构体的成员名字作为JSON的对象,只有导出的结构体成员才会被编码
编码的逆操作是解码,对应将JSON数据解码为Go语言的数据结构,Go语言中一般叫unmarshaling,通过json.Unmarshal函数完成
许多web服务都提供JSON接口,通过HTTP接口发送JSON格式请求并返回JSON格式的信息
文本和HTML模块
复杂打印格式,由text/template和html/template等模板包提供,提供了一个将变量值填充到一个文本或HTML格式的模板的机制
对于每一个action,都有一个当前值的概念,对应点操作符,写作“.”。当前值“.”最初被初始化为调用模板时的参数
在一个action中,|操作符表示将前一个表达式的结果作为后一个函数的输入,类似于UNIX中管道的概念