go语言入门

  1. 字符串长度
    ascii字符长度使用len(),每个中文长度为3
    unicode字符串长度用utf8.RuneCountInString()函数
  2. 字符串遍历
    ascii字符串遍历使用下标
    在这里插入图片描述
    unicode字符串遍历使用for range
    在这里插入图片描述
  3. 字符串索引
    strings.Index:正向搜索子字符串
    strings.LastIndex:反向搜索子字符串
    搜索的起始位置可以通过切片偏移制作
  4. 修改字符串内容
    字符串不可变
    修改字符串时,可以将字符串转换成[]byte进行修改
    []byte和string可以通过强制类型转换互换
  5. 连接字符串
    获取bytes.buffer
    var sb bytes.buffer
    调用WriteString写入缓冲
    sb.WriteString(str)
    调用String获取字符串
    sb.String()

GO语言学习

基本语法

变量

  1. 变量定义
    1. var a int = 5
    2. var a,b,c = 5,“ads”,true
    3. a:=5,注意:=用于定义变量,后面赋值时不能再次使用否则就是重复定义。函数外定义变量不能使用
    4. var (a=3 b=4 c=“aa”)
  2. 内建变量类型
    1. bool、string
    2. (u)int、 (u)int8、 (u)int16、 (u)int32、 (u)int64、uintptr
    3. byte、rune(字符类型32位)
    4. float32、float64、complex64、complex128
  3. 类型转换是强制的
  4. 常量的定义
    常量的名称不要大写
    const filename = “abc.txt”
    const数值可作为各种类型使用
  5. 枚举类型
    1. 普通枚举类型
      const(
      a = 0
      b = 1
      c = 2
      )
    2. 自增值枚举类型
      const(
      a = iota(从0开始)
      b
      c
      )
  6. 要点
    1. 变量类型写在变量名后面
    2. 编译器可推测变量类型
    3. 没有char,只有rune
    4. 原生支持复数类型

程序结构

  1. if
    if的条件里可以赋值
    if的条件里赋值的变量作用域就在这个if语句里
    if content, err:=ioutil.ReadFile(filename); err == nil {
    	fmt.Println(string(contents))
    } else {
    	fmt.Println("cannot print file contents:",  err)
    }
    
  2. switch
    会自动break,除非fallthrough
    switch后可以没有表达式,在case后面使用条件即可
    func score(score int) string {
    	var g string
    	switch  {
    	case score < 0 || score > 100:
    		panic(fmt.Sprintf("error score:%d", score))
    	case score <= 60:
    		g = "F"
    	case score < 80:
    		g="B"
    	case score < 90:
    		g="A"
    	case score <= 100:
    		g="S"
    	}
    	return g
    }
    
  3. for
    for的条件里不需要括号
    for的条件里可以省略初始条件,结束条件,递增表达式
  4. 要点回顾
    1. for、if后面的条件没有括号
    2. if条件里也可以定义变量
    3. 没有while
    4. switch不需要break,也可以不需要表达式可以在case后面跟条件

函数与指针

  1. 函数

    1. 函数可返回多个值
    func div(a,b int) (int, int) {
    	return a/b, a%b
    }
    
    1. 返回多个值时可以起名字(仅用于非常简单的函数),对于调用者而言没有区别
    func div(a,b int) (q, r int) {
    	q = a/b
    	r = a%b
    	return
    }
    
    1. 函数作为参数
    func apply(op func(int, int) int, a, b int) int {
    	fmt.Printf("Calling %s with %d, %d\n",
    			runtime.FuncForPC(reflect.ValueOf(op).Pointer()).Name(), a, b)
    			return op(a, b)	
    }
    
    1. 可变参数列表
    func sumArgs(values ...int) int {
    	sum := 0
    	for i := range values {
    		sum += values[i]
    	}
    	return sum	
    }
    
    1. 要点回顾
      1. 返回值类型写在最后面
      2. 可返回多个值
      3. 函数作为参数
      4. 没有默认参数,可选参数
  2. 指针

    1. 参数传递
      go语言只有值传递,也就是将变量的值复制一份后传递给方法参数。
      值传递可以和指针配合进行传参

