zoukankan      html  css  js  c++  java
  • Golang学习笔记

    https://www.cnblogs.com/Xjng/p/5913954.html

    一、基础

    1. Hello World程序

    demo:

    package main
    import "fmt" // 注释
    //注释
    func main() {
        fmt.Printf("Hello World
    ")
    }

    执行:
    go run demo.go

    编译成可执行文件
    go build demo.go

    2. 声明和赋值

    func main() {
        var a int
        var b string = "aaaa"
        var (
            c int
            d bool
        )
        conse i = 10
        e := 15
        a = 1
        b = "hello"
        c = 2
        d = false
        f,g:=12,13
        if (a + c + e < 100 && d) {
            fmt.Printf("Hello World
    ")
        } else {
            fmt.Printf(b)
        }
    
    }
    • 变量的类型在变量名后面,所以不能同时声明和赋值
    • 在2.4后,支持a:=1这种类型,类似于动态类型的声明了,这时会自动识别变量的类型
    • 可以在var里面声明多个变量
    • 声明了的变量一定要用,不然编译会错误
    • const定义常量,类似var,而已可以定义多个

    字符串转换

    func main() {
        var s string ="aaaaaa"
        sb:=[]byte(s)
        s1:=string(sb)
        fmt.Printf("%c
    ",sb[0])
        fmt.Printf("%s
    ",s1)
        fmt.Printf("%s
    ",s)
    }

    直接访问字符串的下标是不可以的,需要先转换为byte类型,通过string函数转换回来。

    其他操作符

    Precedence Operator(s)
    Highest :

    * / % << >> & &^
    + - | ^
    == != < <= > >=
    <-
    &&

    Lowest

    ||

    3. 控制结构

    3.1. if

    func main() {
        if a:=1;a<0{
            fmt.Printf("a is less 0")
        }else{
            fmt.Printf("a is bigger 0")
        }
    }
    • and &&
    • or ||
    • un !=
    • ==
    • <= >=

      2. for

      for有三种形式
    • for init;condition;post {} 类似C的for
    • for condition {} 类似C的while
    • for {} 类似C的for(;;) 死循环

    golang中没有do和while

    func main() {
        for i := 0; i < 10; i++ {
            fmt.Printf("i:%d
    ", i)
        }
        j := 0
        for j < 10 {
            fmt.Printf("j:%d
    ", j)
            j++
        }
        k := 0
        for {
            fmt.Printf("k:%d
    ", k)
            k++
            if k > 9 {
                break
            }
        }
    }

    rang可以方便地遍历对象

    l := []string{"a", "b", "c"}
    for k, v := range l {
        fmt.Printf("pos:%d,value:%s
    ", k, v)
    }

    switch

    func main() {
        i := 0
        switch i {
        case -1, 0:
            fmt.Printf("is -1 or 0")
        case 1:
            fmt.Printf("is 1")
        default:
            fmt.Printf("default")
        }
    }
    • case中逗号表示或

    4. 预定义的函数

    Table 2.3. Go 中的预定义函数
    close new panic complex
    closed make recover real
    len append print imag
    cap copy println
    • len 和cap 可用于不同的类型,len 用于返回字符串、slice 和数组的长度。参阅”array、slices和map” 小节了解更多关于slice、数组和函数cap 的详细信息。
    • new 用于各种类型的内存分配。参阅”用new 分配内存” 在59 页。
    • make 用于内建类型(map、slice 和channel)的内存分配。参阅”用make 分配内存” 在59
      页。
    • copy 用于复制slice。append 用于追加slice。参阅本章的”slice”。
    • panic 和recover 用于异常处理机制。参阅”恐慌(Panic)和恢复(Recover)” 在37 页了
      解更多信息。
    • print 和println 是底层打印函数,可以在不引入fmt 包的情况下使用。它们主要用于调
      试。
    • complex、real和imag 全部用于处理复数。有了之前给的简单的例子,不用再进一步讨论
      复数了。

    5. array,slice和map

    array就是Python的数组
    map就是Python的字典

    5.1 array

    array使用[n]定义,其中n是数组的大小,type是元素的类型。n是可选的。
    数组的定义和使用。

    l1:=[]string{"a","b"}
    var l2 [2]int
    l2[0]=1
    l2[1]=2
    var l3 [2][3]int
    l3[0][0]=1
    print(l1[0])
    print(l2[0])
    print(l3[0][0])

    当传递一个array给函数的时候,函数得到的是一个array的副本,即传值。

    5.2 slice(切片)

    slice和array类似,不同的是slice是array的一个指针,所以修改slice,是会影响array的,而且传递一个slice给函数的时候,传递的是指针,所以是传址。

    l1:=[]string{"a","b","c","d"}
    s1:=l1[1:2]
    s2:=l1[:] //类似l1[0:4]
    s3:=l1[:2] //类似l1[0:2]
    print(s1[0])
    print(s2[0])
    print(s3[0])

    append用户向切片中添加元素,返回新的切片,新的切片的内存地址可能和之前的不一样。

    l1:=[]string{"a","b","c","d"}
    s2:=append(l1,"e","f")
    print(s2[4])
    print(s2[5])

    5.3 map

    map的定义:map[<from type>]<to type>

    var map1 = make(map[string]int)
    map2:=map[string]int{
        "k1":11,"k2":12,
    }
    print(map2)
    map1["k1"] = 12
    v, ok := map1["k1"] //12 true
    print(v, ok,"
    ")
    v1, ok1 := map1["k2"] //0 false
    print(v1, ok1,"
    ")
    //map1["k2"] = 0, false //删除,不知道为什么测试失败

    遍历:

    map2:=map[string]int{
        "k1":11,"k2":12,
    }
    for k,v:=range map2{
        print(k,v,"
    ")
    }

    二、函数

    func (p mytype) funcname(q int) (r,s int) { return 0,0 }
    1. 保留字func 用于定义一个函数;
    2. 函数可以定义用于特定的类型,这类函数更加通俗的称呼是method。这部分称
      作receiver 而它是可选的。它将在6 章使用;
    3. funcname是你函数的名字;
    4. int 类型的变量q 是输入参数。参数用pass-by-value 方式传递,意味着它们会被复
      制;
    5. 变量r 和s 是这个函数的named return parameters。在Go 的函数中可以返回多个
      值。参阅”多个返回值” 在32。如果想要返回无命名的参数,只需要提供类型:(int,
      int)。如果只有一个返回值,可以省略圆括号。如果函数是一个子过程,并且没有任
      何返回值,也可以省略这些内容;
    6. 这是函数体,注意return 是一个语句,所以包裹参数的括号是可选的。

    DEMO:

    func add(a int,b int) (int,int,int){
        return a+b,a,b
    }
    func main() {
        a,b,c:=add(12,13)
        print(a,b,c)
    
    }

    函数的参数都是传值的形式。

    1. 命名返回的参数

    func add(a int,b int) (int){
        sum:=a+b
        return sum
    }
    func add(a int,b int) (sum int){
        sum=a+b
        return
    }

    在定义函数的返回类型的时候,加上类型对应的变量名,然后在函数体中,return后面不带参数,这样go就会找到函数体中的变量sum,然后返回。注意,由于定义函数的时候已经定义了sum变量,所以后面修改的时候不需要加冒号。

    2. 定义函数退出前执行的函数

    例如在打开文件的时候,每一次返回都需要关闭文件描述符,这样会有大量代码重复,在go中,可以定义函数退出前执行的函数。

    func test(a int) (sum int){
        defer print("test done")
        if a<0{
            return -1
        }else{
            return 1
        }
    }

    这样无论a是大于还是小于0,都会输出文字。

    3.可变参数

    类似于Python的*args

    func test(args ...int) (int) {
        for i, v := range args {
            print(i, v, "
    ")
        }
        return 1
    }
    func main() {
        test(1, 2, 3, 4, 5)
    }

    4. 快速定义函数

    类似于Python的lambda

    add_one := func(i int) int {
        return 1 + i
    }
    print(add_one(2))

    5.函数作为参数

    func test(i int, fun func(int) int) (int) {
        i++
        return fun(i)
    }
    func main() {
        add_one := func(i int) int {
            return 1 + i
        }
        print(test(2,add_one))
    }

    最后的值是4,

    6.恐慌和恢复

    go中没有异常的处理,只有恐慌和恢复

    func thrownPanic(fun func() int) (b bool) {
        defer func() {
            if r := recover(); r != nil {
                b = true
            }
        }()
        fun()
        return
    }
    func main() {
        add_one := func() int {
            a := []int{1, 2, 3}
            print(a[0])
            return 1
        }
        print(thrownPanic(add_one))
    }

    在thrownPanic中,会调用fun,然后在函数结束前执行defer的函数,如果fun中产生了异常,r会为非nil,这样返回true,否则返回false
    这样外层的函数就能知道调用fun是否产生了异常。

        "runtime/debug"
        "reflect"
        "fmt"
    )
    func test_func(){
        defer func(){
            if err:=recover();err!=nil{
                fmt.Println("Panic ",err,reflect.TypeOf(err))
                debug.PrintStack()
            }
        }()
        list:=[]int{1}
        println(list[1])
    }
    func main(){
        test_func()

    程序在执行println(list[1])的时候,会产生恐慌,也就是异常,但是程序不会立刻退出,还会执行defer的函数,这时,通过revocer函数,可以catch住这个异常,然后把异常信息打印出来,这样程序可以继续正常运行,其实跟try exept差不多。

    三、包

    包说明

    目录结构

    /
        test.go
        /util
            util1.go
            util2.go

    util1.go:

    package util
    
    func add(a int, b int) int {
        //私有函数,只能在包内被调用
        return a + b
    }
    func Add(a int, b int) int {
        //公有函数,可以在其他包中调用
        return a + b
    }

    test.go

    package main
    import "./util" // 注释
    //注释
    func main() {
        print(util.Add(12,13))
    }

    有多个地方用到util这个名字:

    1. test.go中的import
    2. test.go中调用Add时的前缀
    3. utils1.go中的package 名字
    4. utils1.go的文件名

    其中1,2,3需要一样,4可以不一样

    在包中,变量或者函数名,根据首字母是否大写来判断该变量或函数是否公有的
    在一个包中,也就是文件夹,不同的文件中的变量或函数名不能重复。

    别名

    import u "./util" // 注释
    //注释
    func main() {
        print(u.Add(12,13))
    }

    导入util并设置别名为u

    package main
    import . "./util" // 注释
    //注释
    func main() {
        print(Add(12,13))
    }

    别名设置为点,就不需要名字了

    导入路径

    上面的导入方法是相对路径导入,即在util前面加上./
    还有绝对路径的导入

     import   "shorturl/model"       //加载GOROOT/src/shorturl/model模块

    包的文档

    每个包都应该包含文档,如果包中有多个文件,文档可以在任意一个。格式:

    /*
    The regexp package implements a simple library for
    regular expressions.
    The syntax of the regular expressions accepted is:
    regexp:
    concatenation '|' concatenation
    */
    package regexp

    单元测试

    在util目录下面创建文件util_test.go:

    package util
    import "testing"
    func TestAdd(t *testing.T){
        if Add(12,13)!=24 {
            t.Log("test Add 12 13 fail")
            t.Fail()
        }
    }

    然后cd到util目录,执行go test,这样go就会调用所有*_test.go文件里面的Test*函数。在函数里面,如果测试失败,就调用t.Fail()

    常用的包

    标准的Go 代码库中包含了大量的包,并且在安装Go 的时候多数会伴随一起安装。浏
    览$GOROOT/src/pkg 目录并且查看那些包会非常有启发。无法对每个包就加以解说,不过下
    面的这些值得讨论:b

    • fmt
      包fmt 实现了格式化的I/O 函数,这与C 的printf 和scanf 类似。格式化短语派生于C
      。一些短语(%-序列)这样使用:
      %v
      默认格式的值。当打印结构时,加号(%+v)会增加字段名;
      %#v
      Go 样式的值表达;
      %T
      带有类型的Go 样式的值表达;
    • io
      这个包提供了原始的I/O 操作界面。它主要的任务是对os 包这样的原始的I/O 进行封
      装,增加一些其他相关,使其具有抽象功能用在公共的接口上。
    • bufio
      这个包实现了缓冲的I/O。它封装于io.Reader 和io.Writer 对象,创建了另一个对象
      (Reader 和Writer)在提供缓冲的同时实现了一些文本I/O 的功能。
    • sort
      sort 包提供了对数组和用户定义集合的原始的排序功能。
      b描述来自包的godoc。额外的解释用斜体。
      54 Chapter 4: 包
    • strconv
      strconv 包提供了将字符串转换成基本数据类型,或者从基本数据类型转换为字符串
      的功能。
    • os
      os 包提供了与平台无关的操作系统功能接口。其设计是Unix 形式的。
    • sync
      sync 包提供了基本的同步原语,例如互斥锁。
    • flag
      flag 包实现了命令行解析。参阅”命令行参数” 在第92 页。
    • json
      json 包实现了编码与解码RFC 4627 [22] 定义的JSON 对象。
    • template
      数据驱动的模板,用于生成文本输出,例如HTML。
      将模板关联到某个数据结构上进行解析。模板内容指向数据结构的元素(通常结构的
      字段或者map 的键)控制解析并且决定某个值会被显示。模板扫描结构以便解析,而
      “游标”@ 决定了当前位置在结构中的值。
    • http
      http 实现了HTTP 请求、响应和URL 的解析,并且提供了可扩展的HTTP 服务和基本
      的HTTP 客户端。
    • unsafe
      unsafe 包包含了Go 程序中数据类型上所有不安全的操作。通常无须使用这个。
    • reflect
      reflect 包实现了运行时反射,允许程序通过抽象类型操作对象。通常用于处理静态类
      型interface{} 的值,并且通过Typeof 解析出其动态类型信息,通常会返回一个有接
      口类型Type 的对象。包含一个指向类型的指针,StructType、IntType 等等,描述
      了底层类型的详细信息。可以用于类型转换或者类型赋值。参阅6,第”自省和反射”
      节。
    • exec
      exec 包执行外部命令。

    四、进阶

    1. 指针

    go中也有指针,但是和C有区别,不能进行指针运算。

    var p *int; //p=nil
    //*p = 8; //这样会报错,因为p还没有分配内存
    var i int;
    p = &i; //令p的值等于i的内存值
    *p = 8; //相当于修改i的值
    print(p, &i, i)//看到,p和*i是一样的,i=8
    • 在类型的前面加星号,表示定义一个该类型的指针,定义之后,没有为指针分配内存,指针为nil

    2. 内存分配

    go中有两种方法可以分配内存:new和make

    2.1 new

    new是声明一个变量,返回变量的指针。

    var p1 *int; //p=nil
    p2 := new(int)
    var p3 int
    print(p1,"
    ")
    print(*p2,"
    ")
    print(p3,"
    ")

    p1是一个指针,但是它还没有初始化
    p2也是一个指针,它已经初始化了,初始化值为0
    p3是一个变量,已经初始化,初始化值为0

    2.2 make

    make只能声明slice,map和channel。返回值,而不是指针。
    也可以使用new来声明指针,然后使用make来初始化:

    p2 := new([]int)
    //(*p2)[0]=1 //这样是不行的,因为p2还没有初始化
    *p2=make([]int,11)
    (*p2)[0]=1
    print((*p2)[0])

    make([]int,11)声明了一个长度为11的切片slice

    2.3 结构定义

    go的结构和C的结构类似,然后使用new来定义

    type Person struct {
        name string
        age int
    }
    p1:=new(Person)
    p1.name="kevin"
    p1.age=23
    println(p1.name)
    println(p1.age)

    定义结构的方法:

    type Person struct {
        name string
        age  int
    }
    
    func (p *Person) SetAge(v int) int {
        p.age = v
        return v
    }
    func main() {
        p1 := new(Person)
        p1.SetAge(12)
        println(p1.age)
    
    }

    五、接口

    六、并发

    1. goroutine

    go中一个很重要的概念是goroutine,协程的英文是coroutine,第一个字母不同,即goroutine类似于协程,但是又有所不同,是go特殊的概念。
    goroutine的特点:

    • goroutine 并行执行的,有着相同地址空间的函数。
    • 轻量的
    • 初始化的代价很低

    一个并发的DEMO:

    package main
    
    import "time"
    //注释
    
    func f(name string) {
        for i := 0; i < 10; i++ {
            println(name, i)
            time.Sleep(1 * 1e9)
        }
    }
    func main() {
        go f("f 1")
        go f("f 2")
        time.Sleep(15 * 1e9)
    }

    类似于线程,然后启动的方法也比较方便,只需要在前面加一个go的关键字。
    1e9是一个内部的常量,是秒的意思

    2. channel

    goroutine之间通过channel来通讯,channel类似于队列,即Python中的Queue。
    定义channel的时候,需要指定channel接受的类型,可以为int,string,和interface等。

    var c chan int;  //定义一个接受int类型的channel
    c = make(chan int)
    c<-1//向c中put一个对象
    item:=<- c //从c中取一个对象

    所以上面的并发程序可以改为:

    func f(name string) {
        for i := 0; i < 10; i++ {
            println(name, add(i))
            time.Sleep(1 * 1e9)
        }
        c <- 1//向c中put一个对象
    }
    func main() {
        c = make(chan int)
        go f("f 1")
        go f("f 2")
        <-c
        <-c
    }

    在goroutine中,可以put,也可以get。

    2.1 缓冲

    上面的channel是无缓冲的,也就是put完之后,goroutine就会阻塞,直到有goroutine取走。
    定义有缓冲的channel:

    ch:=make(chan int,1)

    1就是缓冲的数量

    获取的时候,也是阻塞的,可以使用非阻塞的方法:

    v,ok:=<-c

    如果有值,ok为true,否则为false

    3.并发问题

    尽管是叫并发,但是同一时刻,只有一个goroutine在执行,也就是占用CPU,类似Python的线程和协程。
    可以通过runtime.GOMAXPROCS(n)来设置同一个时刻运行的goroutine的数量,也可以修改环境变量GOMAXPROCS。

    七、通讯

    1. 文件

    读取文件

    import "os"
    
    func main() {
        buf := make([]byte, 1024)
        f ,_:= os.Open("./test.data")
        defer f.Close()
        for {
            n, _ := f.Read(buf)
            if n == 0 {
                break
            }
            os.stdout.write(buf[0:n]) //必须使用write,如果使用println,会输出切片的内存地址
        }
    }

    通过bufio读取文件

    import "os"
    import "bufio"
    //注释
    
    func main() {
        buf := make([]byte, 1024)
        f, _ := os.Open("/etc/passwd")
        defer f.Close()
        r := bufio.NewReader(f)
        w := bufio.NewWriter(os.Stdout)
        defer w.Flush()
        for {
            n, _ := r.Read(buf)
            if n == 0 { break }
            w.Write(buf[0:n])
        }
    }

    创建目录

    if _, e := os.Stat("name"); e != nil {
        os.Mkdir("name", 0755)
    } else {
        // error
    }

    2. 命令行参数

    import "os"
    import "flag"
    import "fmt"
    //注释
    
    func main() {
        dnssec := flag.Bool("dnssec", false, "Request DNSSEC records")
        port := flag.String("port", "53", "Set the query port")
        flag.Usage = func() {
            fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] [name ...]
    ", os.Args[0])
            flag.PrintDefaults()
        }
        flag.Parse()
        println(*dnssec,*port)
    }

    3. 网络

    八、性能测试

    go的特点就是高性能和高并发

    测试用例:
    从1加到1000,执行一百万次,计算需要的时间。

    使用linux的time命令来进行计时。

    1. go

    sum.go

    package main
    func sum(num int)int{
        sum:=0
        for i:=1;i<=num;i++{
            sum+=i
        }
        return sum
    }
    func main(){
        sum_:=0
        for j:=0;j<1000000;j++{
            sum_+=sum(1000)
        }
        println(sum_)
        println(sum(1000))
    
    }

    编译:

    go build sum.go 

    执行

    time ./sum

    结果:

    real    0m0.464s
    user    0m0.460s
    sys     0m0.001s

    2. C

    sum.c

    #include <stdio.h>
    long int sum(int num){
        long int sum=0;
        int i =0;
        for( i=1;i<=num;i++){
            sum=sum+i;
        };
        return sum;
    }
    int main(){
        int i ;
        long int sum_=0;
        for (i=0;i<1000000;i++){
            sum_+=sum(1000);
        }
        printf("%ld
    ",sum(1000));
        printf("%ld
    ",sum_);
    //    printf(sum_);
        return 0;
    
    };

    编译:

     gcc sum.c  -fPIC -shared -o sum.so

    执行

    time ./sumc 

    结果:

    real    0m2.874s
    user    0m2.856s
    sys     0m0.000s

    3. Python

    test_sum.py

    def sum(num):
        s = 0
        for i in xrange(1,num+1):
            s += i
        return s
    
    def main():
        sum_=0
        for i in range(1000000):
            sum_+=sum(1000)
        print sum_
    
    if __name__ == '__main__':
        main()

    执行

    time python test_sum.py

    结果

    real    0m35.146s
    user    0m34.814s
    sys     0m0.125s
    1. 在Python中调用C
      test_sum_c.py
    from ctypes import cdll
    c_lib = cdll.LoadLibrary('./sum.so')
    
    
    if __name__ == '__main__':
        c_lib.main()

    执行

    time python test_sum_c.py

    结果

    real    0m2.899s
    user    0m2.874s
    sys     0m0.006s

    5. 比较

    语言|go|C|Python|Python调用c
    ---|
    用时:|0.464s|2.874s|35.146s|2.899s

      • go竟然比c还快,而且快很多
      • Python非常慢
  • 相关阅读:
    ASP.NET WEB开发,实现上传图片
    使用Word API打开Word文档 ASP.NET编程中常用到的27个函数集
    工资单的编辑与保存
    生成工资表
    系统设置
    空间参考
    Envelope几何对象 Curve对象几何对象 Multipatch几何对象 Geometry集合接口 IGeometryCollection接口
    Polygone对象
    Segment,Path,Ring和Polyline对象
    [Android]使用ActivityGroup来切换Activity和Layout
  • 原文地址:https://www.cnblogs.com/rxbook/p/10638179.html
Copyright © 2011-2022 走看看