Go的基本数据类型
数字数据类型
整数
Go 支持 int8, int16, int32, int64, uint8, uint16, uint32, 以及 uint64。
一般直接用 int 和 unint 。
另外,byte 是 uint8 的别名。
浮点数
Go 支持 float32 和 float64 。前者保证小数点后6位,后者保证15位的精度。
复数
Go 支持 complex64 和 complex128 。前者用两个 float32 来分别表示实数和虚数部分。后者用两个 float64 来分别表示实数和虚数部分。
示例代码
package main
import (
"fmt"
)
func main() {
c1 := 12 + 1i
c2 := complex(5, 7)
fmt.Printf("Type of c1: %T\n", c1)
fmt.Printf("Type of c2: %T\n", c2)
var c3 complex64 = complex64(c1 + c2)
fmt.Println("c3:", c3)
fmt.Printf("Type of c3: %T\n", c3)
cZero := c3 - c3
fmt.Println("cZero:", cZero)
x := 12
k := 5
fmt.Println(x)
fmt.Printf("Type of x: %T\n", x)
div := x / k
fmt.Println("div", div)
var m, n float64
m = 1.223
fmt.Println("m, n:", m, n)
y := 4 / 2.3
fmt.Println("y:", y)
divFloat := float64(x) / float64(k)
fmt.Println("divFloat", divFloat)
fmt.Printf("Type of divFloat: %T\n", divFloat)
}
示例代码
package main
import (
"fmt"
)
func main() {
for i := 0; i < 100; i++ {
if i%20 == 0 {
continue
}
if i == 95 {
break
}
fmt.Print(i, " ")
}
fmt.Println()
i := 10
for {
if i < 0 {
break
}
fmt.Print(i, " ")
i--
}
fmt.Println()
i = 0
anExpression := true
for ok := true; ok; ok = anExpression {
if i > 10 {
anExpression = false
}
fmt.Print(i, " ")
i++
}
fmt.Println()
anArray := [5]int{0, 1, -1, 2, -2}
for i, value := range anArray {
fmt.Println("index:", i, "value: ", value)
}
}
字符串
和C语言不同,字符串在Go中是值类型。并且Go默认支持UTF-8。
在Go中,字符串是只读的byte slice。字符串中的元素可以是character, rune或者byte。
可以通过len()函数得到字符串的长度。
const sLiteral = "\x99\x42\x32\x55\x50\x35\x23\x50\x29\x9c"
s2 := "€£³"
示例代码
package main
import (
"fmt"
)
func main() {
const sLiteral = "\x99\x42\x32\x55\x50\x35\x23\x50\x29\x9c"
fmt.Println(sLiteral)
fmt.Printf("x: %x\n", sLiteral)
fmt.Printf("sLiteral length: %d\n", len(sLiteral))
for i := 0; i < len(sLiteral); i++ {
fmt.Printf("%x ", sLiteral[i])
}
fmt.Println()
fmt.Printf("q: %q\n", sLiteral)
fmt.Printf("+q: %+q\n", sLiteral)
fmt.Printf(" x: % x\n", sLiteral)
fmt.Printf("s: As a string: %s\n", sLiteral)
s2 := "€£³"
for x, y := range s2 {
fmt.Printf("%#U starts at byte position %d\n", y, x)
}
fmt.Printf("s2 length: %d\n", len(s2))
const s3 = "ab12AB"
fmt.Println("s3:", s3)
fmt.Printf("x: % x\n", s3)
fmt.Printf("s3 length: %d\n", len(s3))
for i := 0; i < len(s3); i++ {
fmt.Printf("%x ", s3[i])
}
fmt.Println()
}
rune
rune是一个int32的值,它用来表示一个Unicode code point。一个Unicode code point 或者 code position 是一个值一般用来代表一个Unicode字符,也会用来提供格式信息。
rune一般用单引号括起来。
代码示例:
package main
import (
"fmt"
)
func main() {
const r1 = '€'
fmt.Println("(int32) r1:", r1)
fmt.Printf("(HEX) r1: %x\n", r1)
fmt.Printf("(as a String) r1: %s\n", r1)
fmt.Printf("(as a character) r1: %c\n", r1)
fmt.Println("A string is a collection of runes:", []byte("Mihalis"))
aString := []byte("Mihalis")
for x, y := range aString {
fmt.Println(x, y)
fmt.Printf("Char: %c\n", aString[x])
}
fmt.Printf("%s\n", aString)
}
unicode
包
unicode包中有一个函数 unicode.IsPrint() 可以用来识别一个字符串中指定部分中的rune是否可打印。
package main
import (
"fmt"
"unicode"
)
func main() {
const sL = "\x99\x00ab\x50\x00\x23\x50\x29\x9c"
for i := 0; i < len(sL); i++ {
if unicode.IsPrint(rune(sL[i])) {
fmt.Printf("%c\n", sL[i])
} else {
fmt.Println("Not printable!")
}
}
}
strings
包
strings 包是Go标准库中用来处理UTF-8字符串的。
package main
import (
"fmt"
s "strings" //创建一个包的别名
"unicode"
)
var f = fmt.Printf // 创建一个函数别名
func main() {
upper := s.ToUpper("Hello there!")
f("To Upper: %s\n", upper)
f("To Lower: %s\n", s.ToLower("Hello THERE"))
f("%s\n", s.Title("tHis wiLL be A title!"))
f("EqualFold: %v\n", s.EqualFold("Mihalis", "MIHAlis"))
f("EqualFold: %v\n", s.EqualFold("Mihalis", "MIHAli"))
f("Prefix: %v\n", s.HasPrefix("Mihalis", "Mi"))
f("Prefix: %v\n", s.HasPrefix("Mihalis", "mi"))
f("Suffix: %v\n", s.HasSuffix("Mihalis", "is"))
f("Suffix: %v\n", s.HasSuffix("Mihalis", "IS"))
f("Index: %v\n", s.Index("Mihalis", "ha"))
f("Index: %v\n", s.Index("Mihalis", "Ha"))
f("Count: %v\n", s.Count("Mihalis", "i"))
f("Count: %v\n", s.Count("Mihalis", "I"))
f("Repeat: %s\n", s.Repeat("ab", 5))
f("TrimSpace: %s\n", s.TrimSpace(" \tThis is a line. \n"))
f("TrimLeft: %s", s.TrimLeft(" \tThis is a\t line. \n", "\n\t "))
f("TrimRight: %s\n", s.TrimRight(" \tThis is a\t line. \n", "\n\t "))
f("Compare: %v\n", s.Compare("Mihalis", "MIHALIS"))
f("Compare: %v\n", s.Compare("Mihalis", "Mihalis"))
f("Compare: %v\n", s.Compare("MIHALIS", "MIHalis"))
f("Fields: %v\n", s.Fields("This is a string!"))
f("Fields: %v\n", s.Fields("Thisis\na\tstring!"))
f("%s\n", s.Split("abcd efg", ""))
f("%s\n", s.Replace("abcd efg", "", "_", -1))
f("%s\n", s.Replace("abcd efg", "", "_", 4))
f("%s\n", s.Replace("abcd efg", "", "_", 2))
lines := []string{"Line 1", "Line 2", "Line 3"}
f("Join: %s\n", s.Join(lines, "+++"))
f("SplitAfter: %s\n", s.SplitAfter("123++432++", "++"))
trimFunction := func(c rune) bool {
return !unicode.IsLetter(c)
}
f("TrimFunc: %s\n", s.TrimFunc("123 abc ABC \t .", trimFunction))
}
数组
示例代码
anArray := [4]int{1, 2, 4, -4}
可以用 len() 函数来获取数组大小。
len(anArray)
数组元素索引从0开始,有效范围是 0 ~ len(anArray) - 1 。
Go 的数组一旦定义,大小就是固定了。将数组作为参数传递给函数时,是传递的数组的拷贝,所以对于比较大的数组,将会造成性能上的损失。
因此,在Go 编程中,数组常常被slice所替换。
多维数组
twoD := [4][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}
threeD := [2][2][2]int{{{1, 0}, {-2, 4}}, {{5, -1}, {7, 0}}}
代码示例
package main
import (
"fmt"
)
func main() {
anArray := [4]int{1, 2, 4, -4}
twoD := [4][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}
threeD := [2][2][2]int{{{1, 0}, {-2, 4}}, {{5, -1}, {7, 0}}}
fmt.Println("The length of", anArray, "is", len(anArray))
fmt.Println("The first element of", twoD, "is", twoD[0][0])
fmt.Println("The length of", threeD, "is", len(threeD))
for i := 0; i < len(threeD); i++ {
v := threeD[i]
for j := 0; j < len(v); j++ {
m := v[j]
for k := 0; k < len(m); k++ {
fmt.Print(m[k], " ")
}
}
fmt.Println()
}
for _, v := range threeD { // '_' 是占位符
for _, m := range v {
for _, s := range m {
fmt.Print(s, " ")
}
}
fmt.Println()
}
}
slice
在Go 编程中,除了个别场景会用到数组以外,一般都用slice。就想在C++中,我们一般都用vector一样。并且和C++的vector一样,slice的size会自动扩展。
和数组不同,slice是按引用传递给函数的。
基本操作
定义
aSliceLiteral := []int{1, 2, 3, 4, 5}
和数组的定义类似,但是没有指定size。
用 make() 函数创建
s := make([]int, 20)
Go会自动初始化slice,将其中的元素都设为 0 ,对象的话,会设为 nil 。
清空
s := nil
添加
s = append(s, 12)
截取
用 [ : ] 来得到slice的指定连续部分
s2 := s[1:3]
这个表达式称为重分片(re-slicing),但是需要注意的是,重分片并没有做拷贝操作,对s2的修改将导致原slice的修改。
示例代码
package main
import "fmt"
func main() {
s1 := make([]int, 5)
reSlice := s1[1:3]
fmt.Println(s1)
fmt.Println(reSlice)
reSlice[0] = -100
reSlice[1] = 123456
fmt.Println(s1)
fmt.Println(reSlice)
}
重分片的另一个需要注意点是,子slice会导致原slice驻留内存,直到子slice被释放。比如,在当你用slice读取一个大文件,而只使用其中一小部分的时候。
len() 和 cap()
分别获取元素个数和slice的容量。
示例代码
package main
import (
"fmt"
)
func printSlice(x []int) {
for _, number := range x {
fmt.Print(number, " ")
}
fmt.Println()
}
func main() {
aSlice := []int{-1, 0, 4}
fmt.Printf("aSlice: ")
printSlice(aSlice)
fmt.Printf("Cap: %d, Length: %d\n", cap(aSlice), len(aSlice))
aSlice = append(aSlice, -100)
fmt.Printf("aSlice: ")
printSlice(aSlice)
fmt.Printf("Cap: %d, Length: %d\n", cap(aSlice), len(aSlice))
aSlice = append(aSlice, -2)
aSlice = append(aSlice, -3)
aSlice = append(aSlice, -4)
printSlice(aSlice)
fmt.Printf("Cap: %d, Length: %d\n", cap(aSlice), len(aSlice))
}
拷贝
可以用 copy() 来创建slice的拷贝。
注意,Go自带的copy函数,只拷贝目标和源slice中最少数量的元素。也就是说,copy操作不会修改目标slice的大小。
示例代码
package main
import (
"fmt"
)
func main() {
a6 := []int{-10, 1, 2, 3, 4, 5}
a4 := []int{-1, -2, -3, -4}
fmt.Println("a6:", a6)
fmt.Println("a4:", a4)
copy(a6, a4)
fmt.Println("a6:", a6)
fmt.Println("a4:", a4)
fmt.Println()
b6 := []int{-10, 1, 2, 3, 4, 5}
b4 := []int{-1, -2, -3, -4}
fmt.Println("b6:", b6)
fmt.Println("b4:", b4)
copy(b4, b6)
fmt.Println("b6:", b6)
fmt.Println("b4:", b4)
fmt.Println()
array4 := [4]int{4, -4, 4, -4}
s6 := []int{1, 1, -1, -1, 5, -5}
copy(s6, array4[0:])
fmt.Println("array4:", array4[0:])
fmt.Println("s6:", s6)
fmt.Println()
array5 := [5]int{5, -5, 5, -5, 5}
s7 := []int{7, 7, -7, -7, 7, -7, 7}
copy(array5[0:], s7)
fmt.Println("array5:", array5)
fmt.Println("s7:", s7)
}
技巧:
通过[:] 将数组转成 slice。
排序
通过sort.Slice() 函数。
代码示例
package main
import (
"fmt"
"sort"
)
type aStructure struct {
person string
height int
weight int
}
func main() {
mySlice := make([]aStructure, 0)
mySlice = append(mySlice, aStructure{"Mihalis", 180, 90})
mySlice = append(mySlice, aStructure{"Bill", 134, 45})
mySlice = append(mySlice, aStructure{"Marietta", 155, 45})
mySlice = append(mySlice, aStructure{"Epifanios", 144, 50})
mySlice = append(mySlice, aStructure{"Athina", 134, 40})
fmt.Println("0:", mySlice)
sort.Slice(mySlice, func(i, j int) bool {
return mySlice[i].height < mySlice[j].height
})
fmt.Println("<:", mySlice)
sort.Slice(mySlice, func(i, j int) bool {
return mySlice[i].height > mySlice[j].height
})
fmt.Println(">:", mySlice)
}
byte slice
s := make([]byte, 5)
在Go中,byte slice一般用来保存字符串。并且Go提供了方便的方法,让你在byte slice和string类型间相互转换。
byte slice主要用于文件的读写。
多维slice
s1 := make([][]int, 4)
代码示例
package main
import (
"fmt"
)
func main() {
aSlice := []int{1, 2, 3, 4, 5}
fmt.Println(aSlice)
integers := make([]int, 2)
fmt.Println(integers)
integers = nil
fmt.Println(integers)
anArray := [5]int{-1, -2, -3, -4, -5}
refAnArray := anArray[:]
fmt.Println(anArray)
fmt.Println(refAnArray)
anArray[4] = -100
fmt.Println(refAnArray)
s := make([]byte, 5)
fmt.Println(s)
twoD := make([][]int, 3)
fmt.Println(twoD)
fmt.Println()
for i := 0; i < len(twoD); i++ {
for j := 0; j < 2; j++ {
twoD[i] = append(twoD[i], i*j)
}
}
for _, x := range twoD {
for i, y := range x {
fmt.Println("i:", i, "value:", y)
}
fmt.Println()
}
}
合并
package main
import (
"fmt"
)
func main() {
s := []int{1, 2, 3}
a := [3]int{4, 5, 6}
ref := a[:]
fmt.Println("Existing array:\t", ref)
t := append(s, ref...)
fmt.Println("New slice:\t", t)
s = append(s, ref...)
fmt.Println("Existing slice:\t", s)
s = append(s, s...)
fmt.Println("s+s:\t\t", s)
}
注意ref后面的 ‘…’ ,用它来unpack前面的ref。
map
Go中的map类似于其他语言的hash table。
创建map
m = make(map[string]int) // key是string类型,value是int类型
//or
m2 := map[string]int {
"k1": 12,
"k2": 15,
}
访问元素
var v = m2["k1"]
删除元素
delete(m2, "k1")
遍历元素
for key, value := range m2 {
fmt.Println(key, value)
}
示例代码
package main
import (
"fmt"
)
func main() {
iMap := make(map[string]int)
iMap["k1"] = 12
iMap["k2"] = 13
fmt.Println("iMap:", iMap)
anotherMap := map[string]int{
"k1": 12,
"k2": 13,
}
fmt.Println("anotherMap:", anotherMap)
delete(anotherMap, "k1")
delete(anotherMap, "k1")
delete(anotherMap, "k1") // key可以多次删除
fmt.Println("anotherMap:", anotherMap)
_, ok := iMap["doesItExist"] // 编码技巧:判断key是否存在
// 当key不存在map中时,map会返回0,因此我们无法得知key是否存在。
if ok {
fmt.Println("Exists!")
} else {
fmt.Println("Does NOT exist")
}
for key, value := range iMap {
fmt.Println(key, value)
}
}
常量
和C++或者C#一样,用const关键词来修饰常量。
const PI = 3.14
// go比较特别的写法
const (
C1 = "1000"
C2 = "2000"
C3 = "3000"
)
const s1 = 123
const s2 float64 = 123 // 这种写法,在语法上更严格
常量生成器 iota
iota只能用在const scope中,总是从0开始产生累加值。在下面的示例代码中有其用法的展示。
示例代码
package main
import (
"fmt"
)
type Digit int
type Power2 int
const PI = 3.1415926
const (
C1 = "C1C1C1"
C2 = "C2C2C2"
C3 = "C3C3C3"
)
func main() {
const s1 = 123
var v1 float32 = s1 * 12
fmt.Println(v1)
fmt.Println(PI)
const (
Zero Digit = iota
One
Two
Three
Four
)
fmt.Println(One)
fmt.Println(Two)
const (
p2_0 Power2 = 1 << iota
_
p2_2
_
p2_4
_
p2_6
)
fmt.Println("2^0:", p2_0)
fmt.Println("2^2:", p2_2)
fmt.Println("2^4:", p2_4)
fmt.Println("2^6:", p2_6)
}
指针
和C/C++一样,用 * 号取指针值,用 & 号取变量地址。
代码示例
package main
import (
"fmt"
)
func getPointer(n *int) {
*n = *n * *n
}
func returnPointer(n int) *int {
v := n * n
return &v
}
func main() {
i := -10
j := 25
pI := &i
pJ := &j
fmt.Println("pI memory:", pI)
fmt.Println("pJ memory:", pJ)
fmt.Println("pI value:", *pI)
fmt.Println("pJ value:", *pJ)
*pI = 123456
*pI--
fmt.Println("i:", i)
getPointer(pJ)
fmt.Println("j:", j)
k := returnPointer(12)
fmt.Println(*k)
fmt.Println(k)
}
时间和日期
首先需要引入 time
package。
time
package中时间的最小单位是纳秒。
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Epoch time:", time.Now().Unix()) // 返回Unix epoch时间
t := time.Now()
fmt.Println(t, t.Format(time.RFC3339))
fmt.Println(t.Weekday(), t.Day(), t.Month(), t.Year())
time.Sleep(time.Second) // wait for 1 second
t1 := time.Now()
fmt.Println("Time difference:", t1.Sub(t)) // time.Sub() 用来计算时间差
formatT := t.Format("01 January 2006")
fmt.Println(formatT)
loc, _ := time.LoadLocation("Europe/Paris")
londonTime := t.In(loc)
fmt.Println("Paris:", londonTime)
}
解析时间和日期
用time.Parse()
package main
import (
"fmt"
"os"
"path/filepath"
"time"
)
func main() {
var myTime string
if len(os.Args) != 2 {
fmt.Printf("usage: %s string\n", filepath.Base(os.Args[0]))
os.Exit(1)
}
myTime = os.Args[1]
d, err := time.Parse("15:04", myTime)
if err == nil {
fmt.Println("Full:", d)
fmt.Println("Time:", d.Hour(), d.Minute())
} else {
fmt.Println(err)
}
}
package main
import (
"fmt"
"os"
"path/filepath"
"time"
)
func main() {
var myDate string
if len(os.Args) != 2 {
fmt.Printf("usage: %s string\n", filepath.Base(os.Args[0]))
return
}
myDate = os.Args[1]
d, err := time.Parse("02 January 2006", myDate)
if err == nil {
fmt.Println("Full:", d)
fmt.Println("Time:", d.Day(), d.Month(), d.Year())
} else {
fmt.Println(err)
}
}
改变时间日期格式
package main
import (
"fmt"
"regexp"
"time"
)
func main() {
logs := []string{"127.0.0.1 - - [16/Nov/2017:10:49:46 +0200] 325504", "127.0.0.1 - - [16/Nov/2017:10:16:41 +0200] \"GET /CVEN HTTP/1.1\" 200 12531 \"-\" \"Mozilla/5.0 AppleWebKit/537.36", "127.0.0.1 200 9412 - - [12/Nov/2017:06:26:05 +0200] \"GET \"http://www.mtsoukalos.eu/taxonomy/term/47\" 1507",
"[12/Nov/2017:16:27:21 +0300]",
"[12/Nov/2017:20:88:21 +0200]",
"[12/Nov/2017:20:21 +0200]",
}
for _, logEntry := range logs {
r := regexp.MustCompile(`.*\[(\d\d\/\w+/\d\d\d\d:\d\d:\d\d:\d\d.*)\].*`)
if r.MatchString(logEntry) {
match := r.FindStringSubmatch(logEntry)
dt, err := time.Parse("02/Jan/2006:15:04:05 -0700", match[1])
if err == nil {
newFormat := dt.Format(time.RFC850)
fmt.Println(newFormat)
} else {
fmt.Println("Not a valid date time format!")
}
} else {
fmt.Println("Not a match!")
}
}
}
测量运行时间
用time.Since(start_time)
package main
import (
"fmt"
"time"
)
func main() {
start := time.Now()
time.Sleep(time.Second)
duration := time.Since(start)
fmt.Println("It took time.Sleep(1)", duration, "to finish.")
start = time.Now()
time.Sleep(2 * time.Second)
duration = time.Since(start)
fmt.Println("It took time.Sleep(2)", duration, "to finish.")
start = time.Now()
for i := 0; i < 200000000; i++ {
_ = i
}
duration = time.Since(start)
fmt.Println("It took the for loop", duration, "to finish.")
sum := 0
start = time.Now()
for i := 0; i < 200000000; i++ {
sum += i
}
duration = time.Since(start)
fmt.Println("It took the for loop", duration, "to finish.")
}
(end.)