菜鸟教程go

687 阅读9分钟

go语言最主要的特性

自动垃圾回收
更丰富的内置类型
函数多返回值
错误处理
匿名函数和闭包
类型和接口
并发编程
反射
语言交互性

学术流派

  面向过程编程、面向对象编程、函数式编程、面向消息编程
  Go语言接受了函数式编程的一些想法,支持匿名函数与闭包
   Go语言接受了以Erlang语言为代表的面向消息编程思想,支持goroutine和通道,并推荐使用消息而不是共享内存来进行并发编程

第一个 Go 程序

hello.go

package main

import "fmt"

func main() {
fmt.Println("Hello, World!")
}

gorunhello.gogo run hello.go go build hello.go lshellohello.gols hello hello.go ./hello Hello, World!

Go 语言环境安装

UNIX/Linux/Mac OS X, 和 FreeBSD

   下载二进制包:go1.4.linux-amd64.tar.gz
   tar -C /usr/local -xzf go1.4.linux-amd64.tar.gz
   export PATH=$PATH:/usr/local/go/bin

MAC

   brew install go/brew install golang
    brew info go 查看版本信息
    ~  brew info go
    ~  brew info golang

ubuntu

  apt install golang-go

Red Hat

  sudo yum install golang
  go version
   mkdir ~/workspace
   echo 'export GOPATH="$HOME/workspace"' >> ~/.bashrc
   source ~/.bashrc
  yum search golang

DeepinOS 下或 ubuntu

      vim  ~/.bashrc 或 vim ~/profile // bashrc 对系统所有用户有效,profile 对当前用户有效
      三个变量 GOPATH、PATH、GOROOT
      GOROOT 就是 go 的安装路径;
      GOPATH 就是go的工作目录;
      PATH是go安装路径下的bin目录
      
      export GOROOT="/usr/local/go"
      export GOPATH="/go"
      export PATH=$PATH:/usr/local/go/bin
      
      source  ~/.bashrc 或 source ~/profile

Windows

   go1.4.2.windows-amd64.msi
   默认情况下 .msi 文件会安装在 c:\Go 目录下,将 c:\Go\bin 目录添加到 Path 环境变量中
   创建工作目录 C:\>Go_WorkSpace。
   C:\Go_WorkSpace>go run test.go

Go 语言结构

包声明

   package main:在源文件中非注释的第一行指明这个文件属于哪个包
   package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包
  
  文件名与包名没有直接关系,不一定要将文件名与包名定成同一个。
  文件夹名与包名没有直接关系,并非需要一致。
  同一个文件夹下的文件只能有一个包名,否则编译报错
   

引入包

    import "fmt" 

函数

   func main() 是程序开始执行的函数
   init() -> main()

变量

   以一个大写字母开头标识符的对象就可被外部包的代码所使用,这被称为导出
   标识符如果以小写字母开头,则对包外是不可见的,在整个包的内部是可见并且可用的