容器

  1. 数组
    1. 定义
    var array1 [5]int
    array2 := [3]int{1,2,3]
    arr3 := [...]int{1,2,3,4,}
    var arr4 [4][5]int
    
    1. 遍历
    for i,v:= range numbers {
    
    }
    
    1. 数组是值类型,传递参数时会将数组整体拷贝一份
    2. [10]int 和 [20]int是不同类型
    3. 在go语言中一般不直接使用数组
  2. 切片
    1. 定义
      arr := […]int{1,2,3,4}
      s := arr[2:]
      s[0]=10
      slice本身没有数据,是对底层array的一个view
      arr的值变为[1,2,10,4]
      参数传递时[]int类型代表切片
      通过slice可以替换数组中的值
    2. 实现
      slice中有三个变量分为别ptr、len、cap,ptr指向数组中slice的第一个元素、len代表slice中元素的个数、cap代表从slice第一个元素到数组最后一个元素中间的元素个数。
    3. slice的扩展
      可以向后扩展,不可以向前扩展
      s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)
    4. 向slice添加元素
      添加元素时如果超越cap,系统会重新分配更大的底层数组
      由于值传递的关系,必须接收append的返回值
      s=append(s, val)
    5. slice相关操作
      创建:make([]int, 10, 32)
      copy: copy(des, src),将src中的元素copy到des中
      删除某个元素:append(s2[:3], s2[4:]…)
  3. map
    1. 定义
      s := map[key类型]value类型 {
      “name”: “ccc”
      }
      复合map:map[K1]map[K2]V2
      s := make(map[K]V)
    2. 遍历
      for k,v := range m {
      }
      使用range遍历key或者遍历key、value对
      不保证遍历顺序,如需顺序,需手动对key排序
      使用len获取元素个数
    3. key不存在时,value为zero value
    4. 用value, ok := m[key]来判断key是否存在
    5. 删除一个key,delete(m, key)
    6. map的key
      map使用哈希表,key必须可以比较相等
      除了slice,map,function都可以作为key
      struct类型不包含上述字段,也可以作为key,在编译时校验
  4. 寻找最长不含有重复字符的子串
  5. 字符和字符串处理
    1. rune相当于go的char
    2. 使用range遍历pos、rune对
    3. 使用utf8.RuneCountInString获得字符数量
    4. 使用len获得字节长度
    5. 使用[]byte获得字节
    6. 其他字符串操作
      Fields,split,join
      Contains,Index
      ToLower,ToUpper
      Trim,TrimRight,TrimLeft

面向对象

  1. go语言仅支持封装,不支持继承和多态
  2. go语言没有class,只有struct
  3. 不论是地址还是结构本身,一律使用.来访问成员
  4. 使用自定义工厂函数
    func createTreeNode(value int) *TreeNode {
    	return &TreeNode{Value: value}
    }
    root.left.Right = createTreeNode(2)
    
    注意返回了局部变量的地址
  5. 定义结构体的方法
    本质和普通函数没有区别,接收者也相当于参数
    func (node TreeNode) print(){
    	fmt.Print(node.value)
    }
    
    使用指针作为方法的接收者
    func (node *TreeNode) setValue(value int) {
    	node.value = value
    }
    
    只有使用指针才能改变结构体的内容
    nil指针也可以调用方法
  6. 写一个树的左根右遍历
  7. 值接收者 vs 指针接收者
    要改变内容必须使用指针接收者
    结构过大也考虑使用指针接收者
    一致性:如果有指针接收者,最好都是指针接收者
  8. 封装
    名字一般使用CamelCase
    首字母大写:public
    首字母小写:private

  9. 每个目录一个包
    main包包含可执行入口(main方法),只能有一个main方法
    为结构定义的方法必须放在同一个包内,但可以是不同文件
  10. 如何扩展系统类型或者别人的类型
    1. 组合
    type Node struct{
    	node *TreeNode
    }
    
    1. 别名
    type Queue []int
    
  11. GOPATH环境变量
    默认在~/go(linux),%USERPROFILE%\go(windows)
    官方推荐:所有项目和第三方库都放在同一个GOPATH下
    也可以将每个项目放在不同的GOPATH
  12. 安装gopm,再通过gopm安装其他的go依赖包
    go get github.com/gpmgo/gopm
    使用gopm
    gopm get --g -v golang.org/x/tools/cmd/goimports
  13. GOPATH下的目录结构
    go build 来编译,得到可执行文件
    go install 产生pkg文件和可执行文件,先build然后将可执行文件放在bin目录下
    go run直接编译运行