语句 & 表达式

    { 不能单独放在一行

注释

     // 开头的单行注释
    /*...*/ 是多行注释

build.sh:指定位置产生已编译好的可执行文件

   #!/usr/bin/env bash

    CURRENT_DIR=`pwd`
    OLD_GO_PATH="$GOPATH"  #例如: /usr/local/go
    OLD_GO_BIN="$GOBIN"    #例如: /usr/local/go/bin

    export GOPATH="$CURRENT_DIR" 
    export GOBIN="$CURRENT_DIR/bin"

    #指定并整理当前的源码路径
    gofmt -w src

    go install test_hello

    export GOPATH="$OLD_GO_PATH"
    export GOBIN="$OLD_GO_BIN"

基础语法

Go 标记

   关键字,标识符,常量,字符串,符号

行分隔符

    分号 ; 结尾

注释

    // 开头的单行注释
    多行注释也叫块注释,已以 /* 开头,并以 */ 结尾

标识符

   一个或是多个字母(A~Z和a~z)数字(0~9)、下划线_组成的序列
   第一个字符必须是字母或下划线而不能是数字

字符串连接

   通过 + 实现

关键字

   break	default	func	interface	select
    case	defer	go	map	struct
    chan	else	goto	package	switch
    const	fallthrough	if	range	type
    continue	for	import	return	var

空格

   变量的声明必须使用空格隔开:var age int;

格式化字符串

    fmt.Sprintf
    // %d 表示整型数字,%s 表示字符串
    var stockcode=123
    var enddate="2020-12-31"
    var url="Code=%d&endDate=%s"
    var target_url=fmt.Sprintf(url,stockcode,enddate)
    fmt.Println(target_url)

Go 程序的一般结构

   // 当前程序的包名
    package main

    // 导入其他包
    import . "fmt"

    // 常量定义
    const PI = 3.14

    // 全局变量的声明和赋值
    var name = "gopher"

    // 一般类型声明
    type newType int

    // 结构的声明
    type gopher struct{}

    // 接口的声明
    type golang interface{}

    // 由main函数作为程序入口点启动
    func main() {
        Println("Hello World!")
    }

模块的导入

   单个导入:import "fmt"
   导入多个:import (
                    "fmt"
                    "math"
            )
    别名调用:import fmt2 "fmt"
    省略调用:import . "fmt"
    项目名/包名:import "test/controllers"
    方法的调用:包名.方法名()
            本包内方法名可为小写,包外调用方法名首字母必须为大写。
    

关键字

   const :常量
   var :全局变量的声明和赋值
   type :结构(struct)和接口(interface)的声明
   func :函数的声明

Golang fmt 包

   Print() :非字符串参数之间会添加空格
   Println() :所有参数之间会添加空格,最后会添加一个换行符
   Printf() :将参数列表 a 填写到格式字符串 format 的占位符中
   
   func Fprint(w io.Writer, a ...interface{}) (n int, err error)
   func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
   func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
   
   func Sprint(a ...interface{}) string
   func Sprintln(a ...interface{}) string
   func Sprintf(format string, a ...interface{}) string
   
   func Errorf(format string, a ...interface{}) error

Formatter

   type Formatter interface {
        // f 用于获取占位符的旗标、宽度、精度等信息,也用于输出格式化的结果
        // c 是占位符中的动词
        Format(f State, c rune)
    }
    
    type State interface {
        // Formatter 通过 Write 方法将格式化结果写入格式化器中,以便输出。
        Write(b []byte) (ret int, err error)
        // Formatter 通过 Width 方法获取占位符中的宽度信息及其是否被设置。
        Width() (wid int, ok bool)
        // Formatter 通过 Precision 方法获取占位符中的精度信息及其是否被设置。
        Precision() (prec int, ok bool)
        // Formatter 通过 Flag 方法获取占位符中的旗标[+- 0#]是否被设置。
        Flag(c int) bool
    }
    
    type Stringer interface {
        String() string
    }
    
    type GoStringer interface {
        GoString() string
    }
    实例:
    
    type Ustr string

    func (us Ustr) String() string {
        return strings.ToUpper(string(us))
    }

    func (us Ustr) GoString() string {
        return `"` + strings.ToUpper(string(us)) + `"`
    }

    func (u Ustr) Format(f fmt.State, c rune) {
        write := func(s string) {
            f.Write([]byte(s))
        }
        switch c {
        case 'm', 'M':
            write("旗标:[")
            for s := "+- 0#"; len(s) > 0; s = s[1:] {
                if f.Flag(int(s[0])) {
                    write(s[:1])
                }
            }
            write("]")
            if v, ok := f.Width(); ok {
                write(" | 宽度:" + strconv.FormatInt(int64(v), 10))
            }
            if v, ok := f.Precision(); ok {
                write(" | 精度:" + strconv.FormatInt(int64(v), 10))
            }
        case 's', 'v': // 如果使用 Format 函数,则必须自己处理所有格式,包括 %#v
            if c == 'v' && f.Flag('#') {
                write(u.GoString())
            } else {
                write(u.String())
            }
        default: // 如果使用 Format 函数,则必须自己处理默认输出
            write("无效格式:" + string(c))
        }
    }

    func main() {
        u := Ustr("Hello World!")
        // "-" 标记和 "0" 标记不能同时存在
        fmt.Printf("%-+ 0#8.5m\n", u) // 旗标:[+- #] | 宽度:8 | 精度:5
        fmt.Printf("%+ 0#8.5M\n", u)  // 旗标:[+ 0#] | 宽度:8 | 精度:5
        fmt.Println(u)                // HELLO WORLD!
        fmt.Printf("%s\n", u)         // HELLO WORLD!
        fmt.Printf("%#v\n", u)        // "HELLO WORLD!"
        fmt.Printf("%d\n", u)         // 无效格式:d
    }
    

Scan

   func Scan(a ...interface{}) (n int, err error)
   从标准输入中读取数据,并将数据用空白分割并解析后存入 a 提供的变量中
   变量必须以指针传入,当读到 EOF 或所有变量都填写完毕则停止扫描。
   func Scanln(a ...interface{}) (n int, err error)
   func Scanf(format string, a ...interface{}) (n int, err error)
     从标准输入中读取数据,并根据格式字符串 format 对数据进行解析
     将解析结果存入参数 a 所提供的变量中,变量必须以指针传入。
   
   从 r 中读取数据
   func Fscan(r io.Reader, a ...interface{}) (n int, err error)
   func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
   func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)
   
   从 str 中读取数据
   func Sscan(str string, a ...interface{}) (n int, err error)
   func Sscanln(str string, a ...interface{}) (n int, err error)
   func Sscanf(str string, format string, a ...interface{}) (n int, err error)
   
   实例
   func main() {
        a, b, c := "", 0, false
        fmt.Scan(&a, &b, &c)
        fmt.Println(a, b, c)
        // 在终端执行后,输入 abc 1 回车 true 回车
        // 结果 abc 1 true
    }

    // 对于 Scanln 而言,回车结束扫描
    func main() {
        a, b, c := "", 0, false
        fmt.Scanln(&a, &b, &c)
        fmt.Println(a, b, c)
        // 在终端执行后,输入 abc 1 true 回车
        // 结果 abc 1 true
    }

    // 格式字符串可以指定宽度
    func main() {
        a, b, c := "", 0, false
        fmt.Scanf("%4s%d%t", &a, &b, &c)
        fmt.Println(a, b, c)
        // 在终端执行后,输入 1234567true 回车
        // 结果 1234 567 true
    }