接口

  1. 定义
    type  Retriver interface{
    	Get(url string) string
    }
    
  2. 实现者不需要实现某个接口,只需要实现接口中的某个方法
    使用者可以根据自己的需求实现接口中的方法进行调用
    type Retriver struct{
    		content string
    }
    func (r Retriver) Get(url string) string {
    	return r.content
    }
    
  3. 接口变量有什么
    指针接收者实现只能以指针方式使用,值接收者都可以
    接口变量中有实现者的类型和实现者的值或者指针
    例如
    r.(type)
    r.(mock.Retriver)
  4. interface{}表示任何类型
    interface{}强转类型,interface{}.(int)
  5. 接口的组合
    可以将多个接口组合成一个接口,实现者实现多个接口中的方法
  6. 常用系统接口
    1. Stringer
      里面的string方法相当于Java中的toString()

函数式编程

  1. 闭包
    返回一个函数,该函数可以操作外面函数的变量,外面函数的变量称为该函数的自由变量
  2. 斐波那契数字
    func fibonacci() func() int{
    	a, b := 0, 1
    	return func() int {
    		a,b  = b, a+b
    		return a
    	}
    }
    
  3. 使用函数遍历二叉树

错误处理与资源管理

  1. defer调用
    使用defer修饰方法调用,当外部方法调用完成后defer修饰的方法调用才会执行(哪怕出错或者return),defer修饰的方法调用顺序是先进后出
    确保调用在函数结束时发生
    参数在defer语句时计算
    defer列表为后进先出
  2. 错误处理
    file, err := os.Open("abc.txt")
    if err!= nil{
    	if pathError, ok := err.(*os.PathError); ok{
    			fmt.Println(pathError.Err)
    	}else{
    			fmt.Println("unknown error", err)
    	}
    }
    
  3. panic
    停止当前函数执行
    一直向上返回,执行每一层的defer
    如果没有遇见recover,程序退出
  4. recover
    仅在defer中使用
    获取panic的值
    如果无法处理,可重新panic

测试

  1. 表格驱动测试
    分离的测试数据和测试逻辑
    tests := []struct{
    	h		   string
    	code   int
    	message  string
    }{
    	{'haha', 500, 'heihiei'}
    }
    
    明确的出错信息
    可以部分失败
  2. 单元测试
    测试文件以_test结尾
    测试方法以Test开头,并且参数为 t *testing.T
    t.Errorf(“expect:,actual:”)
  3. 命令行测试
    执行go test . 会对当前目录下所有的test文件进行测试
  4. 批量测试
    测试方法参数为b *testing.B
    循环b.N次
    go test -bench .
    1. 查看cpu执行情况
      需要安装graphviz,www.graphviz.org
      go test -bench . -cpuprofile cpu.out
      go tool pprof cpu.out
      web
    2. b.ResetTimer()重置计算时间,去除数据准备时间
  5. http测试
    httptest.NewRecorder() 创建httpReponseWriter
    httptest.NewRequest() 创建*httpRequest
  6. 生成文档
    用注释写文档
    godoc -http 6060 生成文档
    go doc查看文档
  7. 文档中生成示例代码
    使用Example既做测试又能生成示例
    Excample类名_方法名
    func ExampleQueue_Pop() {
    	q := Queue{1}
    	q.Push(2)
    	q.Push(3)
    	fmt.Println(q.Pop())   # Println不需要也可以
    	// Output:
    	// 1
    }
    

goroutine

  1. 协程
    轻量级线程
    非抢占式多任务处理,由协程主动交出控制权。线程就是抢占式,某个线程可能一条语句刚执行一半就被CPU切走了,抢占式需要保存资源比较耗时
    编译器/解释器/虚拟机层面的多任务
    多个协程可能在一个或多个线程上运行
  2. 查看数据竞争情况
    go run -race a.go
  3. goroutine
    go语言程序底层有一个调度器,会负责调度协程,有些协程会放在一个线程中,有些则不会
    任何函数调用只需加上go就能送给调度器运行
    不需要在定义函数时区分是否是异步函数
    调度器在合适的点进行切换
    使用-race来检测数据访问冲突
  4. goroutine可能的切换点
    IO,select
    channel
    等待锁
    函数调用(有时)
    runtime.Gosched
    只是参考,不能保证切换,不能保证在其他地方不切换

Channel

  1. channel可以作为参数、返回值
  2. 创建channel
    make(chan int)
  3. channel中有数据时必须有goroutine来收,否则会出现dead-lock问题,因此调度器会切换到收取channel数据的goroutine
  4. 带缓冲的channel可以缓冲一些数据,不需要立即切换goroutine可以提高性能
  5. 发送方可以close channel,channel关闭后只是数据为默认值,接收方可以使用range遍历或者使用两个返回值来确定channel是否关闭
  6. channel的使用
    c := make(chan int)
    c <- 1 往channel发数据
    n := <-c 从channel收数据
  7. sync.WaitGroup
    初始时指定任务数量
    调用Done方法表示完成任务,任务数减1
    调用Wait方法,当任务数不为0时等待,当任务数为0时继续执行
  8. channel的阻塞
    发送者角度:对于同一个通道,发送操作(协程或者函数中的),在接收者准备好之前是阻塞的。如果chan中的数据无人接收,就无法再给通道传入其他数据。
    接收者角度:对于同一个通道,接收操作是阻塞的(协程或函数中的),直到发送者可用:如果通道中没有数据,接收者就阻塞了。
  9. select调度
    使用select+defatult可以实现无阻塞接收
    select会忽略nil channel
    select {
       case n := <-c1:
       	values = append(values, n)
       case n := <-c2:
       	values = append(values, n)
       case activeWorker <- n:
       	printf
       default:
       	
    }
    
  10. 计时器
    time.After(800 * time.Millisecond)会返回一个channel,800毫秒后会往该channel发送一个数据
    time.Tick(time.second)会返回一个channel,每秒都会往该channel发送一个数据
  11. 传统同步机制
    1. sync.WaitGroup
    2. sync.Mutex
      Lock()
      UnLock()
    3. sync.Con

http标准库

  1. http客户端
    1. 简单版
      res, err := http.Get("http://www.imooc.com")
      defer res.Body.Close()
      s, err := httputil.DumpResponse(res, true)
      
    2. 复杂版
      request, err := http.NewRequest(http.MethodGet,
      													"http://www.imooc.com", nil)
      request.Header.Add(key, value)
      client := http.Client{
      	CheckRedirect: func(req, via) error{    # req就是目标请求,该方法每次请求都会调用不止重定向时调用
      			return nil
      	},
      }
      response , err := client.Do(request)
      
  2. http服务器的性能分析
    1. 在http服务器启动的文件中,import _ “net/http/pprof”
    2. 访问/debug/pprof
    3. 使用go tool pprof分析性能,在pprof包中的pprof.go文件中可以看到分析对应性能的地址
  3. 其他标准库
    bufio
    log
    encoding/json
    regexp
    time
    strings/math/rand
  4. 学习标准库
    studygolang.com/pkgdoc
  5. 广度优先搜索
    用循环创建二维slice,首先创建最外维的slice然后在循环创建内部的slice
    使用slice实现队列
    用Fscanf读取文件
    对Point的抽象

单任务爬虫

  1. 获取响应体的内容
    ioutil.readAll(res.Body)
  2. 下载第三方包解决页面乱码
    gopm get -g -v golang.org/x/text
    transform.NewReader(res.Body, simplefiedchinese.GBK.NewDecoder())
    gopm get -g -v golang.org/x/net/html——判断内容的编码
  3. 正则表达式
    1. go语言提供了
      ` `
      这个符号为正则表达式使用,这样不需要使用转义字符
      
    2. 创建正则表达式
      reg := regexp.MustCompile([a-zA-Z0-9]+@)
      reg.FindAllString(text, -1) # 查找所有与正则表达式匹配的子串
    3. 提取正则表达式
      reg := regexp.MustCompile(([a-zA-Z0-9]+)@)
      reg.FindAllStringSubmatch(text, -1) # 查找所有与正则表达式匹配的子串并将要提取的部门提取出来
  4. 决定编码方式
    bytes, err := bufio.NewReader(r).Peek(1024)
    e, _, _ := charset.DetermineEncoding(bytes, "")
    

分布式爬虫

  1. jsonrpc
    1. 方法规范
      返回的结果以指针的形式放在参数中,真正的方法只返回err
      只能有一个输入一个输出一个err
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值