Scanner

   type Scanner interface {
        // state 用于获取占位符中的宽度信息,也用于从扫描器中读取数据进行解析。
        // verb 是占位符中的动词
        Scan(state ScanState, verb rune) error
    }
    
    type ScanState interface {
        // ReadRune 从扫描器中读取一个字符,如果用在 Scanln 类的扫描器中,
        // 则该方法会在读到第一个换行符之后或读到指定宽度之后返回 EOF。
        // 返回“读取的字符”和“字符编码所占用的字节数”
        ReadRune() (r rune, size int, err error)
        // UnreadRune 撤消最后一次的 ReadRune 操作,
        // 使下次的 ReadRune 操作得到与前一次 ReadRune 相同的结果。
        UnreadRune() error
        // SkipSpace 为 Scan 方法提供跳过开头空白的能力。
        // 根据扫描器的不同(Scan 或 Scanln)决定是否跳过换行符。
        SkipSpace()
        // Token 用于从扫描器中读取符合要求的字符串,
        // Token 从扫描器中读取连续的符合 f(c) 的字符 c,准备解析。
        // 如果 f 为 nil,则使用 !unicode.IsSpace(c) 代替 f(c)。
        // skipSpace:是否跳过开头的连续空白。返回读取到的数据。
        // 注意:token 指向共享的数据,下次的 Token 操作可能会覆盖本次的结果。
        Token(skipSpace bool, f func(rune) bool) (token []byte, err error)
        // Width 返回占位符中的宽度值以及宽度值是否被设置
        Width() (wid int, ok bool)
        // 因为上面实现了 ReadRune 方法,所以 Read 方法永远不应该被调用。
        // 一个好的 ScanState 应该让 Read 直接返回相应的错误信息。
        Read(buf []byte) (n int, err error)
    }
    
    实例
      type Ustr string

        func (u *Ustr) Scan(state fmt.ScanState, verb rune) (err error) {
            var s []byte
            switch verb {
            case 'S':
                s, err = state.Token(true, func(c rune) bool { return 'A' <= c && c <= 'Z' })
                if err != nil {
                    return
                }
            case 's', 'v':
                s, err = state.Token(true, func(c rune) bool { return 'a' <= c && c <= 'z' })
                if err != nil {
                    return
                }
            default:
                return fmt.Errorf("无效格式:%c", verb)
            }
            *u = Ustr(s)
            return nil
        }
        
        func main() {
            var a, b, c, d, e Ustr
            n, err := fmt.Scanf("%3S%S%3s%2v%x", &a, &b, &c, &d, &e)
            fmt.Println(a, b, c, d, e)
            fmt.Println(n, err)
            // 在终端执行后,输入 ABCDEFGabcdefg 回车
            // 结果:
            // ABC DEFG abc de
            // 4 无效格式:x
        }
        

Go 语言数据类型

布尔型

 常量 true 或者 falsevar b bool = true

数字类型

  整型 int 
      uint8int8
      uint16int16
      uint32int32
      uint64int64
  浮点型 float32float64
      float32
      float64
      complex64
      complex128
  byte类似 uint8
  rune类似 int32
  uint3264int3264uintptr:无符号整型,用于存放一个指针

字符串类型

   字符串的字节使用 UTF-8 编码标识 Unicode 文本

派生类型

    (a) 指针类型(Pointer)
    (b) 数组类型
    (c) 结构化类型(struct)
    (d) Channel 类型
    (e) 函数类型
    (f) 切片类型
    (g) 接口类型(interface)
    (h) Map 类型

Go 语言变量

关键字:var

变量声明:

    指定变量类型:未初始化默认为零值
    数值类型(包括complex64/128)为 0
    布尔类型为 false
    字符串为 ""(空字符串)
    以下几种类型为 nilvar a *int
        var a []int
        var a map[string] int
        var a chan int
        var a func(string) int
        var a error // error 是接口
        
    自行判定变量类型
        var v_name = value
    省略 var
        v_name := value
        := 左侧如果没有声明新的变量,就产生编译错误

值类型和引用类型

值类型:基本类型

   变量直接指向存在内存中的值
    &i 来获取变量 i 的内存地址
   值类型的变量的值存储在栈中
   使用等号 = :在内存中将 i 的值进行了拷贝

引用类型

   引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字)
   当使用赋值语句 r2 = r1 时,只有引用(地址)被复制

细节

   交换值:a, b = b, a
   空白标识符 _ 也被用于抛弃值

Go 语言常量

 const identifier [type] = value
 const (
        a = "abc"
        b = len(a)
        c = unsafe.Sizeof(a)
 )
 常量表达式中,函数必须是内置函数
 在定义常量组时,如果不提供初始值,则表示将使用上行的表达式

iota

const关键字出现时将被重置为 0(const 内部的第一行之前)
   const 中每新增一行常量声明将使 iota 计数一次
   iota 只是在同一个 const 常量组内递增,有新的 const 关键字时,iota 计数会重新开始。
   const (
        i=1<<iota
        j=3<<iota
        k
        l
    )
    <<n==*(2^n)